killbill-uncached

Details

diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java
new file mode 100644
index 0000000..ef01b80
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright 2010-2011 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.entitlement.api.migration;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Stage;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.Duration;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.TestApiBase;
+import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApiException;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi.EntitlementAccountMigration;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi.EntitlementBundleMigration;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi.EntitlementSubscriptionMigration;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi.EntitlementSubscriptionMigrationCase;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+import com.ning.billing.entitlement.glue.MockEngineModuleSql;
+
+public abstract class TestMigration extends TestApiBase {
+
+
+    public void testSingleBasePlanReal() {
+
+        try {
+            DateTime beforeMigration = clock.getUTCNow();
+            EntitlementAccountMigration toBeMigrated = createAccountWithRegularBasePlan();
+            DateTime afterMigration = clock.getUTCNow();
+
+            testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
+            migrationApi.migrate(toBeMigrated);
+            assertTrue(testListener.isCompleted(5000));
+
+            List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
+            assertEquals(bundles.size(), 1);
+            SubscriptionBundle bundle = bundles.get(0);
+
+            List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(bundle.getId());
+            assertEquals(subscriptions.size(), 1);
+            Subscription subscription = subscriptions.get(0);
+            assertDateWithin(subscription.getStartDate(), beforeMigration, afterMigration);
+            assertEquals(subscription.getEndDate(), null);
+            assertEquals(subscription.getCurrentPriceList(), PriceListSet.DEFAULT_PRICELIST_NAME);
+            assertEquals(subscription.getCurrentPhase().getPhaseType(), PhaseType.EVERGREEN);
+            assertEquals(subscription.getState(), SubscriptionState.ACTIVE);
+            assertEquals(subscription.getCurrentPlan().getName(), "assault-rifle-annual");
+        } catch (EntitlementMigrationApiException e) {
+            Assert.fail("", e);
+        }
+    }
+
+
+    public void testSingleBasePlanFutureCancelledReal() {
+
+        try {
+
+            DateTime beforeMigration = clock.getUTCNow();
+            EntitlementAccountMigration toBeMigrated = createAccountWithRegularBasePlanFutreCancelled();
+            DateTime afterMigration = clock.getUTCNow();
+
+            testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
+            migrationApi.migrate(toBeMigrated);
+            assertTrue(testListener.isCompleted(5000));
+
+            List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
+            assertEquals(bundles.size(), 1);
+            SubscriptionBundle bundle = bundles.get(0);
+            //assertEquals(bundle.getStartDate(), effectiveDate);
+
+            List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(bundle.getId());
+            assertEquals(subscriptions.size(), 1);
+            Subscription subscription = subscriptions.get(0);
+            assertDateWithin(subscription.getStartDate(), beforeMigration, afterMigration);
+            assertEquals(subscription.getCurrentPriceList(), PriceListSet.DEFAULT_PRICELIST_NAME);
+            assertEquals(subscription.getCurrentPhase().getPhaseType(), PhaseType.EVERGREEN);
+            assertEquals(subscription.getState(), SubscriptionState.ACTIVE);
+            assertEquals(subscription.getCurrentPlan().getName(), "assault-rifle-annual");
+
+            testListener.pushExpectedEvent(NextEvent.CANCEL);
+            Duration oneYear = getDurationYear(1);
+            clock.setDeltaFromReality(oneYear, 0);
+            assertTrue(testListener.isCompleted(5000));
+
+            assertDateWithin(subscription.getStartDate(), beforeMigration, afterMigration);
+            assertNotNull(subscription.getEndDate());
+            assertTrue(subscription.getEndDate().isAfterNow());
+            assertEquals(subscription.getCurrentPriceList(), PriceListSet.DEFAULT_PRICELIST_NAME);
+            assertEquals(subscription.getCurrentPhase(), null);
+            assertEquals(subscription.getState(), SubscriptionState.CANCELLED);
+            assertNull(subscription.getCurrentPlan());
+
+        } catch (EntitlementMigrationApiException e) {
+            Assert.fail("", e);
+        }
+    }
+
+    public void testSingleBasePlanWithPendingPhaseReal() {
+
+        try {
+            DateTime beforeMigration = clock.getUTCNow();
+            EntitlementAccountMigration toBeMigrated = createAccountFuturePendingPhase();
+            DateTime afterMigration = clock.getUTCNow();
+
+            testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
+            migrationApi.migrate(toBeMigrated);
+            assertTrue(testListener.isCompleted(5000));
+
+            List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
+            assertEquals(bundles.size(), 1);
+            SubscriptionBundle bundle = bundles.get(0);
+
+            List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(bundle.getId());
+            assertEquals(subscriptions.size(), 1);
+            Subscription subscription = subscriptions.get(0);
+            assertDateWithin(subscription.getStartDate(), beforeMigration, afterMigration);
+            assertEquals(subscription.getEndDate(), null);
+            assertEquals(subscription.getCurrentPriceList(), PriceListSet.DEFAULT_PRICELIST_NAME);
+            assertEquals(subscription.getCurrentPhase().getPhaseType(), PhaseType.TRIAL);
+            assertEquals(subscription.getState(), SubscriptionState.ACTIVE);
+            assertEquals(subscription.getCurrentPlan().getName(), "assault-rifle-monthly");
+
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            Duration thirtyDays = getDurationDay(30);
+            clock.setDeltaFromReality(thirtyDays, 0);
+            assertTrue(testListener.isCompleted(5000));
+
+            assertDateWithin(subscription.getStartDate(), beforeMigration, afterMigration);
+            assertEquals(subscription.getEndDate(), null);
+            assertEquals(subscription.getCurrentPriceList(), PriceListSet.DEFAULT_PRICELIST_NAME);
+            assertEquals(subscription.getCurrentPhase().getPhaseType(), PhaseType.EVERGREEN);
+            assertEquals(subscription.getState(), SubscriptionState.ACTIVE);
+            assertEquals(subscription.getCurrentPlan().getName(), "assault-rifle-monthly");
+            assertEquals(subscription.getCurrentPhase().getName(), "assault-rifle-monthly-evergreen");
+
+        } catch (EntitlementMigrationApiException e) {
+            Assert.fail("", e);
+        }
+    }
+
+
+    public void testSingleBasePlanWithPendingChangeReal() {
+
+        try {
+            DateTime beforeMigration = clock.getUTCNow();
+            EntitlementAccountMigration toBeMigrated = createAccountFuturePendingChange();
+            DateTime afterMigration = clock.getUTCNow();
+
+            testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
+            migrationApi.migrate(toBeMigrated);
+            assertTrue(testListener.isCompleted(5000));
+
+            List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
+            assertEquals(bundles.size(), 1);
+            SubscriptionBundle bundle = bundles.get(0);
+
+            List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(bundle.getId());
+            assertEquals(subscriptions.size(), 1);
+            Subscription subscription = subscriptions.get(0);
+            assertDateWithin(subscription.getStartDate(), beforeMigration, afterMigration);
+            assertEquals(subscription.getEndDate(), null);
+            assertEquals(subscription.getCurrentPriceList(), PriceListSet.DEFAULT_PRICELIST_NAME);
+            assertEquals(subscription.getCurrentPhase().getPhaseType(), PhaseType.EVERGREEN);
+            assertEquals(subscription.getState(), SubscriptionState.ACTIVE);
+            assertEquals(subscription.getCurrentPlan().getName(), "assault-rifle-monthly");
+
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
+            Duration oneMonth = getDurationMonth(1);
+            clock.setDeltaFromReality(oneMonth, 0);
+            assertTrue(testListener.isCompleted(5000));
+
+            assertDateWithin(subscription.getStartDate(), beforeMigration, afterMigration);
+            assertEquals(subscription.getEndDate(), null);
+            assertEquals(subscription.getCurrentPriceList(), PriceListSet.DEFAULT_PRICELIST_NAME);
+
+            assertEquals(subscription.getCurrentPhase().getPhaseType(), PhaseType.EVERGREEN);
+            assertEquals(subscription.getState(), SubscriptionState.ACTIVE);
+            assertEquals(subscription.getCurrentPlan().getName(), "shotgun-annual");
+
+        } catch (EntitlementMigrationApiException e) {
+            Assert.fail("", e);
+        }
+    }
+
+
+    private EntitlementAccountMigration createAccountWithSingleBasePlan(final List<EntitlementSubscriptionMigrationCase> cases) {
+
+        return new EntitlementAccountMigration() {
+
+            private final UUID accountId = UUID.randomUUID();
+
+            @Override
+            public EntitlementBundleMigration[] getBundles() {
+                List<EntitlementBundleMigration> bundles = new ArrayList<EntitlementBundleMigration>();
+                EntitlementBundleMigration bundle0 = new EntitlementBundleMigration() {
+
+                    @Override
+                    public EntitlementSubscriptionMigration[] getSubscriptions() {
+                        EntitlementSubscriptionMigration subscription = new EntitlementSubscriptionMigration() {
+                            @Override
+                            public EntitlementSubscriptionMigrationCase[] getSubscriptionCases() {
+                                return cases.toArray(new EntitlementSubscriptionMigrationCase[cases.size()]);
+                            }
+                            @Override
+                            public ProductCategory getCategory() {
+                                return ProductCategory.BASE;
+                            }
+                        };
+                        EntitlementSubscriptionMigration[] result = new EntitlementSubscriptionMigration[1];
+                        result[0] = subscription;
+                        return result;
+                    }
+                    @Override
+                    public String getBundleKey() {
+                        return "12345";
+                    }
+                };
+                bundles.add(bundle0);
+                return bundles.toArray(new EntitlementBundleMigration[bundles.size()]);
+            }
+
+            @Override
+            public UUID getAccountKey() {
+                return accountId;
+            }
+        };
+    }
+
+    private EntitlementAccountMigration createAccountWithRegularBasePlan() {
+        List<EntitlementSubscriptionMigrationCase> cases = new LinkedList<EntitlementSubscriptionMigrationCase>();
+        cases.add(new EntitlementSubscriptionMigrationCase() {
+            @Override
+            public PlanPhaseSpecifier getPlanPhaseSpecifer() {
+                return new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+            }
+            @Override
+            public DateTime getEffectiveDate() {
+                return clock.getUTCNow().minusMonths(3);
+            }
+            @Override
+            public DateTime getCancelledDate() {
+                return null;
+            }
+        });
+        return createAccountWithSingleBasePlan(cases);
+    }
+
+    private EntitlementAccountMigration createAccountWithRegularBasePlanFutreCancelled() {
+        List<EntitlementSubscriptionMigrationCase> cases = new LinkedList<EntitlementSubscriptionMigrationCase>();
+        final DateTime effectiveDate = clock.getUTCNow().minusMonths(3);
+        cases.add(new EntitlementSubscriptionMigrationCase() {
+            @Override
+            public PlanPhaseSpecifier getPlanPhaseSpecifer() {
+                return new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+            }
+            @Override
+            public DateTime getEffectiveDate() {
+                return effectiveDate;
+            }
+            @Override
+            public DateTime getCancelledDate() {
+                return effectiveDate.plusYears(1);
+            }
+        });
+        return createAccountWithSingleBasePlan(cases);
+    }
+
+
+    private EntitlementAccountMigration createAccountFuturePendingPhase() {
+        List<EntitlementSubscriptionMigrationCase> cases = new LinkedList<EntitlementSubscriptionMigrationCase>();
+        final DateTime trialDate = clock.getUTCNow().minusDays(10);
+        cases.add(new EntitlementSubscriptionMigrationCase() {
+            @Override
+            public PlanPhaseSpecifier getPlanPhaseSpecifer() {
+                return new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.TRIAL);
+            }
+            @Override
+            public DateTime getEffectiveDate() {
+                return trialDate;
+            }
+            @Override
+            public DateTime getCancelledDate() {
+                return trialDate.plusDays(30);
+            }
+        });
+        cases.add(new EntitlementSubscriptionMigrationCase() {
+            @Override
+            public PlanPhaseSpecifier getPlanPhaseSpecifer() {
+                return new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+            }
+            @Override
+            public DateTime getEffectiveDate() {
+                return trialDate.plusDays(31);
+            }
+            @Override
+            public DateTime getCancelledDate() {
+                return null;
+            }
+        });
+        return createAccountWithSingleBasePlan(cases);
+    }
+
+    private EntitlementAccountMigration createAccountFuturePendingChange() {
+        List<EntitlementSubscriptionMigrationCase> cases = new LinkedList<EntitlementSubscriptionMigrationCase>();
+        final DateTime effectiveDate = clock.getUTCNow().minusDays(10);
+        cases.add(new EntitlementSubscriptionMigrationCase() {
+            @Override
+            public PlanPhaseSpecifier getPlanPhaseSpecifer() {
+                return new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+            }
+            @Override
+            public DateTime getEffectiveDate() {
+                return effectiveDate;
+            }
+            @Override
+            public DateTime getCancelledDate() {
+                return effectiveDate.plusMonths(1);
+            }
+        });
+        cases.add(new EntitlementSubscriptionMigrationCase() {
+            @Override
+            public PlanPhaseSpecifier getPlanPhaseSpecifer() {
+                return new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+            }
+            @Override
+            public DateTime getEffectiveDate() {
+                return effectiveDate.plusMonths(1).plusDays(1);
+            }
+            @Override
+            public DateTime getCancelledDate() {
+                return null;
+            }
+        });
+        return createAccountWithSingleBasePlan(cases);
+    }
+
+}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigrationSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigrationSql.java
new file mode 100644
index 0000000..2329f3d
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigrationSql.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010-2011 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.entitlement.api.migration;
+
+import org.testng.annotations.Test;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Stage;
+import com.ning.billing.entitlement.glue.MockEngineModuleSql;
+
+public class TestMigrationSql extends TestMigration {
+
+    @Override
+    protected Injector getInjector() {
+        return Guice.createInjector(Stage.DEVELOPMENT, new MockEngineModuleSql());
+    }
+
+    @Test(enabled=true, groups="sql")
+    public void testSingleBasePlan() {
+        invokeRealMethod(this);
+    }
+
+    @Test(enabled=true, groups="sql")
+    public void testSingleBasePlanFutureCancelled() {
+        invokeRealMethod(this);
+    }
+
+    @Test(enabled=true, groups="sql")
+    public void testSingleBasePlanWithPendingPhase() {
+        invokeRealMethod(this);
+    }
+
+    @Test(enabled=true, groups="sql")
+    public void testSingleBasePlanWithPendingChange() {
+        invokeRealMethod(this);
+    }
+}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
new file mode 100644
index 0000000..21bbf77
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2010-2011 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.entitlement.api;
+
+import com.google.inject.Injector;
+import com.ning.billing.account.api.IAccount;
+import com.ning.billing.catalog.DefaultCatalogService;
+import com.ning.billing.catalog.api.*;
+import com.ning.billing.config.EntitlementConfig;
+import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
+import com.ning.billing.entitlement.api.EntitlementService;
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
+import com.ning.billing.entitlement.api.user.EntitlementUserApi;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition;
+import com.ning.billing.entitlement.engine.core.Engine;
+import com.ning.billing.entitlement.engine.dao.EntitlementDao;
+import com.ning.billing.entitlement.engine.dao.MockEntitlementDao;
+import com.ning.billing.entitlement.events.EntitlementEvent;
+import com.ning.billing.entitlement.events.phase.PhaseEvent;
+import com.ning.billing.entitlement.events.user.ApiEventType;
+import com.ning.billing.entitlement.events.user.ApiEvent;
+import com.ning.billing.lifecycle.KillbillService.ServiceException;
+import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.eventbus.DefaultEventBusService;
+import com.ning.billing.util.eventbus.EventBusService;
+import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.List;
+import java.util.UUID;
+
+import static org.testng.Assert.*;
+
+
+public abstract class TestApiBase {
+
+    protected static final Logger log = LoggerFactory.getLogger(TestApiBase.class);
+
+    protected static final long DAY_IN_MS = (24 * 3600 * 1000);
+
+    protected EntitlementService entitlementService;
+    protected EntitlementUserApi entitlementApi;
+    protected EntitlementBillingApi billingApi;
+
+    protected EntitlementMigrationApi migrationApi;
+
+    protected CatalogService catalogService;
+    protected EntitlementConfig config;
+    protected EntitlementDao dao;
+    protected ClockMock clock;
+    protected EventBusService busService;
+
+    protected IAccount account;
+    protected Catalog catalog;
+    protected ApiTestListener testListener;
+    protected SubscriptionBundle bundle;
+
+    public static void loadSystemPropertiesFromClasspath( final String resource )
+    {
+        final URL url = TestApiBase.class.getResource(resource);
+        assertNotNull(url);
+
+        try {
+            System.getProperties().load( url.openStream() );
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @AfterClass(groups={"setup"})
+    public void tearDown() {
+        try {
+            busService.getEventBus().register(testListener);
+            ((DefaultEventBusService) busService).stopBus();
+        } catch (Exception e) {
+            log.warn("Failed to tearDown test properly ", e);
+        }
+
+    }
+
+    @BeforeClass(groups={"setup"})
+    public void setup() {
+
+        loadSystemPropertiesFromClasspath("/entitlement.properties");
+        final Injector g = getInjector();
+
+        entitlementService = g.getInstance(EntitlementService.class);
+        catalogService = g.getInstance(CatalogService.class);
+        busService = g.getInstance(EventBusService.class);
+        config = g.getInstance(EntitlementConfig.class);
+        dao = g.getInstance(EntitlementDao.class);
+        clock = (ClockMock) g.getInstance(Clock.class);
+        try {
+
+            ((DefaultCatalogService) catalogService).loadCatalog();
+            ((DefaultEventBusService) busService).startBus();
+            ((Engine) entitlementService).initialize();
+            init();
+        } catch (EntitlementUserApiException e) {
+            Assert.fail(e.getMessage());
+        } catch (ServiceException e) {
+            Assert.fail(e.getMessage());
+        }
+    }
+
+    protected abstract Injector getInjector();
+
+    private void init() throws EntitlementUserApiException {
+        account = getAccount();
+        assertNotNull(account);
+
+        catalog = catalogService.getCatalog();
+        assertNotNull(catalog);
+
+
+        testListener = new ApiTestListener(busService.getEventBus());
+        entitlementApi = entitlementService.getUserApi();
+        billingApi = entitlementService.getBillingApi();
+        migrationApi = entitlementService.getMigrationApi();
+
+    }
+
+    @BeforeMethod(groups={"setup"})
+    public void setupTest() {
+
+        log.warn("\n");
+        log.warn("RESET TEST FRAMEWORK\n\n");
+
+        testListener.reset();
+
+        clock.resetDeltaFromReality();
+        ((MockEntitlementDao) dao).reset();
+        try {
+            busService.getEventBus().register(testListener);
+            bundle = entitlementApi.createBundleForAccount(account, "myDefaultBundle");
+        } catch (Exception e) {
+            Assert.fail(e.getMessage());
+        }
+        assertNotNull(bundle);
+
+        ((Engine)entitlementService).start();
+    }
+
+    @AfterMethod(groups={"setup"})
+    public void cleanupTest() {
+
+
+        ((Engine)entitlementService).stop();
+        log.warn("DONE WITH TEST\n");
+    }
+
+    // Glue magic to invoke the real test
+    protected void invokeRealMethod(Object invoker)  {
+
+        try {
+            String methodName = Thread.currentThread().getStackTrace()[2].getMethodName();
+            String realMethodName= methodName + "Real";
+
+            Class<?> thisClass = invoker.getClass();
+            Class<?> superClass = thisClass.getSuperclass();
+            Method [] methods = superClass.getDeclaredMethods();
+            for (Method cur : methods) {
+                if (cur.getName().equals(realMethodName)) {
+                    cur.invoke(invoker);
+                    return;
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            Assert.fail(e.getMessage());
+        }
+    }
+
+    protected SubscriptionData createSubscription(final String productName, final BillingPeriod term, final String planSet) throws EntitlementUserApiException {
+        testListener.pushExpectedEvent(NextEvent.CREATE);
+        SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
+                new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSet, null),
+                clock.getUTCNow());
+        assertNotNull(subscription);
+        assertTrue(testListener.isCompleted(5000));
+        return subscription;
+    }
+
+    protected void checkNextPhaseChange(SubscriptionData subscription, int expPendingEvents, DateTime expPhaseChange) {
+
+        List<EntitlementEvent> events = dao.getPendingEventsForSubscription(subscription.getId());
+        assertNotNull(events);
+        printEvents(events);
+        assertEquals(events.size(), expPendingEvents);
+        if (events.size() > 0 && expPhaseChange != null) {
+            boolean foundPhase = false;
+            boolean foundChange = false;
+
+            for (EntitlementEvent cur : events) {
+                if (cur instanceof PhaseEvent) {
+                    assertEquals(foundPhase, false);
+                    foundPhase = true;
+                    assertEquals(cur.getEffectiveDate(), expPhaseChange);
+                } else if (cur instanceof ApiEvent) {
+                    ApiEvent uEvent = (ApiEvent) cur;
+                    assertEquals(ApiEventType.CHANGE, uEvent.getEventType());
+                    assertEquals(foundChange, false);
+                    foundChange = true;
+                } else {
+                    assertFalse(true);
+                }
+            }
+        }
+    }
+
+
+    protected void assertDateWithin(DateTime in, DateTime lower, DateTime upper) {
+        assertTrue(in.isEqual(lower) || in.isAfter(lower));
+        assertTrue(in.isEqual(upper) || in.isBefore(upper));
+    }
+
+    protected Duration getDurationDay(final int days) {
+        Duration result = new Duration() {
+            @Override
+            public TimeUnit getUnit() {
+                return TimeUnit.DAYS;
+            }
+            @Override
+            public int getNumber() {
+                return days;
+            }
+        };
+        return result;
+    }
+
+    protected Duration getDurationMonth(final int months) {
+        Duration result = new Duration() {
+            @Override
+            public TimeUnit getUnit() {
+                return TimeUnit.MONTHS;
+            }
+            @Override
+            public int getNumber() {
+                return months;
+            }
+        };
+        return result;
+    }
+
+
+    protected Duration getDurationYear(final int years) {
+        Duration result = new Duration() {
+            @Override
+            public TimeUnit getUnit() {
+                return TimeUnit.YEARS;
+            }
+            @Override
+            public int getNumber() {
+                return years;
+            }
+        };
+        return result;
+    }
+
+    protected IAccount getAccount() {
+        IAccount account = new IAccount() {
+            @Override
+            public String getName() {
+                return "accountName";
+            }
+            @Override
+            public String getEmail() {
+                return "accountName@yahoo.com";
+            }
+            @Override
+            public String getPhone() {
+                return "4152876341";
+            }
+            @Override
+            public String getKey() {
+                return "k123456";
+            }
+            @Override
+            public int getBillCycleDay() {
+                return 1;
+            }
+            @Override
+            public Currency getCurrency() {
+                return Currency.USD;
+            }
+
+            @Override
+            public UUID getId() {
+                return UUID.randomUUID();
+            }
+
+            @Override
+            public void load() {}
+
+            @Override
+            public void save() {}
+
+            @Override
+            public String getFieldValue(String fieldName) {
+                return null;
+            }
+
+            @Override
+            public void setFieldValue(String fieldName, String fieldValue) {}
+        };
+        return account;
+    }
+
+    protected PlanPhaseSpecifier getProductSpecifier(final String productName, final String priceList, final BillingPeriod term, final PhaseType phaseType) {
+        return new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, priceList, phaseType);
+    }
+
+    protected void printEvents(List<EntitlementEvent> events) {
+        for (EntitlementEvent cur : events) {
+            log.debug("Inspect event " + cur);
+        }
+    }
+
+    protected void printSubscriptionTransitions(List<SubscriptionTransition> transitions) {
+        for (SubscriptionTransition cur : transitions) {
+            log.debug("Transition " + cur);
+        }
+    }
+
+}