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..8ddc345 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;
@@ -119,10 +120,13 @@ public class DefaultEntitlementTransferApi implements EntitlementTransferApi {
PhaseEventData.createNextPhaseEvent(currentPhase.getName(), subscription, clock.getUTCNow(), effectiveDate);
break;
- // Ignore
+ // Ignore these events except if it's the first event for the new subscription
case CANCEL:
case UNCANCEL:
case MIGRATE_BILLING:
+ if (firstEvent) {
+ newEvent = new ApiEventTransfer(apiBuilder);
+ }
break;
default:
throw new EntitlementError(String.format("Unepxected transitionType %s", existingEvent.getSubscriptionTransitionType()));
@@ -130,8 +134,9 @@ public class DefaultEntitlementTransferApi implements EntitlementTransferApi {
return newEvent;
}
- private List<EntitlementEvent> toEvents(final List<ExistingEvent> existingEvents, final SubscriptionData subscription,
- final DateTime transferDate, final CallContext context) throws EntitlementTransferApiException {
+ @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>();
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);
+ }
+}