killbill-uncached

server: make the timeline support AuditLevel To limit the

8/13/2012 4:10:19 PM

Details

diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
index d06ec99..4ecee4e 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
@@ -220,30 +220,24 @@ public class AccountResource extends JaxRsResourceBase {
     @Path("/{accountId:" + UUID_PATTERN + "}/" + TIMELINE)
     @Produces(APPLICATION_JSON)
     public Response getAccountTimeline(@PathParam("accountId") final String accountIdString,
-                                       @QueryParam(QUERY_AUDIT) @DefaultValue("false") final Boolean withAudit) throws AccountApiException, PaymentApiException, EntitlementRepairException {
+                                       @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode) throws AccountApiException, PaymentApiException, EntitlementRepairException {
         final UUID accountId = UUID.fromString(accountIdString);
         final Account account = accountApi.getAccountById(accountId);
 
         // Get the invoices
         final List<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId());
         AuditLogsForInvoices invoicesAuditLogs = null;
-        if (withAudit) {
-            invoicesAuditLogs = auditUserApi.getAuditLogsForInvoices(invoices);
-        }
+        invoicesAuditLogs = auditUserApi.getAuditLogsForInvoices(invoices, auditMode.getLevel());
 
         // Get the payments
         final List<Payment> payments = paymentApi.getAccountPayments(accountId);
         AuditLogsForPayments paymentsAuditLogs = null;
-        if (withAudit) {
-            paymentsAuditLogs = auditUserApi.getAuditLogsForPayments(payments);
-        }
+        paymentsAuditLogs = auditUserApi.getAuditLogsForPayments(payments, auditMode.getLevel());
 
         // Get the refunds
         final List<Refund> refunds = paymentApi.getAccountRefunds(account);
         AuditLogsForRefunds refundsAuditLogs = null;
-        if (withAudit) {
-            refundsAuditLogs = auditUserApi.getAuditLogsForRefunds(refunds);
-        }
+        refundsAuditLogs = auditUserApi.getAuditLogsForRefunds(refunds, auditMode.getLevel());
         final Multimap<UUID, Refund> refundsByPayment = ArrayListMultimap.<UUID, Refund>create();
         for (final Refund refund : refunds) {
             refundsByPayment.put(refund.getPaymentId(), refund);
@@ -252,9 +246,7 @@ public class AccountResource extends JaxRsResourceBase {
         // Get the chargebacks
         final List<InvoicePayment> chargebacks = invoicePaymentApi.getChargebacksByAccountId(accountId);
         AuditLogsForInvoicePayments chargebacksAuditLogs = null;
-        if (withAudit) {
-            chargebacksAuditLogs = auditUserApi.getAuditLogsForInvoicePayments(chargebacks);
-        }
+        chargebacksAuditLogs = auditUserApi.getAuditLogsForInvoicePayments(chargebacks, auditMode.getLevel());
         final Multimap<UUID, InvoicePayment> chargebacksByPayment = ArrayListMultimap.<UUID, InvoicePayment>create();
         for (final InvoicePayment chargeback : chargebacks) {
             chargebacksByPayment.put(chargeback.getPaymentId(), chargeback);
@@ -263,9 +255,7 @@ public class AccountResource extends JaxRsResourceBase {
         // Get the bundles
         final List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(account.getId());
         AuditLogsForBundles bundlesAuditLogs = null;
-        if (withAudit) {
-            bundlesAuditLogs = auditUserApi.getAuditLogsForBundles(bundles);
-        }
+        bundlesAuditLogs = auditUserApi.getAuditLogsForBundles(bundles, auditMode.getLevel());
         final List<BundleTimeline> bundlesTimeline = new LinkedList<BundleTimeline>();
         for (final SubscriptionBundle bundle : bundles) {
             bundlesTimeline.add(timelineApi.getBundleTimeline(bundle.getId()));
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AuditMode.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AuditMode.java
new file mode 100644
index 0000000..e9ea7a1
--- /dev/null
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AuditMode.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.jaxrs.resources;
+
+import com.ning.billing.util.api.AuditLevel;
+
+public class AuditMode {
+
+    private final AuditLevel level;
+
+    public AuditMode(final String auditModeString) {
+        this.level = AuditLevel.valueOf(auditModeString.toUpperCase());
+    }
+
+    public AuditLevel getLevel() {
+        return level;
+    }
+
+    public boolean withAudit() {
+        return !AuditLevel.NONE.equals(level);
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("AuditMode");
+        sb.append("{level=").append(level);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final AuditMode auditMode = (AuditMode) o;
+
+        if (level != auditMode.level) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return level != null ? level.hashCode() : 0;
+    }
+}
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java b/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
index c234411..9b62599 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
@@ -16,14 +16,12 @@
 
 package com.ning.billing.jaxrs;
 
-import java.math.BigDecimal;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
-import javax.annotation.Nullable;
 import javax.ws.rs.core.Response.Status;
 
 import org.joda.time.DateTime;
@@ -32,20 +30,14 @@ import org.testng.annotations.Test;
 
 import com.ning.billing.jaxrs.json.AccountJson;
 import com.ning.billing.jaxrs.json.AccountTimelineJson;
-import com.ning.billing.jaxrs.json.AuditLogJson;
 import com.ning.billing.jaxrs.json.BillCycleDayJson;
-import com.ning.billing.jaxrs.json.ChargebackJson;
-import com.ning.billing.jaxrs.json.CreditJson;
 import com.ning.billing.jaxrs.json.CustomFieldJson;
-import com.ning.billing.jaxrs.json.InvoiceJsonSimple;
 import com.ning.billing.jaxrs.json.PaymentJsonSimple;
-import com.ning.billing.jaxrs.json.PaymentJsonWithBundleKeys;
 import com.ning.billing.jaxrs.json.PaymentMethodJson;
 import com.ning.billing.jaxrs.json.RefundJson;
 import com.ning.billing.jaxrs.json.TagDefinitionJson;
 import com.ning.billing.jaxrs.json.TagJson;
 import com.ning.billing.jaxrs.resources.JaxrsResource;
-import com.ning.billing.util.ChangeType;
 import com.ning.http.client.Response;
 
 import com.fasterxml.jackson.core.type.TypeReference;
@@ -112,100 +104,6 @@ public class TestAccount extends TestJaxrsBase {
         Assert.assertEquals(timeline.getBundles().get(0).getSubscriptions().get(0).getEvents().size(), 2);
     }
 
-    @Test
-    public void testAccountTimelineWithAudits() throws Exception {
-        final DateTime startTime = clock.getUTCNow();
-        final AccountJson accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
-        final DateTime endTime = clock.getUTCNow();
-
-        // Add credit
-        final InvoiceJsonSimple invoice = getInvoicesForAccount(accountJson.getAccountId()).get(1);
-        final DateTime creditEffectiveDate = clock.getUTCNow();
-        final BigDecimal creditAmount = BigDecimal.ONE;
-        createCreditForInvoice(accountJson.getAccountId(), invoice.getInvoiceId(),
-                               creditAmount, clock.getUTCNow(), creditEffectiveDate);
-
-        // Add refund
-        final PaymentJsonSimple postedPayment = getPaymentsForAccount(accountJson.getAccountId()).get(0);
-        final BigDecimal refundAmount = BigDecimal.ONE;
-        createRefund(postedPayment.getPaymentId(), refundAmount);
-
-        // Add chargeback
-        final BigDecimal chargebackAmount = BigDecimal.ONE;
-        createChargeBack(postedPayment.getPaymentId(), chargebackAmount);
-
-        final AccountTimelineJson timeline = getAccountTimelineWithAudits(accountJson.getAccountId());
-
-        // Verify payments
-        Assert.assertEquals(timeline.getPayments().size(), 1);
-        final PaymentJsonWithBundleKeys paymentJson = timeline.getPayments().get(0);
-        final List<AuditLogJson> paymentAuditLogs = paymentJson.getAuditLogs();
-        Assert.assertEquals(paymentAuditLogs.size(), 2);
-        verifyAuditLog(paymentAuditLogs.get(0), ChangeType.INSERT, null, null, "PaymentRequestProcessor", startTime, endTime);
-        verifyAuditLog(paymentAuditLogs.get(1), ChangeType.UPDATE, null, null, "PaymentRequestProcessor", startTime, endTime);
-
-        // Verify refunds
-        Assert.assertEquals(paymentJson.getRefunds().size(), 1);
-        final RefundJson refundJson = paymentJson.getRefunds().get(0);
-        Assert.assertEquals(refundJson.getPaymentId(), paymentJson.getPaymentId());
-        Assert.assertEquals(refundJson.getRefundAmount().compareTo(refundAmount), 0);
-        final List<AuditLogJson> refundAuditLogs = refundJson.getAuditLogs();
-        Assert.assertEquals(refundAuditLogs.size(), 3);
-        verifyAuditLog(refundAuditLogs.get(0), ChangeType.INSERT, reason, comment, createdBy, startTime, endTime);
-        verifyAuditLog(refundAuditLogs.get(1), ChangeType.UPDATE, reason, comment, createdBy, startTime, endTime);
-        verifyAuditLog(refundAuditLogs.get(2), ChangeType.UPDATE, reason, comment, createdBy, startTime, endTime);
-
-        // Verify chargebacks
-        Assert.assertEquals(paymentJson.getChargebacks().size(), 1);
-        final ChargebackJson chargebackJson = paymentJson.getChargebacks().get(0);
-        Assert.assertEquals(chargebackJson.getPaymentId(), paymentJson.getPaymentId());
-        Assert.assertEquals(chargebackJson.getChargebackAmount().compareTo(chargebackAmount), 0);
-        final List<AuditLogJson> chargebackAuditLogs = chargebackJson.getAuditLogs();
-        Assert.assertEquals(chargebackAuditLogs.size(), 1);
-        verifyAuditLog(chargebackAuditLogs.get(0), ChangeType.INSERT, reason, comment, createdBy, startTime, endTime);
-
-        // Verify invoices
-        Assert.assertEquals(timeline.getInvoices().size(), 2);
-        final List<AuditLogJson> firstInvoiceAuditLogs = timeline.getInvoices().get(0).getAuditLogs();
-        Assert.assertEquals(firstInvoiceAuditLogs.size(), 1);
-        verifyAuditLog(firstInvoiceAuditLogs.get(0), ChangeType.INSERT, null, null, "Transition", startTime, endTime);
-        final List<AuditLogJson> secondInvoiceAuditLogs = timeline.getInvoices().get(1).getAuditLogs();
-        Assert.assertEquals(secondInvoiceAuditLogs.size(), 1);
-        verifyAuditLog(secondInvoiceAuditLogs.get(0), ChangeType.INSERT, null, null, "Transition", startTime, endTime);
-
-        // Verify credits
-        final List<CreditJson> credits = timeline.getInvoices().get(1).getCredits();
-        Assert.assertEquals(credits.size(), 1);
-        Assert.assertEquals(credits.get(0).getCreditAmount().compareTo(creditAmount.negate()), 0);
-        final List<AuditLogJson> creditAuditLogs = credits.get(0).getAuditLogs();
-        Assert.assertEquals(creditAuditLogs.size(), 1);
-        verifyAuditLog(creditAuditLogs.get(0), ChangeType.INSERT, reason, comment, createdBy, startTime, endTime);
-
-        // Verify bundles
-        Assert.assertEquals(timeline.getBundles().size(), 1);
-        Assert.assertEquals(timeline.getBundles().get(0).getSubscriptions().size(), 1);
-        Assert.assertEquals(timeline.getBundles().get(0).getSubscriptions().get(0).getEvents().size(), 2);
-        final List<AuditLogJson> bundleAuditLogs = timeline.getBundles().get(0).getAuditLogs();
-        Assert.assertEquals(bundleAuditLogs.size(), 3);
-        verifyAuditLog(bundleAuditLogs.get(0), ChangeType.INSERT, reason, comment, createdBy, startTime, endTime);
-        verifyAuditLog(bundleAuditLogs.get(1), ChangeType.UPDATE, null, null, "Transition", startTime, endTime);
-        verifyAuditLog(bundleAuditLogs.get(2), ChangeType.UPDATE, null, null, "Transition", startTime, endTime);
-
-        // TODO subscription events audit logs
-    }
-
-    private void verifyAuditLog(final AuditLogJson auditLogJson, final ChangeType changeType, @Nullable final String reasonCode,
-                                @Nullable final String comments, @Nullable final String changedBy,
-                                final DateTime startTime, final DateTime endTime) {
-        Assert.assertEquals(auditLogJson.getChangeType(), changeType.toString());
-        Assert.assertFalse(auditLogJson.getChangeDate().isBefore(startTime));
-        // Flaky
-        //Assert.assertFalse(auditLogJson.getChangeDate().isAfter(endTime));
-        Assert.assertEquals(auditLogJson.getReasonCode(), reasonCode);
-        Assert.assertEquals(auditLogJson.getComments(), comments);
-        Assert.assertEquals(auditLogJson.getChangedBy(), changedBy);
-    }
-
     @Test(groups = "slow")
     public void testAccountPaymentMethods() throws Exception {
 
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestAccountTimeline.java b/server/src/test/java/com/ning/billing/jaxrs/TestAccountTimeline.java
new file mode 100644
index 0000000..91496c9
--- /dev/null
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestAccountTimeline.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.jaxrs;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.DateTime;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.jaxrs.json.AccountJson;
+import com.ning.billing.jaxrs.json.AccountTimelineJson;
+import com.ning.billing.jaxrs.json.AuditLogJson;
+import com.ning.billing.jaxrs.json.ChargebackJson;
+import com.ning.billing.jaxrs.json.CreditJson;
+import com.ning.billing.jaxrs.json.InvoiceJsonSimple;
+import com.ning.billing.jaxrs.json.PaymentJsonSimple;
+import com.ning.billing.jaxrs.json.PaymentJsonWithBundleKeys;
+import com.ning.billing.jaxrs.json.RefundJson;
+import com.ning.billing.util.ChangeType;
+import com.ning.billing.util.api.AuditLevel;
+
+public class TestAccountTimeline extends TestJaxrsBase {
+
+    private static final String PAYMENT_REQUEST_PROCESSOR = "PaymentRequestProcessor";
+    private static final String TRANSITION = "Transition";
+
+    @Test(groups = "slow")
+    public void testAccountTimelineWithAudits() throws Exception {
+        final DateTime startTime = clock.getUTCNow();
+        final AccountJson accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
+        final DateTime endTime = clock.getUTCNow();
+
+        // Add credit
+        final InvoiceJsonSimple invoice = getInvoicesForAccount(accountJson.getAccountId()).get(1);
+        final DateTime creditEffectiveDate = clock.getUTCNow();
+        final BigDecimal creditAmount = BigDecimal.ONE;
+        createCreditForInvoice(accountJson.getAccountId(), invoice.getInvoiceId(),
+                               creditAmount, clock.getUTCNow(), creditEffectiveDate);
+
+        // Add refund
+        final PaymentJsonSimple postedPayment = getPaymentsForAccount(accountJson.getAccountId()).get(0);
+        final BigDecimal refundAmount = BigDecimal.ONE;
+        createRefund(postedPayment.getPaymentId(), refundAmount);
+
+        // Add chargeback
+        final BigDecimal chargebackAmount = BigDecimal.ONE;
+        createChargeBack(postedPayment.getPaymentId(), chargebackAmount);
+
+        // Verify payments
+        verifyPayments(accountJson.getAccountId(), startTime, endTime, refundAmount, chargebackAmount);
+
+        // Verify invoices
+        verifyInvoices(accountJson.getAccountId(), startTime, endTime);
+
+        // Verify credits
+        verifyCredits(accountJson.getAccountId(), startTime, endTime, creditAmount);
+
+        // Verify bundles
+        verifyBundles(accountJson.getAccountId(), startTime, endTime);
+    }
+
+    private void verifyPayments(final String accountId, final DateTime startTime, final DateTime endTime,
+                                final BigDecimal refundAmount, final BigDecimal chargebackAmount) throws Exception {
+        for (final AuditLevel auditLevel : AuditLevel.values()) {
+            final AccountTimelineJson timeline = getAccountTimelineWithAudits(accountId, auditLevel);
+
+            // Verify payments
+            Assert.assertEquals(timeline.getPayments().size(), 1);
+            final PaymentJsonWithBundleKeys paymentJson = timeline.getPayments().get(0);
+
+            // Verify refunds
+            Assert.assertEquals(paymentJson.getRefunds().size(), 1);
+            final RefundJson refundJson = paymentJson.getRefunds().get(0);
+            Assert.assertEquals(refundJson.getPaymentId(), paymentJson.getPaymentId());
+            Assert.assertEquals(refundJson.getRefundAmount().compareTo(refundAmount), 0);
+
+            // Verify chargebacks
+            Assert.assertEquals(paymentJson.getChargebacks().size(), 1);
+            final ChargebackJson chargebackJson = paymentJson.getChargebacks().get(0);
+            Assert.assertEquals(chargebackJson.getPaymentId(), paymentJson.getPaymentId());
+            Assert.assertEquals(chargebackJson.getChargebackAmount().compareTo(chargebackAmount), 0);
+
+            // Verify audits
+            final List<AuditLogJson> paymentAuditLogs = paymentJson.getAuditLogs();
+            final List<AuditLogJson> refundAuditLogs = refundJson.getAuditLogs();
+            final List<AuditLogJson> chargebackAuditLogs = chargebackJson.getAuditLogs();
+            if (AuditLevel.NONE.equals(auditLevel)) {
+                // Audits for payments
+                Assert.assertEquals(paymentAuditLogs.size(), 0);
+
+                // Audits for refunds
+                Assert.assertEquals(refundAuditLogs.size(), 0);
+
+                // Audits for chargebacks
+                Assert.assertEquals(chargebackAuditLogs.size(), 0);
+            } else if (AuditLevel.MINIMAL.equals(auditLevel)) {
+                // Audits for payments
+                Assert.assertEquals(paymentAuditLogs.size(), 1);
+                verifyAuditLog(paymentAuditLogs.get(0), ChangeType.INSERT, null, null, PAYMENT_REQUEST_PROCESSOR, startTime, endTime);
+
+                // Audits for refunds
+                Assert.assertEquals(refundAuditLogs.size(), 1);
+                verifyAuditLog(refundAuditLogs.get(0), ChangeType.INSERT, reason, comment, createdBy, startTime, endTime);
+
+                // Audits for chargebacks
+                Assert.assertEquals(chargebackAuditLogs.size(), 1);
+                verifyAuditLog(chargebackAuditLogs.get(0), ChangeType.INSERT, reason, comment, createdBy, startTime, endTime);
+            } else {
+                // Audits for payments
+                Assert.assertEquals(paymentAuditLogs.size(), 2);
+                verifyAuditLog(paymentAuditLogs.get(0), ChangeType.INSERT, null, null, PAYMENT_REQUEST_PROCESSOR, startTime, endTime);
+                verifyAuditLog(paymentAuditLogs.get(1), ChangeType.UPDATE, null, null, PAYMENT_REQUEST_PROCESSOR, startTime, endTime);
+
+                // Audits for refunds
+                Assert.assertEquals(refundAuditLogs.size(), 3);
+                verifyAuditLog(refundAuditLogs.get(0), ChangeType.INSERT, reason, comment, createdBy, startTime, endTime);
+                verifyAuditLog(refundAuditLogs.get(1), ChangeType.UPDATE, reason, comment, createdBy, startTime, endTime);
+                verifyAuditLog(refundAuditLogs.get(2), ChangeType.UPDATE, reason, comment, createdBy, startTime, endTime);
+
+                // Audits for chargebacks
+                Assert.assertEquals(chargebackAuditLogs.size(), 1);
+                verifyAuditLog(chargebackAuditLogs.get(0), ChangeType.INSERT, reason, comment, createdBy, startTime, endTime);
+            }
+        }
+    }
+
+    private void verifyInvoices(final String accountId, final DateTime startTime, final DateTime endTime) throws Exception {
+        for (final AuditLevel auditLevel : AuditLevel.values()) {
+            final AccountTimelineJson timeline = getAccountTimelineWithAudits(accountId, auditLevel);
+
+            // Verify invoices
+            Assert.assertEquals(timeline.getInvoices().size(), 2);
+
+            // Verify audits
+            final List<AuditLogJson> firstInvoiceAuditLogs = timeline.getInvoices().get(0).getAuditLogs();
+            final List<AuditLogJson> secondInvoiceAuditLogs = timeline.getInvoices().get(1).getAuditLogs();
+            if (AuditLevel.NONE.equals(auditLevel)) {
+                Assert.assertEquals(firstInvoiceAuditLogs.size(), 0);
+                Assert.assertEquals(secondInvoiceAuditLogs.size(), 0);
+            } else {
+                Assert.assertEquals(firstInvoiceAuditLogs.size(), 1);
+                verifyAuditLog(firstInvoiceAuditLogs.get(0), ChangeType.INSERT, null, null, TRANSITION, startTime, endTime);
+                Assert.assertEquals(secondInvoiceAuditLogs.size(), 1);
+                verifyAuditLog(secondInvoiceAuditLogs.get(0), ChangeType.INSERT, null, null, TRANSITION, startTime, endTime);
+            }
+        }
+    }
+
+    private void verifyCredits(final String accountId, final DateTime startTime, final DateTime endTime, final BigDecimal creditAmount) throws Exception {
+        for (final AuditLevel auditLevel : AuditLevel.values()) {
+            final AccountTimelineJson timeline = getAccountTimelineWithAudits(accountId, auditLevel);
+
+            // Verify credits
+            final List<CreditJson> credits = timeline.getInvoices().get(1).getCredits();
+            Assert.assertEquals(credits.size(), 1);
+            Assert.assertEquals(credits.get(0).getCreditAmount().compareTo(creditAmount.negate()), 0);
+
+            // Verify audits
+            final List<AuditLogJson> creditAuditLogs = credits.get(0).getAuditLogs();
+            if (AuditLevel.NONE.equals(auditLevel)) {
+                Assert.assertEquals(creditAuditLogs.size(), 0);
+            } else {
+                Assert.assertEquals(creditAuditLogs.size(), 1);
+                verifyAuditLog(creditAuditLogs.get(0), ChangeType.INSERT, reason, comment, createdBy, startTime, endTime);
+            }
+        }
+    }
+
+    private void verifyBundles(final String accountId, final DateTime startTime, final DateTime endTime) throws Exception {
+        for (final AuditLevel auditLevel : AuditLevel.values()) {
+            final AccountTimelineJson timeline = getAccountTimelineWithAudits(accountId, auditLevel);
+
+            // Verify bundles
+            Assert.assertEquals(timeline.getBundles().size(), 1);
+            Assert.assertEquals(timeline.getBundles().get(0).getSubscriptions().size(), 1);
+            Assert.assertEquals(timeline.getBundles().get(0).getSubscriptions().get(0).getEvents().size(), 2);
+
+            // Verify audits
+            final List<AuditLogJson> bundleAuditLogs = timeline.getBundles().get(0).getAuditLogs();
+            if (AuditLevel.NONE.equals(auditLevel)) {
+                Assert.assertEquals(bundleAuditLogs.size(), 0);
+            } else if (AuditLevel.MINIMAL.equals(auditLevel)) {
+                Assert.assertEquals(bundleAuditLogs.size(), 1);
+                verifyAuditLog(bundleAuditLogs.get(0), ChangeType.INSERT, reason, comment, createdBy, startTime, endTime);
+            } else {
+                Assert.assertEquals(bundleAuditLogs.size(), 3);
+                verifyAuditLog(bundleAuditLogs.get(0), ChangeType.INSERT, reason, comment, createdBy, startTime, endTime);
+                verifyAuditLog(bundleAuditLogs.get(1), ChangeType.UPDATE, null, null, TRANSITION, startTime, endTime);
+                verifyAuditLog(bundleAuditLogs.get(2), ChangeType.UPDATE, null, null, TRANSITION, startTime, endTime);
+            }
+
+            // TODO subscription events audit logs
+        }
+    }
+
+    private void verifyAuditLog(final AuditLogJson auditLogJson, final ChangeType changeType, @Nullable final String reasonCode,
+                                @Nullable final String comments, @Nullable final String changedBy,
+                                final DateTime startTime, final DateTime endTime) {
+        Assert.assertEquals(auditLogJson.getChangeType(), changeType.toString());
+        Assert.assertFalse(auditLogJson.getChangeDate().isBefore(startTime));
+        // Flaky
+        //Assert.assertFalse(auditLogJson.getChangeDate().isAfter(endTime));
+        Assert.assertEquals(auditLogJson.getReasonCode(), reasonCode);
+        Assert.assertEquals(auditLogJson.getComments(), comments);
+        Assert.assertEquals(auditLogJson.getChangedBy(), changedBy);
+    }
+}
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
index 062769c..71069c8 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
@@ -86,6 +86,7 @@ import com.ning.billing.payment.provider.MockPaymentProviderPluginModule;
 import com.ning.billing.server.ServerTestSuiteWithEmbeddedDB;
 import com.ning.billing.server.listeners.KillbillGuiceListener;
 import com.ning.billing.server.modules.KillbillServerModule;
+import com.ning.billing.util.api.AuditLevel;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.ClockMock;
 import com.ning.billing.util.email.EmailModule;
@@ -364,17 +365,17 @@ public class TestJaxrsBase extends ServerTestSuiteWithEmbeddedDB {
     }
 
     protected AccountTimelineJson getAccountTimeline(final String accountId) throws Exception {
-        return doGetAccountTimeline(accountId, false);
+        return doGetAccountTimeline(accountId, AuditLevel.NONE);
     }
 
-    protected AccountTimelineJson getAccountTimelineWithAudits(final String accountId) throws Exception {
-        return doGetAccountTimeline(accountId, true);
+    protected AccountTimelineJson getAccountTimelineWithAudits(final String accountId, final AuditLevel auditLevel) throws Exception {
+        return doGetAccountTimeline(accountId, auditLevel);
     }
 
-    private AccountTimelineJson doGetAccountTimeline(final String accountId, final Boolean withAudits) throws Exception {
+    private AccountTimelineJson doGetAccountTimeline(final String accountId, final AuditLevel auditLevel) throws Exception {
         final String uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountId + "/" + JaxrsResource.TIMELINE;
 
-        final Response response = doGet(uri, ImmutableMap.<String, String>of(JaxrsResource.QUERY_AUDIT, withAudits.toString()), DEFAULT_HTTP_TIMEOUT_SEC);
+        final Response response = doGet(uri, ImmutableMap.<String, String>of(JaxrsResource.QUERY_AUDIT, auditLevel.toString()), DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
 
         final String baseJson = response.getResponseBody();
diff --git a/util/src/main/java/com/ning/billing/util/audit/dao/DefaultAuditDao.java b/util/src/main/java/com/ning/billing/util/audit/dao/DefaultAuditDao.java
index 0e71983..c70b99c 100644
--- a/util/src/main/java/com/ning/billing/util/audit/dao/DefaultAuditDao.java
+++ b/util/src/main/java/com/ning/billing/util/audit/dao/DefaultAuditDao.java
@@ -24,6 +24,7 @@ import javax.inject.Inject;
 
 import org.skife.jdbi.v2.IDBI;
 
+import com.ning.billing.util.ChangeType;
 import com.ning.billing.util.api.AuditLevel;
 import com.ning.billing.util.audit.AuditLog;
 import com.ning.billing.util.dao.AuditSqlDao;
@@ -81,7 +82,13 @@ public class DefaultAuditDao implements AuditDao {
         if (AuditLevel.FULL.equals(auditLevel)) {
             return allAuditLogs;
         } else if (AuditLevel.MINIMAL.equals(auditLevel) && allAuditLogs.size() > 0) {
-            return ImmutableList.<AuditLog>of(allAuditLogs.get(0));
+            if (ChangeType.INSERT.equals(allAuditLogs.get(0).getChangeType())) {
+                return ImmutableList.<AuditLog>of(allAuditLogs.get(0));
+            } else {
+                // We may be coming here via the history code path - only a single mapped history record id
+                // will be for the initial INSERT
+                return ImmutableList.<AuditLog>of();
+            }
         } else if (AuditLevel.NONE.equals(auditLevel)) {
             return ImmutableList.<AuditLog>of();
         } else {