killbill-memoizeit

Fix timeline API ordering issues and test against real seen

11/9/2013 12:57:34 AM

Details

diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
index 4792c0e..1552503 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
@@ -96,23 +96,30 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
                 if (effectivedComp != 0) {
                     return effectivedComp;
                 }
-                final int createdDateComp = o1.getCreatedDate().compareTo(o2.getCreatedDate());
-                if (createdDateComp != 0) {
-                    return createdDateComp;
+                // For the same effectiveDate we want to first return ENTITLEMENT events
+                final int serviceNameComp = o1.getService().compareTo(o2.getService());
+                if (serviceNameComp != 0) {
+                    if (o1.getService().equals(DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME)) {
+                        return -1;
+                    } else if (o2.getService().equals(DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME)) {
+                        return 1;
+                    } else {
+                        return serviceNameComp;
+                    }
                 }
                 final int uuidComp = o1.getId().compareTo(o2.getId());
                 if (uuidComp != 0) {
                     return uuidComp;
                 }
                 // Same effectiveDate, createdDate and for the same object, we sort first by serviceName and then serviceState
-                final int serviceNameComp = o1.getService().compareTo(o2.getService());
-                if (serviceNameComp != 0) {
-                    return serviceNameComp;
-                }
                 final int serviceStateComp = o1.getStateName().compareTo(o2.getStateName());
                 if (serviceStateComp != 0) {
                     return serviceStateComp;
                 }
+                final int createdDateComp = o1.getCreatedDate().compareTo(o2.getCreatedDate());
+                if (createdDateComp != 0) {
+                    return createdDateComp;
+                }
                 // Underministic-- not sure that will ever happen.
                 return 0;
             }
@@ -122,11 +129,22 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
 
             final List<SubscriptionEvent> newEvents = new ArrayList<SubscriptionEvent>();
             int index = insertFromBlockingEvent(accountTimeZone, allEntitlementUUIDs, result, bs, bs.getEffectiveDate(), newEvents);
-            result.addAll(index, newEvents);
+            insertAfterIndex(result, newEvents, index);
         }
         return result;
     }
 
