killbill-memoizeit

Initially started working on ADDON (tests) and realized there

12/29/2011 8:26:24 PM

Details

diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java
index 2515bf5..a624345 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java
@@ -21,6 +21,8 @@ import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.*;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition.SubscriptionTransitionType;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
 import com.ning.billing.util.clock.DefaultClock;
 import org.joda.time.DateTime;
@@ -106,23 +108,46 @@ public class PlanAligner  {
         return getTimedPhaseOnChange(subscription, plan, priceList, effectiveDate, WhichPhase.NEXT);
     }
 
+
     /**
-     * Returns next future phase for that Plan based on effectiveDate
-     *
-     * @param plan
-     * @param initialPhase the initial phase that subscription started on that Plan
-     * @param effectiveDate the date used to consider what is future
-     * @param initialStartPhase the date for when we started on that Plan/initialPhase
-     * @return
-     * @throws EntitlementError
+     * Returns next Phase for that Subscription at a point in time
+     * <p>
+     * @param subscription the subscription for which we need to compute the next Phase event
+     * @param effectiveDate the date at which we look to compute that event. effective needs to be after last Plan change or initial Plan
+     * @return The PhaseEvent at the correct point in time
      */
-    public TimedPhase getNextTimedPhase(Plan plan, PhaseType initialPhase, DateTime effectiveDate, DateTime initialStartPhase)
-        throws EntitlementError {
+    public TimedPhase getNextTimedPhase(SubscriptionData subscription, DateTime effectiveDate) {
         try {
-            List<TimedPhase> timedPhases = getPhaseAlignments(plan, initialPhase, initialStartPhase);
-            return getTimedPhase(timedPhases, effectiveDate, WhichPhase.NEXT);
-        } catch (EntitlementUserApiException e) {
-            throw new EntitlementError(String.format("Could not compute next phase change for plan %s with initialPhase %s", plan.getName(), initialPhase));
+
+            SubscriptionTransition lastPlanTransition = subscription.getInitialTransitionForCurrentPlan();
+            if (effectiveDate.isBefore(lastPlanTransition.getEffectiveTransitionTime())) {
+                throw new EntitlementError(String.format("Cannot specify an effectiveDate prior to last Plan Change, subscription = %s, effectiveDate = %s",
+                        subscription.getId(), effectiveDate));
+            }
+
+            switch(lastPlanTransition.getTransitionType()) {
+            // If we never had any Plan change, this a simple Phase alignement based on initial Phase and startPhaseDate
+            case CREATE:
+                DateTime initialStartPhase = subscription.getStartDate();
+                PhaseType initialPhase = lastPlanTransition.getNextPhase().getPhaseType();
+                List<TimedPhase> timedPhases = getPhaseAlignments(subscription.getCurrentPlan(), initialPhase, initialStartPhase);
+                return getTimedPhase(timedPhases, effectiveDate, WhichPhase.NEXT);
+            // If we went through Plan changes, look at previous Plan Transition and borrow changePlan logics to recompute everything
+            case CHANGE:
+                return getTimedPhaseOnChange(subscription.getStartDate(),
+                        subscription.getBundleStartDate(),
+                        lastPlanTransition.getPreviousPhase(),
+                        lastPlanTransition.getPreviousPlan(),
+                        lastPlanTransition.getPreviousPriceList(),
+                        lastPlanTransition.getNextPlan(),
+                        lastPlanTransition.getNextPriceList(),
+                        effectiveDate, WhichPhase.NEXT);
+            default:
+                throw new EntitlementError(String.format("Unexpectd initial transition %s for current plan %s on subscription %s",
+                        lastPlanTransition.getTransitionType(), subscription.getCurrentPlan(), subscription.getId()));
+            }
+        } catch (Exception /*EntitlementUserApiException, CatalogApiException */ e) {
+            throw new EntitlementError(String.format("Could not compute next phase change for subscription %s", subscription.getId()), e);
         }
     }
 
@@ -154,15 +179,30 @@ public class PlanAligner  {
         return getPhaseAlignments(plan, initialPhase, planStartDate);
     }
 
+
     private TimedPhase getTimedPhaseOnChange(SubscriptionData subscription,
-            Plan plan, String priceList, DateTime effectiveDate, WhichPhase which)
+            Plan nextPlan, String nextPriceList, DateTime effectiveDate, WhichPhase which)
         throws CatalogApiException, EntitlementUserApiException {
+        return getTimedPhaseOnChange(subscription.getStartDate(),
+                subscription.getBundleStartDate(),
+                subscription.getCurrentPhase(),
+                subscription.getCurrentPlan(),
+                subscription.getCurrentPriceList(),
+                nextPlan,
+                nextPriceList,
+                effectiveDate,
+                which);
+    }
 
