killbill-memoizeit

Details

diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/DefaultEntitlementTransferApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/DefaultEntitlementTransferApi.java
index 4a6a2b1..b288f13 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/DefaultEntitlementTransferApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/transfer/DefaultEntitlementTransferApi.java
@@ -54,6 +54,7 @@ import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.clock.Clock;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
 
@@ -130,18 +131,25 @@ public class DefaultEntitlementTransferApi implements EntitlementTransferApi {
         return newEvent;
     }
 
-    private List<EntitlementEvent> toEvents(final List<ExistingEvent> existingEvents, final SubscriptionData subscription,
+    @VisibleForTesting
+    List<EntitlementEvent> toEvents(final List<ExistingEvent> existingEvents, final SubscriptionData subscription,
                                             final DateTime transferDate, final CallContext context) throws EntitlementTransferApiException {
-
         try {
             final List<EntitlementEvent> result = new LinkedList<EntitlementEvent>();
 
-            EntitlementEvent event = null;
+            EntitlementEvent event;
             ExistingEvent prevEvent = null;
+            EntitlementEvent prevEntitlementEvent = null;
             boolean firstEvent = true;
             for (ExistingEvent cur : existingEvents) {
                 // Skip all events prior to the transferDate
                 if (cur.getEffectiveDate().isBefore(transferDate)) {
+                    if (prevEvent != null) {
+                        final EntitlementEvent tmpPrevEntitlementEvent = createEvent(firstEvent, prevEvent, subscription, transferDate, context);
+                        if (tmpPrevEntitlementEvent != null) {
+                            prevEntitlementEvent = tmpPrevEntitlementEvent;
+                        }
+                    }
                     prevEvent = cur;
                     continue;
                 }
@@ -168,8 +176,10 @@ public class DefaultEntitlementTransferApi implements EntitlementTransferApi {
                 event = createEvent(firstEvent, prevEvent, subscription, transferDate, context);
                 if (event != null) {
                     result.add(event);
+                } else if (prevEntitlementEvent != null) {
+                    // No event at all so far, use the last non-null entitlement event we were able to build
+                    result.add(prevEntitlementEvent);
                 }
-                prevEvent = null;
             }
 
             return result;
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestDefaultEntitlementTransferApi.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestDefaultEntitlementTransferApi.java
new file mode 100644
index 0000000..ced19b9
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestDefaultEntitlementTransferApi.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.entitlement.api.transfer;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.mockito.Mockito;
+import org.skife.jdbi.v2.IDBI;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.ning.billing.catalog.MockCatalog;
+import com.ning.billing.catalog.MockCatalogService;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.CatalogService;
+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.EntitlementTestSuite;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.timeline.EntitlementTimelineApi;
+import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline.ExistingEvent;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.engine.dao.EntitlementDao;
+import com.ning.billing.entitlement.events.EntitlementEvent;
+import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
+import com.ning.billing.entitlement.events.user.ApiEventTransfer;
+import com.ning.billing.entitlement.events.user.ApiEventType;
+import com.ning.billing.util.callcontext.InternalCallContextFactory;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.clock.ClockMock;
+
+import com.google.common.collect.ImmutableList;
+
+// Simple unit tests for DefaultEntitlementTransferApi, see TestTransfer for more advanced tests with dao
+public class TestDefaultEntitlementTransferApi extends EntitlementTestSuite {
+
+    private final Clock clock = new ClockMock();
+
+    private DefaultEntitlementTransferApi transferApi;
+
+    @BeforeMethod(groups = "fast")
+    public void setUp() throws Exception {
+        final EntitlementDao dao = Mockito.mock(EntitlementDao.class);
+        final CatalogService catalogService = new MockCatalogService(new MockCatalog());
+        final SubscriptionFactory subscriptionFactory = Mockito.mock(SubscriptionFactory.class);
+        final EntitlementTimelineApi timelineApi = Mockito.mock(EntitlementTimelineApi.class);
+        final InternalCallContextFactory internalCallContextFactory = new InternalCallContextFactory(Mockito.mock(IDBI.class), clock);
+        transferApi = new DefaultEntitlementTransferApi(clock, dao, timelineApi, catalogService, subscriptionFactory, internalCallContextFactory);
+    }
+
+    @Test(groups = "fast")
+    public void testEventsAfterTransferForMigratedBundle1() throws Exception {
+        // MIGRATE_ENTITLEMENT then MIGRATE_BILLING (both in the past)
+        final DateTime transferDate = clock.getUTCNow();
+        final DateTime migrateEntitlementEventEffectiveDate = transferDate.minusDays(10);
+        final DateTime migrateBillingEventEffectiveDate = migrateEntitlementEventEffectiveDate.plusDays(1);
+        final List<EntitlementEvent> events = transferBundle(migrateEntitlementEventEffectiveDate, migrateBillingEventEffectiveDate, transferDate);
+
+        Assert.assertEquals(events.size(), 1);
+        Assert.assertEquals(events.get(0).getType(), EventType.API_USER);
+        Assert.assertEquals(events.get(0).getEffectiveDate(), transferDate);
+        Assert.assertEquals(((ApiEventTransfer) events.get(0)).getEventType(), ApiEventType.TRANSFER);
+    }
+
+    @Test(groups = "fast")
+    public void testEventsAfterTransferForMigratedBundle2() throws Exception {
+        // MIGRATE_ENTITLEMENT and MIGRATE_BILLING at the same time (both in the past)
+        final DateTime transferDate = clock.getUTCNow();
+        final DateTime migrateEntitlementEventEffectiveDate = transferDate.minusDays(10);
+        final DateTime migrateBillingEventEffectiveDate = migrateEntitlementEventEffectiveDate;
+        final List<EntitlementEvent> events = transferBundle(migrateEntitlementEventEffectiveDate, migrateBillingEventEffectiveDate, transferDate);
+
+        Assert.assertEquals(events.size(), 1);
+        Assert.assertEquals(events.get(0).getType(), EventType.API_USER);
+        Assert.assertEquals(events.get(0).getEffectiveDate(), transferDate);
+        Assert.assertEquals(((ApiEventTransfer) events.get(0)).getEventType(), ApiEventType.TRANSFER);
+    }
+
+    @Test(groups = "fast")
+    public void testEventsAfterTransferForMigratedBundle3() throws Exception {
+        // MIGRATE_ENTITLEMENT then MIGRATE_BILLING (the latter in the future)
+        final DateTime transferDate = clock.getUTCNow();
+        final DateTime migrateEntitlementEventEffectiveDate = transferDate.minusDays(10);
+        final DateTime migrateBillingEventEffectiveDate = migrateEntitlementEventEffectiveDate.plusDays(20);
+        final List<EntitlementEvent> events = transferBundle(migrateEntitlementEventEffectiveDate, migrateBillingEventEffectiveDate, transferDate);
+
+        Assert.assertEquals(events.size(), 1);
+        Assert.assertEquals(events.get(0).getType(), EventType.API_USER);
+        Assert.assertEquals(events.get(0).getEffectiveDate(), transferDate);
+        Assert.assertEquals(((ApiEventTransfer) events.get(0)).getEventType(), ApiEventType.TRANSFER);
+    }
+
+    @Test(groups = "fast")
+    public void testEventsAfterTransferForMigratedBundle4() throws Exception {
+        // MIGRATE_ENTITLEMENT then MIGRATE_BILLING (both in the future)
+        final DateTime transferDate = clock.getUTCNow();
+        final DateTime migrateEntitlementEventEffectiveDate = transferDate.plusDays(10);
+        final DateTime migrateBillingEventEffectiveDate = migrateEntitlementEventEffectiveDate.plusDays(20);
+        final List<EntitlementEvent> events = transferBundle(migrateEntitlementEventEffectiveDate, migrateBillingEventEffectiveDate, transferDate);
+
+        Assert.assertEquals(events.size(), 1);
+        Assert.assertEquals(events.get(0).getType(), EventType.API_USER);
+        Assert.assertEquals(events.get(0).getEffectiveDate(), migrateEntitlementEventEffectiveDate);
+        Assert.assertEquals(((ApiEventTransfer) events.get(0)).getEventType(), ApiEventType.TRANSFER);
+    }
+
+    private List<EntitlementEvent> transferBundle(final DateTime migrateEntitlementEventEffectiveDate, final DateTime migrateBillingEventEffectiveDate,
+                                                  final DateTime transferDate) throws EntitlementTransferApiException {
+        final ImmutableList<ExistingEvent> existingEvents = createMigrateEvents(migrateEntitlementEventEffectiveDate, migrateBillingEventEffectiveDate);
+        final SubscriptionBuilder subscriptionBuilder = new SubscriptionBuilder();
+        final SubscriptionData subscription = new SubscriptionData(subscriptionBuilder);
+
+        return transferApi.toEvents(existingEvents, subscription, transferDate, callContext);
+    }
+
+    private ImmutableList<ExistingEvent> createMigrateEvents(final DateTime migrateEntitlementEventEffectiveDate, final DateTime migrateBillingEventEffectiveDate) {
+        final ExistingEvent migrateEntitlementEvent = new ExistingEvent() {
+            @Override
+            public DateTime getEffectiveDate() {
+                return migrateEntitlementEventEffectiveDate;
+            }
+
+            @Override
+            public String getPlanPhaseName() {
+                return "BicycleTrialEvergreen1USD-trial";
+            }
+
+            @Override
+            public UUID getEventId() {
+                return UUID.randomUUID();
+            }
+
+            @Override
+            public PlanPhaseSpecifier getPlanPhaseSpecifier() {
+                return new PlanPhaseSpecifier("BicycleTrialEvergreen1USD", ProductCategory.BASE, BillingPeriod.NO_BILLING_PERIOD,
+                                              PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.FIXEDTERM);
+            }
+
+            @Override
+            public DateTime getRequestedDate() {
+                return getEffectiveDate();
+            }
+
+            @Override
+            public SubscriptionTransitionType getSubscriptionTransitionType() {
+                return SubscriptionTransitionType.MIGRATE_ENTITLEMENT;
+            }
+        };
+
+        final ExistingEvent migrateBillingEvent = new ExistingEvent() {
+
+            @Override
+            public DateTime getEffectiveDate() {
+                return migrateBillingEventEffectiveDate;
+            }
+
+            @Override
+            public String getPlanPhaseName() {
+                return migrateEntitlementEvent.getPlanPhaseName();
+            }
+
+            @Override
+            public UUID getEventId() {
+                return UUID.randomUUID();
+            }
+
+            @Override
+            public PlanPhaseSpecifier getPlanPhaseSpecifier() {
+                return migrateEntitlementEvent.getPlanPhaseSpecifier();
+            }
+
+            @Override
+            public DateTime getRequestedDate() {
+                return migrateEntitlementEvent.getRequestedDate();
+            }
+
+            @Override
+            public SubscriptionTransitionType getSubscriptionTransitionType() {
+                return SubscriptionTransitionType.MIGRATE_BILLING;
+            }
+        };
+
+        return ImmutableList.<ExistingEvent>of(migrateEntitlementEvent, migrateBillingEvent);
+    }
+}