+
+    private void insertAfterIndex(final LinkedList<SubscriptionEvent> original,  List<SubscriptionEvent> newEvents,  int index) {
+        if (index == original.size() -1) {
+            for (final SubscriptionEvent cur : newEvents) {
+                original.addLast(cur);
+            }
+        } else {
+            original.addAll(index + 1, newEvents);
+        }
+    }
+
     private int insertFromBlockingEvent(final DateTimeZone accountTimeZone, final Set<UUID> allEntitlementUUIDs, final LinkedList<SubscriptionEvent> result, final BlockingState bs, final DateTime bsEffectiveDate, final List<SubscriptionEvent> newEvents) {
 
 
@@ -145,14 +163,12 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
         DefaultSubscriptionEvent curInsertion = null;
         while (it.hasNext()) {
             DefaultSubscriptionEvent cur = (DefaultSubscriptionEvent) it.next();
-            index++;
-
             final int compEffectiveDate = bsEffectiveDate.compareTo(cur.getEffectiveDateTime());
-            final boolean shouldContinue = (compEffectiveDate > 0 ||
-                                            (compEffectiveDate == 0 && bs.getCreatedDate().compareTo(cur.getCreatedDate()) >= 0));
+            final boolean shouldContinue = (compEffectiveDate >= 0);
             if (!shouldContinue) {
                 break;
             }
+            index++;
 
             final TargetState curTargetState = targetStates.get(cur.getEntitlementId());
             switch (cur.getSubscriptionEventType()) {
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
index 05d9adc..1389024 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
@@ -332,8 +332,8 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         assertEquals(events.get(1).getSubscriptionEventType(), SubscriptionEventType.START_BILLING);
         assertEquals(events.get(2).getSubscriptionEventType(), SubscriptionEventType.PHASE);
         assertEquals(events.get(3).getSubscriptionEventType(), SubscriptionEventType.PAUSE_ENTITLEMENT);
-        assertEquals(events.get(4).getSubscriptionEventType(), SubscriptionEventType.STOP_ENTITLEMENT);
-        assertEquals(events.get(5).getSubscriptionEventType(), SubscriptionEventType.STOP_BILLING);
+        assertEquals(events.get(4).getSubscriptionEventType(), SubscriptionEventType.STOP_BILLING);
+        assertEquals(events.get(5).getSubscriptionEventType(), SubscriptionEventType.STOP_ENTITLEMENT);
 
         assertEquals(events.get(0).getPrevPhase(), null);
         assertEquals(events.get(0).getNextPhase().getName(), "trial");
@@ -437,8 +437,8 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         assertEquals(events.get(5).getSubscriptionEventType(), SubscriptionEventType.PAUSE_ENTITLEMENT);
         assertEquals(events.get(6).getSubscriptionEventType(), SubscriptionEventType.PAUSE_ENTITLEMENT);
 
-        assertEquals(events.get(7).getSubscriptionEventType(), SubscriptionEventType.STOP_ENTITLEMENT);
-        assertEquals(events.get(8).getSubscriptionEventType(), SubscriptionEventType.STOP_BILLING);
+        assertEquals(events.get(7).getSubscriptionEventType(), SubscriptionEventType.STOP_BILLING);
+        assertEquals(events.get(8).getSubscriptionEventType(), SubscriptionEventType.STOP_ENTITLEMENT);
 
         assertEquals(events.get(0).getPrevPhase(), null);
         assertEquals(events.get(0).getNextPhase().getName(), "trial1");
@@ -467,6 +467,122 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
     }
 
 
+
+    @Test(groups = "fast")
+    public void testWithOverdueOfflineAndClear() throws CatalogApiException {
+        clock.setDay(new LocalDate(2013, 1, 1));
+
+        final DateTimeZone accountTimeZone = DateTimeZone.UTC;
+        final UUID accountId = UUID.randomUUID();
+        final UUID bundleId = UUID.randomUUID();
+        final String externalKey = "foo";
+
+        final UUID entitlementId = UUID.randomUUID();
+
+        final List<SubscriptionBaseTransition> allTransitions = new ArrayList<SubscriptionBaseTransition>();
+
+        final List<BlockingState> blockingStates = new ArrayList<BlockingState>();
+
+        final DateTime requestedDate = new DateTime();
+        DateTime effectiveDate = new DateTime(2013, 1, 1, 23, 11, 8, 0, DateTimeZone.UTC);
+        final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
+        allTransitions.add(tr1);
+
+        effectiveDate = effectiveDate.plusDays(30);
+        clock.addDays(30);
+        final SubscriptionBaseTransition tr2 = createTransition(entitlementId, EventType.PHASE, null, requestedDate, effectiveDate, clock.getUTCNow(), "trial", "phase");
+        allTransitions.add(tr2);
+
+        effectiveDate = effectiveDate.plusDays(6); // 2013-02-06
+        clock.addDays(6);
+        final SubscriptionBaseTransition tr3 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CANCEL, requestedDate, effectiveDate, clock.getUTCNow(), "phase", null);
+        allTransitions.add(tr3);
+
+        effectiveDate = effectiveDate.plusDays(22);// 2013-02-28
+        clock.addDays(22);
+        final SubscriptionBaseTransition tr4 = createTransition(entitlementId, EventType.API_USER, ApiEventType.RE_CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "phase");
+        allTransitions.add(tr4);
+
+        effectiveDate = effectiveDate.plusDays(12); // 2013-03-12
+        clock.addDays(12);
+        final SubscriptionBaseTransition tr5 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CANCEL, requestedDate, effectiveDate, clock.getUTCNow(), "phase", null);
+        allTransitions.add(tr5);
+
+        final BlockingState bs1 = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.ACCOUNT,
+                                                           "OFFLINE", "overdue-service",
+                                                           true, true, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
+
+        blockingStates.add(bs1);
+
+
+        final BlockingState bs2 = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                           DefaultEntitlementApi.ENT_STATE_CANCELLED, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                           true, true, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
+
+        blockingStates.add(bs2);
+
+
+        effectiveDate = effectiveDate.plusDays(12); // 2013-03-24
+        clock.addDays(12);
+
+        final BlockingState bs3 = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.ACCOUNT,
+                                                           "__KILLBILL__CLEAR__OVERDUE__STATE__", "overdue-service",
+                                                           false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow());
+
+        blockingStates.add(bs3);
+
+
+        final List<Entitlement> entitlements = new ArrayList<Entitlement>();
+        final Entitlement entitlement = createEntitlement(entitlementId, allTransitions);
+        entitlements.add(entitlement);
+
+        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, accountId, bundleId, externalKey, entitlements, blockingStates);
+
+        assertEquals(timeline.getAccountId(), accountId);
+        assertEquals(timeline.getBundleId(), bundleId);
+        assertEquals(timeline.getExternalKey(), externalKey);
+
+        final List<SubscriptionEvent> events = timeline.getSubscriptionEvents();
+        assertEquals(events.size(), 11);
+
+        assertEquals(events.get(0).getEffectiveDate().compareTo(new LocalDate(tr1.getEffectiveTransitionTime(), accountTimeZone)), 0);
+        assertEquals(events.get(1).getEffectiveDate().compareTo(new LocalDate(tr1.getEffectiveTransitionTime(), accountTimeZone)), 0);
+        assertEquals(events.get(2).getEffectiveDate().compareTo(new LocalDate(tr2.getEffectiveTransitionTime(), accountTimeZone)), 0);
+        assertEquals(events.get(3).getEffectiveDate().compareTo(new LocalDate(tr3.getEffectiveTransitionTime(), accountTimeZone)), 0);
+        assertEquals(events.get(4).getEffectiveDate().compareTo(new LocalDate(tr3.getEffectiveTransitionTime(), accountTimeZone)), 0);
+        assertEquals(events.get(5).getEffectiveDate().compareTo(new LocalDate(tr4.getEffectiveTransitionTime(), accountTimeZone)), 0);
+        assertEquals(events.get(6).getEffectiveDate().compareTo(new LocalDate(tr4.getEffectiveTransitionTime(), accountTimeZone)), 0);
+        assertEquals(events.get(7).getEffectiveDate().compareTo(new LocalDate(tr5.getEffectiveTransitionTime(), accountTimeZone)), 0);
+        assertEquals(events.get(8).getEffectiveDate().compareTo(new LocalDate(tr5.getEffectiveTransitionTime(), accountTimeZone)), 0);
+        assertEquals(events.get(9).getEffectiveDate().compareTo(new LocalDate(tr5.getEffectiveTransitionTime(), accountTimeZone)), 0);
+        assertEquals(events.get(10).getEffectiveDate().compareTo(new LocalDate(bs3.getEffectiveDate(), accountTimeZone)), 0);
+
+        assertEquals(events.get(0).getSubscriptionEventType(), SubscriptionEventType.START_ENTITLEMENT);
+        assertEquals(events.get(1).getSubscriptionEventType(), SubscriptionEventType.START_BILLING);
+        assertEquals(events.get(2).getSubscriptionEventType(), SubscriptionEventType.PHASE);
+        assertEquals(events.get(3).getSubscriptionEventType(), SubscriptionEventType.PAUSE_ENTITLEMENT);
+        assertEquals(events.get(4).getSubscriptionEventType(), SubscriptionEventType.PAUSE_BILLING);
+        assertEquals(events.get(5).getSubscriptionEventType(), SubscriptionEventType.RESUME_ENTITLEMENT);
+        assertEquals(events.get(6).getSubscriptionEventType(), SubscriptionEventType.RESUME_BILLING);
+        assertEquals(events.get(7).getSubscriptionEventType(), SubscriptionEventType.STOP_BILLING);
+        assertEquals(events.get(8).getSubscriptionEventType(), SubscriptionEventType.STOP_ENTITLEMENT);
+        assertEquals(events.get(9).getSubscriptionEventType(), SubscriptionEventType.SERVICE_STATE_CHANGE);
+        assertEquals(events.get(10).getSubscriptionEventType(), SubscriptionEventType.SERVICE_STATE_CHANGE);
+
+        assertEquals(events.get(0).getNextPhase().getName(), "trial");
+        assertEquals(events.get(1).getNextPhase().getName(), "trial");
+        assertEquals(events.get(2).getNextPhase().getName(), "phase");
+        assertEquals(events.get(3).getNextPhase(), null);
+        assertEquals(events.get(4).getNextPhase(), null);
+        assertEquals(events.get(5).getNextPhase().getName(), "phase");
+        assertEquals(events.get(6).getNextPhase().getName(), "phase");
+        assertEquals(events.get(7).getNextPhase(), null);
+        assertEquals(events.get(8).getNextPhase(), null);
+        assertEquals(events.get(9).getNextPhase(), null);
+        assertEquals(events.get(10).getNextPhase(), null);
+    }
+
+
     private DefaultEntitlement createEntitlement(final UUID entitlementId, final List<SubscriptionBaseTransition> allTransitions) {
 
         final DefaultEntitlement result = Mockito.mock(DefaultEntitlement.class);