-        Catalog catalog = catalogService.getCatalog();
+    private TimedPhase getTimedPhaseOnChange(DateTime subscriptionStartDate,
+            DateTime bundleStartDate,
+            PlanPhase currentPhase,
+            Plan currentPlan,
+            String currentPriceList,
+            Plan nextPlan, String priceList, DateTime effectiveDate, WhichPhase which)
+        throws CatalogApiException, EntitlementUserApiException {
 
-        PlanPhase currentPhase = subscription.getCurrentPhase();
-        Plan currentPlan = subscription.getCurrentPlan();
-        String currentPriceList = subscription.getCurrentPriceList();
+        Catalog catalog = catalogService.getCatalog();
         ProductCategory currentCategory = currentPlan.getProduct().getCategory();
         if (currentCategory != ProductCategory.BASE) {
             throw new EntitlementError(String.format("Only implemented changePlan for BasePlan"));
@@ -174,9 +214,9 @@ public class PlanAligner  {
                 currentPriceList,
                 currentPhase.getPhaseType());
 
-        PlanSpecifier toPlanSpecifier = new PlanSpecifier(plan.getProduct().getName(),
-                plan.getProduct().getCategory(),
-                plan.getBillingPeriod(),
+        PlanSpecifier toPlanSpecifier = new PlanSpecifier(nextPlan.getProduct().getName(),
+                nextPlan.getProduct().getCategory(),
+                nextPlan.getBillingPeriod(),
                 priceList);
 
         DateTime planStartDate = null;
@@ -185,10 +225,10 @@ public class PlanAligner  {
         alignment = catalog.planChangeAlignment(fromPlanPhaseSpecifier, toPlanSpecifier);
         switch(alignment) {
         case START_OF_SUBSCRIPTION:
-            planStartDate = subscription.getStartDate();
+            planStartDate = subscriptionStartDate;
             break;
         case START_OF_BUNDLE:
-            planStartDate = subscription.getBundleStartDate();
+            planStartDate = bundleStartDate;
             break;
         case CHANGE_OF_PLAN:
             throw new EntitlementError(String.format("Not implemented yet %s", alignment));
@@ -197,7 +237,7 @@ public class PlanAligner  {
         default:
             throw new EntitlementError(String.format("Unknwon PlanAlignmentChange %s", alignment));
         }
-        List<TimedPhase> timedPhases = getPhaseAlignments(plan, null, planStartDate);
+        List<TimedPhase> timedPhases = getPhaseAlignments(nextPlan, null, planStartDate);
         return getTimedPhase(timedPhases, effectiveDate, which);
     }
 
@@ -212,7 +252,7 @@ public class PlanAligner  {
         DateTime curPhaseStart = (initialPhase == null) ? initialPhaseStartDate : null;
         DateTime nextPhaseStart = null;
         for (PlanPhase cur : plan.getAllPhases()) {
-            // For create we can specifcy the phase so skip any phase until we reach initialPhase
+            // For create we can specify the phase so skip any phase until we reach initialPhase
             if (curPhaseStart == null) {
                 if (initialPhase != cur.getPhaseType()) {
                     continue;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java
index 7ad6e03..548675b 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java
@@ -148,8 +148,7 @@ public class SubscriptionApiService {
         List<EntitlementEvent> uncancelEvents = new ArrayList<EntitlementEvent>();
         uncancelEvents.add(uncancelEvent);
 
-        DateTime planStartDate = subscription.getCurrentPlanStart();
-        TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription.getCurrentPlan(), subscription.getInitialPhaseOnCurrentPlan().getPhaseType(), now, planStartDate);
+        TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, now);
         PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
                 PhaseEventData.getNextPhaseEvent(nextTimedPhase.getPhase().getName(), subscription, now, nextTimedPhase.getStartPhase()) :
                     null;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
index 3e8ede5..40132b8 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
@@ -231,6 +231,7 @@ public class SubscriptionData implements Subscription {
         return paidThroughDate;
     }
 
+    /*
     public DateTime getCurrentPlanStart() {
         return getInitialTransitionForCurrentPlan().getEffectiveTransitionTime();
     }
@@ -238,8 +239,8 @@ public class SubscriptionData implements Subscription {
     public PlanPhase getInitialPhaseOnCurrentPlan() {
         return getInitialTransitionForCurrentPlan().getNextPhase();
     }
-
-    private SubscriptionTransitionData getInitialTransitionForCurrentPlan() {
+*/
+    public SubscriptionTransitionData getInitialTransitionForCurrentPlan() {
         if (transitions == null) {
             throw new EntitlementError(String.format("No transitions for subscription %s", getId()));
         }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
index 3eb5dd3..e6f61e7 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
@@ -187,7 +187,7 @@ public class Engine implements EventListener, EntitlementService {
     private void insertNextPhaseEvent(SubscriptionData subscription) {
         try {
             DateTime now = clock.getUTCNow();
-            TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription.getCurrentPlan(), subscription.getInitialPhaseOnCurrentPlan().getPhaseType(), now, subscription.getCurrentPlanStart());
+            TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, now);
             PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
                     PhaseEventData.getNextPhaseEvent(nextTimedPhase.getPhase().getName(), subscription, now, nextTimedPhase.getStartPhase()) :
                         null;
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
index 52e25f7..e4cc948 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
@@ -182,8 +182,6 @@ public abstract class TestApiBase {
 
     @AfterMethod(groups={"setup"})
     public void cleanupTest() {
-
-
         ((Engine)entitlementService).stop();
         log.warn("DONE WITH TEST\n");
     }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
new file mode 100644
index 0000000..8cbdc93
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
@@ -0,0 +1,127 @@
+/*
+ * 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.user;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+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.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+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.glue.MockEngineModuleSql;
+
+public class TestUserApiAddOn extends TestApiBase {
+
+    @Override
+    public Injector getInjector() {
+        return Guice.createInjector(Stage.DEVELOPMENT, new MockEngineModuleSql());
+    }
+
+    @Test(enabled=false, groups={"sql"})
+    public void testAddonCreateWithBundleAlign() {
+        try {
+
+            String baseProduct = "Shotgun";
+            BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+            String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+            // CREATE BP
+            SubscriptionData baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+            String aoProduct = "Telescopic-Scope";
+            BillingPeriod aoTerm = BillingPeriod.MONTHLY;
+            String aoPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+            // MOVE CLOCK 14 DAYS LATER
+            Duration someTimeLater = getDurationDay(13);
+            clock.setDeltaFromReality(someTimeLater, DAY_IN_MS);
+
+            // CREATE ADDON (ALIGN BUNDLE)
+            DateTime beforeAOCreation = clock.getUTCNow();
+            SubscriptionData aoSubscription = createSubscription(aoProduct, aoTerm, aoPriceList);
+            DateTime afterAOCreation = clock.getUTCNow();
+
+            // CHECK EVERYTHING
+            Plan aoCurrentPlan = aoSubscription.getCurrentPlan();
+            assertNotNull(aoCurrentPlan);
+            assertEquals(aoCurrentPlan.getProduct().getName(),aoProduct);
+            assertEquals(aoCurrentPlan.getProduct().getCategory(), ProductCategory.ADD_ON);
+            assertEquals(aoCurrentPlan.getBillingPeriod(), aoTerm);
+
+            PlanPhase aoCurrentPhase = aoSubscription.getCurrentPhase();
+            assertNotNull(aoCurrentPhase);
+            assertEquals(aoCurrentPhase.getPhaseType(), PhaseType.DISCOUNT);
+
+           assertDateWithin(aoSubscription.getStartDate(), beforeAOCreation, afterAOCreation);
+           assertEquals(aoSubscription.getBundleStartDate(), baseSubscription.getBundleStartDate());
+
+           // CHECK next AO PHASE EVENT IS INDEED A MONTH AFTER BP STARTED => BUNDLE ALIGNMENT
+           SubscriptionTransition aoPendingTranstion = aoSubscription.getPendingTransition();
+           assertEquals(aoPendingTranstion.getEffectiveTransitionTime(), baseSubscription.getStartDate().plusMonths(1));
+
+
+
+           // ADD TWO PHASE EVENTS (BP + AO)
+           testListener.reset();
+           testListener.pushExpectedEvent(NextEvent.PHASE);
+           testListener.pushExpectedEvent(NextEvent.PHASE);
+
+           // MOVE THROUGH TIME TO GO INTO EVERGREEN
+           someTimeLater = getDurationDay(20);
+           clock.addDeltaFromReality(someTimeLater);
+           assertTrue(testListener.isCompleted(5000));
+
+
+           try {
+               Thread.currentThread().sleep(1000 * 1000);
+           } catch (InterruptedException e) {
+
+
+           }
+
+           // CHECK EVERYTHING AGAIN
+           aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+
+           aoCurrentPlan = aoSubscription.getCurrentPlan();
+           assertNotNull(aoCurrentPlan);
+           assertEquals(aoCurrentPlan.getProduct().getName(),aoProduct);
+           assertEquals(aoCurrentPlan.getProduct().getCategory(), ProductCategory.ADD_ON);
+           assertEquals(aoCurrentPlan.getBillingPeriod(), aoTerm);
+
+           aoCurrentPhase = aoSubscription.getCurrentPhase();
+           assertNotNull(aoCurrentPhase);
+           assertEquals(aoCurrentPhase.getPhaseType(), PhaseType.EVERGREEN);
+
+        } catch (EntitlementUserApiException e) {
+            Assert.fail(e.getMessage());
+        }
+    }
+}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
index dc6567f..ab32386 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
@@ -418,4 +418,54 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
         }
     }
 
+
+    protected void testCorrectPhaseAlignmentOnChange() {
+        try {
+
+            SubscriptionData subscription = createSubscription("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+            PlanPhase trialPhase = subscription.getCurrentPhase();
+            assertEquals(trialPhase.getPhaseType(), PhaseType.TRIAL);
+
+            // MOVE 2 DAYS AHEAD
+            clock.setDeltaFromReality(getDurationDay(1), DAY_IN_MS);
+
+            // CHANGE IMMEDIATE TO A 3 PHASES PLAN
+            testListener.reset();
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
+            subscription.changePlan("Assault-Rifle", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow());
+            assertTrue(testListener.isCompleted(3000));
+            testListener.reset();
+
+            // CHECK EVERYTHING LOOKS CORRECT
+            Plan currentPlan = subscription.getCurrentPlan();
+            assertNotNull(currentPlan);
+            assertEquals(currentPlan.getProduct().getName(), "Assault-Rifle");
+            assertEquals(currentPlan.getProduct().getCategory(), ProductCategory.BASE);
+            assertEquals(currentPlan.getBillingPeriod(), BillingPeriod.ANNUAL);
+
+            trialPhase = subscription.getCurrentPhase();
+            assertEquals(trialPhase.getPhaseType(), PhaseType.TRIAL);
+
+            // MOVE AFTER TRIAL PERIOD -> DISCOUNT
+            testListener.pushExpectedEvent(NextEvent.PHASE);
+            clock.addDeltaFromReality(trialPhase.getDuration());
+            assertTrue(testListener.isCompleted(3000));
+
+            trialPhase = subscription.getCurrentPhase();
+            assertEquals(trialPhase.getPhaseType(), PhaseType.DISCOUNT);
+
+            subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
+
+            DateTime expectedNextPhaseDate =  subscription.getStartDate().plusDays(30).plusMonths(6);
+            SubscriptionTransition nextPhase = subscription.getPendingTransition();
+            DateTime nextPhaseEffectiveDate = nextPhase.getEffectiveTransitionTime();
+
+            assertEquals(nextPhaseEffectiveDate, expectedNextPhaseDate);
+
+
+        } catch (EntitlementUserApiException e) {
+            Assert.fail(e.getMessage());
+        }
+    }
+
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
index aecaaac..c303c62 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
@@ -66,4 +66,10 @@ public class TestUserApiChangePlanMemory extends TestUserApiChangePlan {
     public void testChangePlanChangePlanAlignEOTWithChargeThroughDate() {
         super.testChangePlanChangePlanAlignEOTWithChargeThroughDate();
     }
+
+    @Override
+    @Test(enabled=true, groups={"fast"})
+    public void testCorrectPhaseAlignmentOnChange() {
+        super.testCorrectPhaseAlignmentOnChange();
+    }
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java
index f5ad803..81cbbc8 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java
@@ -54,6 +54,12 @@ public class TestUserApiChangePlanSql extends TestUserApiChangePlan {
 
     @Override
     @Test(enabled=true, groups={"sql"})
+    public void testCorrectPhaseAlignmentOnChange() {
+        super.testCorrectPhaseAlignmentOnChange();
+    }
+
+    @Override
+    @Test(enabled=true, groups={"sql"})
     public void testChangePlanBundleAlignEOTWithNoChargeThroughDate() {
         super.testChangePlanBundleAlignEOTWithNoChargeThroughDate();
     }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java
index 2533855..fef6e3c 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java
@@ -46,7 +46,7 @@ public class TestUserApiError extends TestApiBase {
     }
 
 
-    @Test(enabled=true)
+    @Test(enabled=true, groups={"fast"})
     public void testCreateSubscriptionBadCatalog() {
         // WRONG PRODUTCS
         tCreateSubscriptionInternal(bundle.getId(), null, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.CAT_NO_SUCH_PRODUCT);
@@ -63,17 +63,17 @@ public class TestUserApiError extends TestApiBase {
 
     }
 
-    @Test(enabled=true)
+    @Test(enabled=true, groups={"fast"})
     public void testCreateSubscriptionNoBundle() {
         tCreateSubscriptionInternal(null, "Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.ENT_CREATE_NO_BUNDLE);
     }
 
-    @Test(enabled=true)
+    @Test(enabled=true, groups={"fast"})
     public void testCreateSubscriptionNoBP() {
         tCreateSubscriptionInternal(bundle.getId(), "Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.ENT_CREATE_NO_BP);
     }
 
-    @Test(enabled=true)
+    @Test(enabled=true, groups={"fast"})
     public void testCreateSubscriptionBPExists() {
         try {
             createSubscription("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME);
@@ -84,7 +84,7 @@ public class TestUserApiError extends TestApiBase {
         }
     }
 
-    @Test(enabled=true)
+    @Test(enabled=true, groups={"fast"})
     public void testCreateSubscriptionAddOnNotAvailable() {
         try {
             UUID accountId = UUID.randomUUID();
@@ -97,7 +97,7 @@ public class TestUserApiError extends TestApiBase {
         }
     }
 
-    @Test(enabled=true)
+    @Test(enabled=true, groups={"fast"})
     public void testCreateSubscriptionAddOnIncluded() {
         try {
             UUID accountId = UUID.randomUUID();
@@ -129,7 +129,7 @@ public class TestUserApiError extends TestApiBase {
     }
 
 
-    @Test(enabled=true)
+    @Test(enabled=true, groups={"fast"})
     public void testChangeSubscriptionNonActive() {
         try {
             Subscription subscription = createSubscription("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME);
@@ -153,7 +153,7 @@ public class TestUserApiError extends TestApiBase {
     }
 
 
-    @Test(enabled=true)
+    @Test(enabled=true, groups={"fast"})
     public void testChangeSubscriptionFutureCancelled() {
         try {
             Subscription subscription = createSubscription("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
@@ -184,11 +184,11 @@ public class TestUserApiError extends TestApiBase {
     }
 
 
-    @Test(enabled=false)
+    @Test(enabled=false, groups={"fast"})
     public void testCancelBadState() {
     }
 
-    @Test(enabled=true)
+    @Test(enabled=true, groups={"fast"})
     public void testUncancelBadState() {
         try {
             Subscription subscription = createSubscription("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
diff --git a/entitlement/src/test/resources/testInput.xml b/entitlement/src/test/resources/testInput.xml
index 8d6b56c..531d88f 100644
--- a/entitlement/src/test/resources/testInput.xml
+++ b/entitlement/src/test/resources/testInput.xml
@@ -447,7 +447,7 @@ Use Cases to do:
 		</plan>
 		<plan name="laser-scope-monthly">
 		<product>Laser-Scope</product>
-		   <initialPhases>
+           <initialPhases>
               <phase type="DISCOUNT">
                  <duration>
                     <unit>MONTHS</unit>
@@ -475,6 +475,20 @@ Use Cases to do:
 		</plan>
 		<plan name="telescopic-scope-monthly">
 			<product>Telescopic-Scope</product>
+			<initialPhases>
+              <phase type="DISCOUNT">
+                 <duration>
+                    <unit>MONTHS</unit>
+                    <number>1</number>
+                  </duration>
+                 <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                      <price><currency>USD</currency><value>399.95</value></price>                             
+                      <price><currency>EUR</currency><value>299.95</value></price>
+                      <price><currency>GBP</currency><value>399.95</value></price>
+                      </recurringPrice>
+                </phase>
+            </initialPhases>
 			<finalPhase type="EVERGREEN">
 				<duration>
 					<unit>UNLIMITED</unit>