killbill-memoizeit

beatrix: update simple Analytics test to verify system events Make

6/29/2012 8:43:44 PM

Details

diff --git a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
index 4daeb39..247a438 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
@@ -25,6 +25,7 @@ import com.ning.billing.entitlement.api.timeline.RepairEntitlementEvent;
 import com.ning.billing.entitlement.api.user.EffectiveSubscriptionEvent;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.RequestedSubscriptionEvent;
+import com.ning.billing.entitlement.api.user.SubscriptionEvent;
 import com.ning.billing.invoice.api.EmptyInvoiceEvent;
 import com.ning.billing.invoice.api.InvoiceCreationEvent;
 import com.ning.billing.payment.api.PaymentErrorEvent;
@@ -56,38 +57,14 @@ public class AnalyticsListener {
     }
 
     @Subscribe
-    public void handleSubscriptionTransitionChange(final EffectiveSubscriptionEvent eventEffective) throws AccountApiException, EntitlementUserApiException {
-        switch (eventEffective.getTransitionType()) {
-            // A subscription enters either through migration or as newly created subscription
-            case MIGRATE_ENTITLEMENT:
-            case CREATE:
-                bstRecorder.subscriptionCreated(eventEffective);
-                break;
-            case RE_CREATE:
-                bstRecorder.subscriptionRecreated(eventEffective);
-                break;
-            case MIGRATE_BILLING:
-                break;
-            case CANCEL:
-                bstRecorder.subscriptionCancelled(eventEffective);
-                break;
-            case CHANGE:
-                bstRecorder.subscriptionChanged(eventEffective);
-                break;
-            case UNCANCEL:
-                break;
-            case PHASE:
-                bstRecorder.subscriptionPhaseChanged(eventEffective);
-                break;
-            default:
-                throw new RuntimeException("Unexpected event type " + eventEffective.getTransitionType());
-        }
+    public void handleEffectiveSubscriptionTransitionChange(final EffectiveSubscriptionEvent eventEffective) throws AccountApiException, EntitlementUserApiException {
+        handleSubscriptionTransitionChange(eventEffective);
     }
 
     @Subscribe
-    public void handleFutureSubscriptionTransitionChange(final RequestedSubscriptionEvent eventRequested) throws AccountApiException, EntitlementUserApiException {
+    public void handleRequestedSubscriptionTransitionChange(final RequestedSubscriptionEvent eventRequested) throws AccountApiException, EntitlementUserApiException {
         if (eventRequested.getEffectiveTransitionTime().isAfter(eventRequested.getRequestedTransitionTime())) {
-            bstRecorder.subscriptionChanged(eventRequested);
+            handleSubscriptionTransitionChange(eventRequested);
         }
     }
 
@@ -169,4 +146,32 @@ public class AnalyticsListener {
     public void handleRepairEntitlement(final RepairEntitlementEvent event) {
         // Ignored for now
     }
+
+    private void handleSubscriptionTransitionChange(final SubscriptionEvent eventEffective) throws AccountApiException, EntitlementUserApiException {
+        switch (eventEffective.getTransitionType()) {
+            // A subscription enters either through migration or as newly created subscription
+            case MIGRATE_ENTITLEMENT:
+            case CREATE:
+                bstRecorder.subscriptionCreated(eventEffective);
+                break;
+            case RE_CREATE:
+                bstRecorder.subscriptionRecreated(eventEffective);
+                break;
+            case MIGRATE_BILLING:
+                break;
+            case CANCEL:
+                bstRecorder.subscriptionCancelled(eventEffective);
+                break;
+            case CHANGE:
+                bstRecorder.subscriptionChanged(eventEffective);
+                break;
+            case UNCANCEL:
+                break;
+            case PHASE:
+                bstRecorder.subscriptionPhaseChanged(eventEffective);
+                break;
+            default:
+                throw new RuntimeException("Unexpected event type " + eventEffective.getTransitionType());
+        }
+    }
 }
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java
index b5a1b68..b27c056 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java
@@ -19,6 +19,8 @@ package com.ning.billing.analytics;
 import java.util.List;
 
 import org.joda.time.DateTime;
+import org.skife.jdbi.v2.Transaction;
+import org.skife.jdbi.v2.TransactionStatus;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -32,10 +34,8 @@ import com.ning.billing.analytics.model.BusinessSubscriptionEvent;
 import com.ning.billing.analytics.model.BusinessSubscriptionTransition;
 import com.ning.billing.catalog.api.CatalogService;
 import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.entitlement.api.user.EffectiveSubscriptionEvent;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
-import com.ning.billing.entitlement.api.user.RequestedSubscriptionEvent;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionEvent;
 
@@ -55,34 +55,28 @@ public class BusinessSubscriptionTransitionRecorder {
         this.accountApi = accountApi;
     }
 
-    public void subscriptionCreated(final EffectiveSubscriptionEvent created) throws AccountApiException, EntitlementUserApiException {
+    public void subscriptionCreated(final SubscriptionEvent created) throws AccountApiException, EntitlementUserApiException {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCreated(created.getNextPlan(), catalogService.getFullCatalog(), created.getEffectiveTransitionTime(), created.getSubscriptionStartDate());
         recordTransition(event, created);
     }
 
-    public void subscriptionRecreated(final EffectiveSubscriptionEvent recreated) throws AccountApiException, EntitlementUserApiException {
+    public void subscriptionRecreated(final SubscriptionEvent recreated) throws AccountApiException, EntitlementUserApiException {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionRecreated(recreated.getNextPlan(), catalogService.getFullCatalog(), recreated.getEffectiveTransitionTime(), recreated.getSubscriptionStartDate());
         recordTransition(event, recreated);
     }
 
-    public void subscriptionCancelled(final EffectiveSubscriptionEvent cancelled) throws AccountApiException, EntitlementUserApiException {
+    public void subscriptionCancelled(final SubscriptionEvent cancelled) throws AccountApiException, EntitlementUserApiException {
         // cancelled.getNextPlan() is null here - need to look at the previous one to create the correct event name
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCancelled(cancelled.getPreviousPlan(), catalogService.getFullCatalog(), cancelled.getEffectiveTransitionTime(), cancelled.getSubscriptionStartDate());
         recordTransition(event, cancelled);
     }
 
-    public void subscriptionChanged(final RequestedSubscriptionEvent changed) throws EntitlementUserApiException, AccountApiException {
-        // Future change
+    public void subscriptionChanged(final SubscriptionEvent changed) throws AccountApiException, EntitlementUserApiException {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionChanged(changed.getNextPlan(), catalogService.getFullCatalog(), changed.getEffectiveTransitionTime(), changed.getSubscriptionStartDate());
         recordTransition(event, changed);
     }
 
-    public void subscriptionChanged(final EffectiveSubscriptionEvent changed) throws AccountApiException, EntitlementUserApiException {
-        final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionChanged(changed.getNextPlan(), catalogService.getFullCatalog(), changed.getEffectiveTransitionTime(), changed.getSubscriptionStartDate());
-        recordTransition(event, changed);
-    }
-
-    public void subscriptionPhaseChanged(final EffectiveSubscriptionEvent phaseChanged) throws AccountApiException, EntitlementUserApiException {
+    public void subscriptionPhaseChanged(final SubscriptionEvent phaseChanged) throws AccountApiException, EntitlementUserApiException {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPhaseChanged(phaseChanged.getNextPlan(), phaseChanged.getNextState(), catalogService.getFullCatalog(), phaseChanged.getEffectiveTransitionTime(), phaseChanged.getSubscriptionStartDate());
         recordTransition(event, phaseChanged);
     }
@@ -111,9 +105,10 @@ public class BusinessSubscriptionTransitionRecorder {
         if (event.getEventType() != BusinessSubscriptionEvent.EventType.ADD) {
             final List<BusinessSubscriptionTransition> transitions = sqlDao.getTransitions(externalKey);
             if (transitions != null && transitions.size() > 0) {
-                final BusinessSubscriptionTransition lastTransition = transitions.get(transitions.size() - 1);
-                if (lastTransition != null && lastTransition.getNextSubscription() != null) {
-                    previousEffectiveTransitionTime = lastTransition.getNextSubscription().getStartDate();
+                for (final BusinessSubscriptionTransition candidate : transitions) {
+                    if (candidate != null && candidate.getNextSubscription() != null && candidate.getNextSubscription().getStartDate().isBefore(transition.getEffectiveTransitionTime())) {
+                        previousEffectiveTransitionTime = candidate.getNextSubscription().getStartDate();
+                    }
                 }
             }
         }
@@ -178,6 +173,31 @@ public class BusinessSubscriptionTransitionRecorder {
         );
 
         log.info(transition.getEvent() + " " + transition);
-        sqlDao.createTransition(transition);
+        sqlDao.inTransaction(new Transaction<Void, BusinessSubscriptionTransitionSqlDao>() {
+            @Override
+            public Void inTransaction(final BusinessSubscriptionTransitionSqlDao transactional, final TransactionStatus status) throws Exception {
+                final String subscriptionId;
+                if (nextSubscription.getSubscriptionId() != null) {
+                    subscriptionId = nextSubscription.getSubscriptionId().toString();
+                } else {
+                    subscriptionId = prevSubscription.getSubscriptionId().toString();
+                }
+
+                // Ignore duplicates: for e.g. phase events, we may already have recorded the transition when the change
+                // was requested. In that case, ignore it
+                final List<BusinessSubscriptionTransition> currentTransitions = transactional.getTransitionForSubscription(subscriptionId);
+                if (currentTransitions != null && currentTransitions.size() > 0) {
+                    for (final BusinessSubscriptionTransition currentTransition : currentTransitions) {
+                        if (currentTransition.isDuplicateOf(transition)) {
+                            return null;
+                        }
+                    }
+                }
+
+                transactional.createTransition(transition);
+
+                return null;
+            }
+        });
     }
 }
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessSubscriptionTransitionSqlDao.java b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessSubscriptionTransitionSqlDao.java
index 9e35630..5b2838c 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessSubscriptionTransitionSqlDao.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessSubscriptionTransitionSqlDao.java
@@ -22,13 +22,14 @@ import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
+import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
 
 import com.ning.billing.analytics.model.BusinessSubscriptionTransition;
 
 @ExternalizedSqlViaStringTemplate3()
 @RegisterMapper(BusinessSubscriptionTransitionMapper.class)
-public interface BusinessSubscriptionTransitionSqlDao {
+public interface BusinessSubscriptionTransitionSqlDao extends Transactional<BusinessSubscriptionTransitionSqlDao> {
     @SqlQuery
     List<BusinessSubscriptionTransition> getTransitions(@Bind("external_key") final String externalKey);
 
diff --git a/analytics/src/main/java/com/ning/billing/analytics/model/BusinessSubscriptionTransition.java b/analytics/src/main/java/com/ning/billing/analytics/model/BusinessSubscriptionTransition.java
index 3618b7d..2e2a6ce 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/model/BusinessSubscriptionTransition.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/model/BusinessSubscriptionTransition.java
@@ -112,15 +112,28 @@ public class BusinessSubscriptionTransition {
 
         final BusinessSubscriptionTransition that = (BusinessSubscriptionTransition) o;
 
+        return totalOrdering == that.totalOrdering && isDuplicateOf(that);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = (int) (totalOrdering ^ (totalOrdering >>> 32));
+        result = 31 * result + (externalKey != null ? externalKey.hashCode() : 0);
+        result = 31 * result + (accountKey != null ? accountKey.hashCode() : 0);
+        result = 31 * result + (requestedTimestamp != null ? requestedTimestamp.hashCode() : 0);
+        result = 31 * result + (event != null ? event.hashCode() : 0);
+        result = 31 * result + (previousSubscription != null ? previousSubscription.hashCode() : 0);
+        result = 31 * result + (nextSubscription != null ? nextSubscription.hashCode() : 0);
+        return result;
+    }
+
+    public boolean isDuplicateOf(final BusinessSubscriptionTransition that) {
         if (accountKey != null ? !accountKey.equals(that.accountKey) : that.accountKey != null) {
             return false;
         }
         if (event != null ? !event.equals(that.event) : that.event != null) {
             return false;
         }
-        if (totalOrdering != that.totalOrdering) {
-            return false;
-        }
         if (externalKey != null ? !externalKey.equals(that.externalKey) : that.externalKey != null) {
             return false;
         }
@@ -136,16 +149,4 @@ public class BusinessSubscriptionTransition {
 
         return true;
     }
-
-    @Override
-    public int hashCode() {
-        int result = (int) (totalOrdering ^ (totalOrdering >>> 32));
-        result = 31 * result + (externalKey != null ? externalKey.hashCode() : 0);
-        result = 31 * result + (accountKey != null ? accountKey.hashCode() : 0);
-        result = 31 * result + (requestedTimestamp != null ? requestedTimestamp.hashCode() : 0);
-        result = 31 * result + (event != null ? event.hashCode() : 0);
-        result = 31 * result + (previousSubscription != null ? previousSubscription.hashCode() : 0);
-        result = 31 * result + (nextSubscription != null ? nextSubscription.hashCode() : 0);
-        return result;
-    }
 }
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockBusinessSubscriptionTransitionSqlDao.java b/analytics/src/test/java/com/ning/billing/analytics/MockBusinessSubscriptionTransitionSqlDao.java
index 056d28f..d05fa9b 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockBusinessSubscriptionTransitionSqlDao.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockBusinessSubscriptionTransitionSqlDao.java
@@ -21,6 +21,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.sqlobject.Bind;
 
 import com.google.common.collect.ImmutableList;
@@ -58,4 +59,33 @@ public class MockBusinessSubscriptionTransitionSqlDao implements BusinessSubscri
     @Override
     public void test() {
     }
+
+    @Override
+    public void begin() {
+    }
+
+    @Override
+    public void commit() {
+    }
+
+    @Override
+    public void rollback() {
+    }
+
+    @Override
+    public void checkpoint(final String name) {
+    }
+
+    @Override
+    public void release(final String name) {
+    }
+
+    @Override
+    public void rollback(final String name) {
+    }
+
+    @Override
+    public <ReturnType> ReturnType inTransaction(final Transaction<ReturnType, BusinessSubscriptionTransitionSqlDao> func) {
+        return null;
+    }
 }
diff --git a/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java b/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java
index d43e0dc..e6334fe 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java
@@ -98,7 +98,7 @@ public class TestAnalyticsListener extends AnalyticsTestSuite {
         final DateTime requestedTransitionTime = effectiveTransitionTime;
         final SubscriptionTransitionData firstTransition = createFirstSubscriptionTransition(requestedTransitionTime, effectiveTransitionTime);
         final BusinessSubscriptionTransition firstBST = createExpectedFirstBST(firstTransition.getTotalOrdering(), requestedTransitionTime, effectiveTransitionTime);
-        listener.handleSubscriptionTransitionChange(new DefaultEffectiveSubscriptionEvent(firstTransition, effectiveTransitionTime));
+        listener.handleEffectiveSubscriptionTransitionChange(new DefaultEffectiveSubscriptionEvent(firstTransition, effectiveTransitionTime));
         Assert.assertEquals(dao.getTransitions(EXTERNAL_KEY).size(), 1);
         Assert.assertEquals(dao.getTransitions(EXTERNAL_KEY).get(0), firstBST);
 
@@ -107,7 +107,7 @@ public class TestAnalyticsListener extends AnalyticsTestSuite {
         final DateTime requestedCancelTransitionTime = effectiveCancelTransitionTime;
         final SubscriptionTransitionData cancelledSubscriptionTransition = createCancelSubscriptionTransition(requestedCancelTransitionTime, effectiveCancelTransitionTime, firstTransition.getNextState());
         final BusinessSubscriptionTransition cancelledBST = createExpectedCancelledBST(cancelledSubscriptionTransition.getTotalOrdering(), requestedCancelTransitionTime, effectiveCancelTransitionTime, firstBST.getNextSubscription());
-        listener.handleSubscriptionTransitionChange(new DefaultEffectiveSubscriptionEvent(cancelledSubscriptionTransition, effectiveTransitionTime));
+        listener.handleEffectiveSubscriptionTransitionChange(new DefaultEffectiveSubscriptionEvent(cancelledSubscriptionTransition, effectiveTransitionTime));
         Assert.assertEquals(dao.getTransitions(EXTERNAL_KEY).size(), 2);
         Assert.assertEquals(dao.getTransitions(EXTERNAL_KEY).get(1), cancelledBST);
 
@@ -116,7 +116,7 @@ public class TestAnalyticsListener extends AnalyticsTestSuite {
         final DateTime requestedRecreatedTransitionTime = effectiveRecreatedTransitionTime;
         final SubscriptionTransitionData recreatedSubscriptionTransition = createRecreatedSubscriptionTransition(requestedRecreatedTransitionTime, effectiveRecreatedTransitionTime, cancelledSubscriptionTransition.getNextState());
         final BusinessSubscriptionTransition recreatedBST = createExpectedRecreatedBST(recreatedSubscriptionTransition.getTotalOrdering(), requestedRecreatedTransitionTime, effectiveRecreatedTransitionTime, cancelledBST.getNextSubscription());
-        listener.handleSubscriptionTransitionChange(new DefaultEffectiveSubscriptionEvent(recreatedSubscriptionTransition, effectiveTransitionTime));
+        listener.handleEffectiveSubscriptionTransitionChange(new DefaultEffectiveSubscriptionEvent(recreatedSubscriptionTransition, effectiveTransitionTime));
         Assert.assertEquals(dao.getTransitions(EXTERNAL_KEY).size(), 3);
         Assert.assertEquals(dao.getTransitions(EXTERNAL_KEY).get(2), recreatedBST);
 
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java
index 4c33fba..4aa125a 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java
@@ -38,11 +38,14 @@ import com.ning.billing.analytics.model.BusinessInvoiceItem;
 import com.ning.billing.analytics.model.BusinessSubscriptionEvent;
 import com.ning.billing.analytics.model.BusinessSubscriptionTransition;
 import com.ning.billing.analytics.utils.Rounder;
+import com.ning.billing.api.TestApiListener;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.CatalogApiException;
 import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.catalog.api.Product;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.Subscription;
@@ -74,6 +77,16 @@ public class TestAnalytics extends TestIntegrationBase {
 
         // Add a subscription
         final Subscription subscription = verifyFirstSubscription(account, bundle);
+
+        // Move after trial
+        clock.addDeltaFromReality(AT_LEAST_ONE_MONTH_MS);
+        busHandler.pushExpectedEvent(TestApiListener.NextEvent.PHASE);
+        busHandler.pushExpectedEvent(TestApiListener.NextEvent.INVOICE);
+        busHandler.pushExpectedEvent(TestApiListener.NextEvent.PAYMENT);
+        Assert.assertTrue(busHandler.isCompleted(DELAY));
+
+        // Check BST - nothing should have changed
+        verifyBSTWithTrialAndEvergreenPhases(account, bundle, subscription);
     }
 
     @Test(groups = "slow")
@@ -207,31 +220,76 @@ public class TestAnalytics extends TestIntegrationBase {
 
         waitALittle();
 
+        // Verify BST
+        verifyBSTWithTrialAndEvergreenPhases(account, bundle, subscription);
+
+        // Make sure the account balance is still zero
+        final BusinessAccount businessAccount = analyticsUserApi.getAccountByKey(account.getExternalKey());
+        Assert.assertEquals(businessAccount.getBalance().doubleValue(), Rounder.round(BigDecimal.ZERO));
+        Assert.assertEquals(businessAccount.getTotalInvoiceBalance().doubleValue(), Rounder.round(BigDecimal.ZERO));
+
+        // The account should have one invoice for the trial phase
+        final List<BusinessInvoice> invoices = analyticsUserApi.getInvoicesForAccount(account.getExternalKey());
+        Assert.assertEquals(invoices.size(), 1);
+        final BusinessInvoice invoice = invoices.get(0);
+        Assert.assertEquals(invoice.getBalance().doubleValue(), 0.0);
+        Assert.assertEquals(invoice.getAmountCharged().doubleValue(), 0.0);
+        Assert.assertEquals(invoice.getAmountCredited().doubleValue(), 0.0);
+        Assert.assertEquals(invoice.getAmountPaid().doubleValue(), 0.0);
+        Assert.assertEquals(invoice.getCurrency(), account.getCurrency());
+
+        // The invoice should have a single item associated to it
+        final List<BusinessInvoiceItem> invoiceItems = analyticsUserApi.getInvoiceItemsForInvoice(invoice.getInvoiceId());
+        Assert.assertEquals(invoiceItems.size(), 1);
+        final BusinessInvoiceItem invoiceItem = invoiceItems.get(0);
+        Assert.assertEquals(invoiceItem.getAmount().doubleValue(), 0.0);
+        // No billing period for the trial item
+        Assert.assertEquals(invoiceItem.getBillingPeriod(), subscription.getCurrentPhase().getBillingPeriod().toString());
+        Assert.assertEquals(invoiceItem.getCurrency(), account.getCurrency());
+        // The subscription end date is null (evergreen)
+        Assert.assertEquals(invoiceItem.getEndDate(), subscription.getStartDate().plus(subscription.getCurrentPhase().getDuration().toJodaPeriod()));
+        Assert.assertEquals(invoiceItem.getExternalKey(), bundle.getKey());
+        Assert.assertEquals(invoiceItem.getInvoiceId(), invoice.getInvoiceId());
+        Assert.assertEquals(invoiceItem.getItemType(), "FIXED");
+        Assert.assertEquals(invoiceItem.getPhase(), subscription.getCurrentPhase().getPhaseType().toString());
+        Assert.assertEquals(invoiceItem.getProductCategory(), subscription.getCurrentPlan().getProduct().getCategory().toString());
+        Assert.assertEquals(invoiceItem.getProductName(), subscription.getCurrentPlan().getProduct().getName());
+        Assert.assertEquals(invoiceItem.getProductType(), subscription.getCurrentPlan().getProduct().getCatalogName());
+        Assert.assertEquals(invoiceItem.getSlug(), subscription.getCurrentPhase().getName());
+        Assert.assertEquals(invoiceItem.getStartDate(), subscription.getStartDate());
+
+        return subscription;
+    }
+
+    private void verifyBSTWithTrialAndEvergreenPhases(final Account account, final SubscriptionBundle bundle, final Subscription subscription) throws CatalogApiException {
         // BST should have two transitions
         final List<BusinessSubscriptionTransition> transitions = analyticsUserApi.getTransitionsForBundle(bundle.getKey());
         Assert.assertEquals(transitions.size(), 2);
 
+        final Plan currentPlan = subscription.getCurrentPlan();
+        final Product currentProduct = currentPlan.getProduct();
+
         // Check the first transition (into trial phase)
         final BusinessSubscriptionTransition initialTransition = transitions.get(0);
         Assert.assertEquals(initialTransition.getExternalKey(), bundle.getKey());
         Assert.assertEquals(initialTransition.getAccountKey(), account.getExternalKey());
-        Assert.assertEquals(initialTransition.getEvent().getCategory(), phaseSpecifier.getProductCategory());
+        Assert.assertEquals(initialTransition.getEvent().getCategory(), currentProduct.getCategory());
         Assert.assertEquals(initialTransition.getEvent().getEventType(), BusinessSubscriptionEvent.EventType.ADD);
 
         // This is the first transition
         Assert.assertNull(initialTransition.getPreviousSubscription());
 
-        Assert.assertEquals(initialTransition.getNextSubscription().getBillingPeriod(), subscription.getCurrentPhase().getBillingPeriod().toString());
+        Assert.assertEquals(initialTransition.getNextSubscription().getBillingPeriod(), BillingPeriod.NO_BILLING_PERIOD.toString());
         Assert.assertEquals(initialTransition.getNextSubscription().getBundleId(), subscription.getBundleId());
         Assert.assertEquals(initialTransition.getNextSubscription().getCurrency(), account.getCurrency().toString());
-        Assert.assertEquals(initialTransition.getNextSubscription().getPhase(), subscription.getCurrentPhase().getPhaseType().toString());
+        Assert.assertEquals(initialTransition.getNextSubscription().getPhase(), PhaseType.TRIAL.toString());
         // Trial: fixed price of zero
-        Assert.assertEquals(initialTransition.getNextSubscription().getPrice().doubleValue(), subscription.getCurrentPhase().getFixedPrice().getPrice(account.getCurrency()).doubleValue());
+        Assert.assertEquals(initialTransition.getNextSubscription().getPrice().doubleValue(), (double) 0);
         Assert.assertEquals(initialTransition.getNextSubscription().getPriceList(), subscription.getCurrentPriceList().getName());
-        Assert.assertEquals(initialTransition.getNextSubscription().getProductCategory(), subscription.getCurrentPlan().getProduct().getCategory());
-        Assert.assertEquals(initialTransition.getNextSubscription().getProductName(), subscription.getCurrentPlan().getProduct().getName());
-        Assert.assertEquals(initialTransition.getNextSubscription().getProductType(), subscription.getCurrentPlan().getProduct().getCatalogName());
-        Assert.assertEquals(initialTransition.getNextSubscription().getSlug(), subscription.getCurrentPhase().getName());
+        Assert.assertEquals(initialTransition.getNextSubscription().getProductCategory(), currentProduct.getCategory());
+        Assert.assertEquals(initialTransition.getNextSubscription().getProductName(), currentProduct.getName());
+        Assert.assertEquals(initialTransition.getNextSubscription().getProductType(), currentProduct.getCatalogName());
+        Assert.assertEquals(initialTransition.getNextSubscription().getSlug(), currentProduct.getName().toLowerCase() + "-monthly-trial");
         Assert.assertEquals(initialTransition.getNextSubscription().getStartDate(), subscription.getStartDate());
         Assert.assertEquals(initialTransition.getNextSubscription().getState(), subscription.getState());
         Assert.assertEquals(initialTransition.getNextSubscription().getSubscriptionId(), subscription.getId());
@@ -240,63 +298,27 @@ public class TestAnalytics extends TestIntegrationBase {
         final BusinessSubscriptionTransition futureTransition = transitions.get(1);
         Assert.assertEquals(futureTransition.getExternalKey(), bundle.getKey());
         Assert.assertEquals(futureTransition.getAccountKey(), account.getExternalKey());
-        Assert.assertEquals(futureTransition.getEvent().getCategory(), phaseSpecifier.getProductCategory());
-        Assert.assertEquals(futureTransition.getEvent().getEventType(), BusinessSubscriptionEvent.EventType.CHANGE);
+        Assert.assertEquals(futureTransition.getEvent().getCategory(), currentProduct.getCategory());
+        Assert.assertEquals(futureTransition.getEvent().getEventType(), BusinessSubscriptionEvent.EventType.SYSTEM_CHANGE);
 
         Assert.assertEquals(futureTransition.getPreviousSubscription(), initialTransition.getNextSubscription());
 
-        Assert.assertEquals(futureTransition.getNextSubscription().getBillingPeriod(), term.toString());
+        // The billing period should have changed (NO_BILLING_PERIOD for the trial period)
+        Assert.assertEquals(futureTransition.getNextSubscription().getBillingPeriod(), BillingPeriod.MONTHLY.toString());
         Assert.assertEquals(futureTransition.getNextSubscription().getBundleId(), subscription.getBundleId());
         Assert.assertEquals(initialTransition.getNextSubscription().getCurrency(), account.getCurrency().toString());
         // From trial to evergreen
         Assert.assertEquals(futureTransition.getNextSubscription().getPhase(), PhaseType.EVERGREEN.toString());
         Assert.assertTrue(futureTransition.getNextSubscription().getPrice().doubleValue() > 0);
         Assert.assertEquals(futureTransition.getNextSubscription().getPriceList(), subscription.getCurrentPriceList().getName());
-        Assert.assertEquals(futureTransition.getNextSubscription().getProductCategory(), subscription.getCurrentPlan().getProduct().getCategory());
-        Assert.assertEquals(futureTransition.getNextSubscription().getProductName(), subscription.getCurrentPlan().getProduct().getName());
-        Assert.assertEquals(futureTransition.getNextSubscription().getProductType(), subscription.getCurrentPlan().getProduct().getCatalogName());
-        Assert.assertEquals(futureTransition.getNextSubscription().getSlug(), subscription.getCurrentPhase().getName().replace("-trial", "-evergreen"));
+        Assert.assertEquals(futureTransition.getNextSubscription().getProductCategory(), currentProduct.getCategory());
+        Assert.assertEquals(futureTransition.getNextSubscription().getProductName(), currentProduct.getName());
+        Assert.assertEquals(futureTransition.getNextSubscription().getProductType(), currentProduct.getCatalogName());
+        Assert.assertEquals(futureTransition.getNextSubscription().getSlug(), currentProduct.getName().toLowerCase() + "-monthly-evergreen");
         // 30 days trial
         Assert.assertEquals(futureTransition.getNextSubscription().getStartDate(), subscription.getStartDate().plusDays(30));
         Assert.assertEquals(futureTransition.getNextSubscription().getState(), subscription.getState());
         Assert.assertEquals(futureTransition.getNextSubscription().getSubscriptionId(), subscription.getId());
-
-        // Make sure the account balance is still zero
-        final BusinessAccount businessAccount = analyticsUserApi.getAccountByKey(account.getExternalKey());
-        Assert.assertEquals(businessAccount.getBalance().doubleValue(), Rounder.round(BigDecimal.ZERO));
-        Assert.assertEquals(businessAccount.getTotalInvoiceBalance().doubleValue(), Rounder.round(BigDecimal.ZERO));
-
-        // The account should have one invoice for the trial phase
-        final List<BusinessInvoice> invoices = analyticsUserApi.getInvoicesForAccount(account.getExternalKey());
-        Assert.assertEquals(invoices.size(), 1);
-        final BusinessInvoice invoice = invoices.get(0);
-        Assert.assertEquals(invoice.getBalance().doubleValue(), 0.0);
-        Assert.assertEquals(invoice.getAmountCharged().doubleValue(), 0.0);
-        Assert.assertEquals(invoice.getAmountCredited().doubleValue(), 0.0);
-        Assert.assertEquals(invoice.getAmountPaid().doubleValue(), 0.0);
-        Assert.assertEquals(invoice.getCurrency(), account.getCurrency());
-
-        // The invoice should have a single item associated to it
-        final List<BusinessInvoiceItem> invoiceItems = analyticsUserApi.getInvoiceItemsForInvoice(invoice.getInvoiceId());
-        Assert.assertEquals(invoiceItems.size(), 1);
-        final BusinessInvoiceItem invoiceItem = invoiceItems.get(0);
-        Assert.assertEquals(invoiceItem.getAmount().doubleValue(), 0.0);
-        // No billing period for the trial item
-        Assert.assertEquals(invoiceItem.getBillingPeriod(), subscription.getCurrentPhase().getBillingPeriod().toString());
-        Assert.assertEquals(invoiceItem.getCurrency(), account.getCurrency());
-        // The subscription end date is null (evergreen)
-        Assert.assertEquals(invoiceItem.getEndDate(), subscription.getStartDate().plus(subscription.getCurrentPhase().getDuration().toJodaPeriod()));
-        Assert.assertEquals(invoiceItem.getExternalKey(), bundle.getKey());
-        Assert.assertEquals(invoiceItem.getInvoiceId(), invoice.getInvoiceId());
-        Assert.assertEquals(invoiceItem.getItemType(), "FIXED");
-        Assert.assertEquals(invoiceItem.getPhase(), subscription.getCurrentPhase().getPhaseType().toString());
-        Assert.assertEquals(invoiceItem.getProductCategory(), subscription.getCurrentPlan().getProduct().getCategory().toString());
-        Assert.assertEquals(invoiceItem.getProductName(), subscription.getCurrentPlan().getProduct().getName());
-        Assert.assertEquals(invoiceItem.getProductType(), subscription.getCurrentPlan().getProduct().getCatalogName());
-        Assert.assertEquals(invoiceItem.getSlug(), subscription.getCurrentPhase().getName());
-        Assert.assertEquals(invoiceItem.getStartDate(), subscription.getStartDate());
-
-        return subscription;
     }
 
     private void verifyChangePlan(final Account account, final SubscriptionBundle bundle, final Subscription subscription) throws EntitlementUserApiException, InterruptedException {