killbill-aplcache
Changes
entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java 109(+104 -5)
entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java 5(+2 -3)
Details
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java
index 37db289..429eafe 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java
@@ -24,11 +24,14 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import java.util.ListIterator;
import java.util.Map;
+import java.util.Set;
import java.util.UUID;
import javax.annotation.Nullable;
+import org.joda.time.LocalDate;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Plan;
@@ -40,8 +43,13 @@ import org.killbill.billing.entitlement.block.BlockingChecker.BlockingAggregator
import org.killbill.billing.entitlement.block.DefaultBlockingChecker.DefaultBlockingAggregator;
import org.killbill.billing.junction.DefaultBlockingState;
+import com.google.common.base.Function;
import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
// Given an event stream (across one or multiple entitlements), insert the blocking events at the right place
public class BlockingStateOrdering extends EntitlementOrderingBase {
@@ -52,9 +60,12 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
public static void insertSorted(final Iterable<Entitlement> entitlements, final InternalTenantContext internalTenantContext, final LinkedList<SubscriptionEvent> inputAndOutputResult) {
INSTANCE.computeEvents(entitlements, internalTenantContext, inputAndOutputResult);
+
}
private void computeEvents(final Iterable<Entitlement> entitlements, final InternalTenantContext internalTenantContext, final LinkedList<SubscriptionEvent> inputAndOutputResult) {
+
+
final Collection<UUID> allEntitlementUUIDs = new HashSet<UUID>();
final Collection<BlockingState> blockingStates = new LinkedList<BlockingState>();
for (final Entitlement entitlement : entitlements) {
@@ -63,16 +74,19 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
blockingStates.addAll(((DefaultEntitlement) entitlement).getEventsStream().getBlockingStates());
}
+ final SupportForOlderVersionThan_0_17_X backwardCompatibleContext = new SupportForOlderVersionThan_0_17_X(inputAndOutputResult, blockingStates);
+
// Trust the incoming ordering here: blocking states were sorted using ProxyBlockingStateDao#sortedCopy
for (final BlockingState currentBlockingState : blockingStates) {
final List<SubscriptionEvent> outputNewEvents = new ArrayList<SubscriptionEvent>();
- final int index = insertFromBlockingEvent(allEntitlementUUIDs, currentBlockingState, inputAndOutputResult, internalTenantContext, outputNewEvents);
+ final int index = insertFromBlockingEvent(allEntitlementUUIDs, currentBlockingState, inputAndOutputResult, backwardCompatibleContext, internalTenantContext, outputNewEvents);
insertAfterIndex(inputAndOutputResult, outputNewEvents, index);
}
+ backwardCompatibleContext.addMissing_START_ENTITLEMENT(inputAndOutputResult, internalTenantContext);
}
// Returns the index and the newEvents generated from the incoming blocking state event. Those new events will all be created for the same effectiveDate and should be ordered.
- private int insertFromBlockingEvent(final Collection<UUID> allEntitlementUUIDs, final BlockingState currentBlockingState, final List<SubscriptionEvent> inputExistingEvents, final InternalTenantContext internalTenantContext, final Collection<SubscriptionEvent> outputNewEvents) {
+ private int insertFromBlockingEvent(final Collection<UUID> allEntitlementUUIDs, final BlockingState currentBlockingState, final List<SubscriptionEvent> inputExistingEvents, final SupportForOlderVersionThan_0_17_X backwardCompatibleContext, final InternalTenantContext internalTenantContext, final Collection<SubscriptionEvent> outputNewEvents) {
// Keep the current state per entitlement
final Map<UUID, TargetState> targetStates = new HashMap<UUID, TargetState>();
for (final UUID cur : allEntitlementUUIDs) {
@@ -104,6 +118,10 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
curTargetState.setEntitlementStopped();
break;
case START_BILLING:
+ // For older subscriptions we miss the START_ENTITLEMENT (the START_BILLING marks both start of billing and entitlement)
+ if (backwardCompatibleContext.isOlderEntitlement(cur.getEntitlementId())) {
+ curTargetState.setEntitlementStarted();
+ }
curTargetState.setBillingStarted();
break;
case PAUSE_BILLING:
@@ -343,7 +361,7 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
bs.getStateName(),
bs.getService(),
bs.isBlockChange(),
- (bs.isBlockEntitlement() && isEntitlementStarted && !isEntitlementStopped),
+ (bs.isBlockEntitlement() && isEntitlementStarted && !isEntitlementStopped),
(bs.isBlockBilling() && isBillingStarted && !isBillingStopped),
bs.getEffectiveDate());
@@ -375,7 +393,7 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
}
final BlockingAggregator stateAfter = getState();
- final boolean shouldResumeEntitlement = isEntitlementStarted && !isEntitlementStopped && stateBefore.isBlockEntitlement() && !stateAfter.isBlockEntitlement();
+ final boolean shouldResumeEntitlement = isEntitlementStarted && !isEntitlementStopped && stateBefore.isBlockEntitlement() && !stateAfter.isBlockEntitlement();
if (shouldResumeEntitlement) {
result.add(SubscriptionEventType.RESUME_ENTITLEMENT);
}
@@ -384,7 +402,7 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
result.add(SubscriptionEventType.RESUME_BILLING);
}
- final boolean shouldBlockEntitlement = isEntitlementStarted && !isEntitlementStopped && !stateBefore.isBlockEntitlement() && stateAfter.isBlockEntitlement();
+ final boolean shouldBlockEntitlement = isEntitlementStarted && !isEntitlementStopped && !stateBefore.isBlockEntitlement() && stateAfter.isBlockEntitlement();
if (shouldBlockEntitlement) {
result.add(SubscriptionEventType.PAUSE_ENTITLEMENT);
}
@@ -407,4 +425,85 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
return aggrBefore;
}
}
+
+ //
+ // The logic to add the missing START_ENTITLEMENT for older subscriptions is contained in this class. When we want/need to drop backward compatibility we can
+ // simply drop this class and where it is called.
+ //
+ private static class SupportForOlderVersionThan_0_17_X {
+
+ private final Set<UUID> olderEntitlementSet;
+
+ public SupportForOlderVersionThan_0_17_X(final List<SubscriptionEvent> initialEntitlementEvents, final Collection<BlockingState> blockingStates) {
+ this.olderEntitlementSet = computeOlderEntitlementSet(initialEntitlementEvents, blockingStates);
+ }
+
+ public boolean isOlderEntitlement(final UUID entitlementId) {
+ return olderEntitlementSet.contains(entitlementId);
+ }
+
+ public void addMissing_START_ENTITLEMENT(final LinkedList<SubscriptionEvent> inputAndOutputResult, final InternalTenantContext internalTenantContext) {
+
+ // Insert missing START_ENTITLEMENT right before START_BILLING (same event as START_BILLING but with different type=START_ENTITLEMENT to be compatible with old code)
+ final ListIterator<SubscriptionEvent> it = inputAndOutputResult.listIterator();
+ while (it.hasNext()) {
+ final SubscriptionEvent cur = it.next();
+ if (cur.getSubscriptionEventType() == SubscriptionEventType.START_BILLING && olderEntitlementSet.contains(cur.getEntitlementId())) {
+ final SubscriptionEvent newEntitlementStartEvent = new DefaultSubscriptionEvent(cur.getId(),
+ cur.getEntitlementId(),
+ internalTenantContext.toUTCDateTime(cur.getEffectiveDate()),
+ SubscriptionEventType.START_ENTITLEMENT,
+ false,
+ false,
+ DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ SubscriptionEventType.START_ENTITLEMENT.toString(),
+ cur.getPrevProduct(),
+ cur.getPrevPlan(),
+ cur.getPrevPhase(),
+ cur.getPrevPriceList(),
+ cur.getPrevBillingPeriod(),
+ cur.getNextProduct(),
+ cur.getNextPlan(),
+ cur.getNextPhase(),
+ cur.getNextPriceList(),
+ cur.getNextBillingPeriod(),
+ internalTenantContext.toUTCDateTime(cur.getEffectiveDate()),
+ internalTenantContext);
+ it.previous();
+ it.add(newEntitlementStartEvent);
+ it.next();
+ }
+ }
+ }
+
+ private Set<UUID> computeOlderEntitlementSet(final List<SubscriptionEvent> initialEntitlementEvents, final Collection<BlockingState> blockingStates) {
+
+ final Set<UUID> START_BILLING_entitlementIdSet = ImmutableSet.copyOf(Iterables.transform(Iterables.filter(initialEntitlementEvents, new Predicate<SubscriptionEvent>() {
+ @Override
+ public boolean apply(final SubscriptionEvent input) {
+ return input.getSubscriptionEventType() == SubscriptionEventType.START_BILLING;
+ }
+ }), new Function<SubscriptionEvent, UUID>() {
+ @Override
+ public UUID apply(final SubscriptionEvent input) {
+ return input.getEntitlementId();
+ }
+ }));
+
+ final Set<UUID> ENT_STATE_START_entitlementIdSet = ImmutableSet.copyOf(Iterables.transform(Iterables.filter(blockingStates, new Predicate<BlockingState>() {
+ @Override
+ public boolean apply(final BlockingState input) {
+ return input.getService().equals(DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME) && input.getStateName().equals(DefaultEntitlementApi.ENT_STATE_START);
+ }
+ }), new Function<BlockingState, UUID>() {
+ @Override
+ public UUID apply(final BlockingState input) {
+ return input.getBlockedId();
+ }
+ }));
+
+ return Sets.<UUID>difference(START_BILLING_entitlementIdSet, ENT_STATE_START_entitlementIdSet);
+ }
+ }
+
}
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java
index 9d5513e..9283363 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java
@@ -24,7 +24,6 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import org.joda.time.DateTime;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.entitlement.DefaultEntitlementService;
import org.killbill.billing.subscription.api.SubscriptionBase;
@@ -80,7 +79,7 @@ public class SubscriptionEventOrdering extends EntitlementOrderingBase {
for (final SubscriptionBaseTransition tr : baseTransitions) {
final List<SubscriptionEventType> eventTypes = toEventTypes(tr.getTransitionType());
for (final SubscriptionEventType eventType : eventTypes) {
- final SubscriptionEvent event = toSubscriptionEvent(tr, eventType, base.getStartDate(), internalTenantContext);
+ final SubscriptionEvent event = toSubscriptionEvent(tr, eventType, internalTenantContext);
insertSubscriptionEvent(event, result);
}
}
@@ -147,7 +146,7 @@ public class SubscriptionEventOrdering extends EntitlementOrderingBase {
result.add(index, event);
}
- private SubscriptionEvent toSubscriptionEvent(final SubscriptionBaseTransition in, final SubscriptionEventType eventType, final DateTime referenceTime, final InternalTenantContext internalTenantContext) {
+ private SubscriptionEvent toSubscriptionEvent(final SubscriptionBaseTransition in, final SubscriptionEventType eventType, final InternalTenantContext internalTenantContext) {
return new DefaultSubscriptionEvent(in.getId(),
in.getSubscriptionId(),
in.getEffectiveTransitionTime(),
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
index 7220143..c4dcbd3 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
@@ -265,6 +265,16 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
@Test(groups = "fast")
public void testOneSimpleEntitlement() throws CatalogApiException {
+ testOneSimpleEntitlementImpl(false);
+ }
+
+ @Test(groups = "fast")
+ public void testOneSimpleEntitlementWithRegression() throws CatalogApiException {
+ testOneSimpleEntitlementImpl(true);
+ }
+
+
+ private void testOneSimpleEntitlementImpl(boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
clock.setDay(new LocalDate(2013, 1, 1));
final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -283,10 +293,12 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
allTransitions.add(tr1);
- final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
- DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
- false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
- blockingStates.add(bsCreate);
+ if (!regressionFlagForOlderVersionThan_0_17_X) {
+ final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+ blockingStates.add(bsCreate);
+ }
effectiveDate = effectiveDate.plusDays(30);
@@ -336,8 +348,18 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
assertNull(events.get(3).getNextPhase());
}
+
+
@Test(groups = "fast")
public void testCancelBundleBeforeSubscription() throws CatalogApiException {
+ testCancelBundleBeforeSubscriptionImpl(false);
+ }
+ @Test(groups = "fast")
+ public void testCancelBundleBeforeSubscriptionWithRegression() throws CatalogApiException {
+ testCancelBundleBeforeSubscriptionImpl(true);
+ }
+
+ private void testCancelBundleBeforeSubscriptionImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
clock.setDay(new LocalDate(2013, 1, 1));
final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -355,10 +377,12 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
allTransitions.add(tr1);
- final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
- DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
- false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
- blockingStates.add(bsCreate);
+ if (!regressionFlagForOlderVersionThan_0_17_X) {
+ final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+ blockingStates.add(bsCreate);
+ }
// Block the bundle before the subscription
effectiveDate = effectiveDate.plusDays(15);
@@ -411,6 +435,16 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
@Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/135")
public void testOneEntitlementWithPauseResume() throws CatalogApiException {
+ testOneEntitlementWithPauseResumeImpl(false);
+ }
+
+ @Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/135")
+ public void testOneEntitlementWithPauseResumeWithRegression() throws CatalogApiException {
+ testOneEntitlementWithPauseResumeImpl(true);
+ }
+
+
+ private void testOneEntitlementWithPauseResumeImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
clock.setDay(new LocalDate(2013, 1, 1));
final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -428,11 +462,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
allTransitions.add(tr1);
- final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
- DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
- false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+ if (!regressionFlagForOlderVersionThan_0_17_X) {
+ final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+
+ blockingStates.add(bsCreate);
+ }
- blockingStates.add(bsCreate);
effectiveDate = effectiveDate.plusDays(30);
clock.addDays(30);
@@ -544,6 +581,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
@Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/147 and https://github.com/killbill/killbill/issues/148")
public void testOneEntitlementWithOverduePauseThenCancel() throws CatalogApiException {
+ testOneEntitlementWithOverduePauseThenCancelImpl(false);
+ }
+
+ public void testOneEntitlementWithOverduePauseThenCancelWithRegression() throws CatalogApiException {
+ testOneEntitlementWithOverduePauseThenCancelImpl(true);
+ }
+
+ private void testOneEntitlementWithOverduePauseThenCancelImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
clock.setDay(new LocalDate(2013, 1, 1));
final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -561,12 +606,13 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
allTransitions.add(tr1);
- final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
- DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
- false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
-
- blockingStates.add(bsCreate);
+ if (!regressionFlagForOlderVersionThan_0_17_X) {
+ final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+ blockingStates.add(bsCreate);
+ }
effectiveDate = effectiveDate.plusDays(30);
clock.addDays(30);
@@ -699,6 +745,15 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
@Test(groups = "fast")
public void testOneEntitlementWithInitialBlockingState() throws CatalogApiException {
+ testOneEntitlementWithInitialBlockingStateImpl(false);
+ }
+
+ @Test(groups = "fast")
+ public void testOneEntitlementWithInitialBlockingStateWithRegression() throws CatalogApiException {
+ testOneEntitlementWithInitialBlockingStateImpl(true);
+ }
+
+ private void testOneEntitlementWithInitialBlockingStateImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
clock.setDay(new LocalDate(2013, 1, 1));
final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -723,12 +778,13 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
allTransitions.add(tr1);
- final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
- DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
- false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
-
- blockingStates.add(bsCreate);
+ if (!regressionFlagForOlderVersionThan_0_17_X) {
+ final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+ blockingStates.add(bsCreate);
+ }
effectiveDate = effectiveDate.plusDays(30);
clock.addDays(30);
@@ -791,6 +847,15 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
@Test(groups = "fast")
public void testOneEntitlementWithBlockingStatesSubscription() throws CatalogApiException {
+ testOneEntitlementWithBlockingStatesSubscriptionImpl(false);
+ }
+
+ @Test(groups = "fast")
+ public void testOneEntitlementWithBlockingStatesSubscriptionWithRegression() throws CatalogApiException {
+ testOneEntitlementWithBlockingStatesSubscriptionImpl(true);
+ }
+
+ private void testOneEntitlementWithBlockingStatesSubscriptionImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
clock.setDay(new LocalDate(2013, 1, 1));
final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -808,12 +873,13 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
allTransitions.add(tr1);
- final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
- DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
- false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
-
- blockingStates.add(bsCreate);
+ if (!regressionFlagForOlderVersionThan_0_17_X) {
+ final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+ blockingStates.add(bsCreate);
+ }
effectiveDate = effectiveDate.plusDays(30);
clock.addDays(30);
@@ -885,6 +951,15 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
@Test(groups = "fast")
public void testWithMultipleEntitlements() throws CatalogApiException {
+ testWithMultipleEntitlementsImpl(false);
+ }
+
+ @Test(groups = "fast")
+ public void testWithMultipleEntitlementsWithRegression() throws CatalogApiException {
+ testWithMultipleEntitlementsImpl(true);
+ }
+
+ private void testWithMultipleEntitlementsImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
clock.setDay(new LocalDate(2013, 1, 1));
final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -904,23 +979,26 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final SubscriptionBaseTransition ent1Tr1 = createTransition(entitlementId1, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial1");
allTransitions1.add(ent1Tr1);
- final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId1, BlockingStateType.SUBSCRIPTION,
- DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
- false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
-
- blockingStates.add(bsCreate);
+ if (!regressionFlagForOlderVersionThan_0_17_X) {
+ final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId1, BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+ blockingStates.add(bsCreate);
+ }
effectiveDate = effectiveDate.plusDays(15);
clock.addDays(15);
final SubscriptionBaseTransition ent2Tr1 = createTransition(entitlementId2, EventType.API_USER, ApiEventType.TRANSFER, requestedDate, effectiveDate, clock.getUTCNow(), null, "phase2");
allTransitions2.add(ent2Tr1);
- final BlockingState bsCreate2 = new DefaultBlockingState(UUID.randomUUID(), entitlementId2, BlockingStateType.SUBSCRIPTION,
- DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
- false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+ if (!regressionFlagForOlderVersionThan_0_17_X ) {
+ final BlockingState bsCreate2 = new DefaultBlockingState(UUID.randomUUID(), entitlementId2, BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
- blockingStates.add(bsCreate2);
+ blockingStates.add(bsCreate2);
+ }
effectiveDate = effectiveDate.plusDays(15);
clock.addDays(15);
@@ -1022,6 +1100,15 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
@Test(groups = "fast")
public void testWithOverdueOffline() throws CatalogApiException {
+ testWithOverdueOfflineImpl(false);
+ }
+
+ @Test(groups = "fast")
+ public void testWithOverdueOfflineWithRegression() throws CatalogApiException {
+ testWithOverdueOfflineImpl(true);
+ }
+
+ private void testWithOverdueOfflineImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
clock.setDay(new LocalDate(2013, 1, 1));
final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -1040,12 +1127,13 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
allTransitions.add(tr1);
- final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
- DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
- false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
-
- blockingStates.add(bsCreate);
+ if (!regressionFlagForOlderVersionThan_0_17_X) {
+ final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+ blockingStates.add(bsCreate);
+ }
effectiveDate = effectiveDate.plusDays(30);
clock.addDays(30);
@@ -1142,6 +1230,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
@Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/134")
public void testRemoveOverlappingBlockingStates() throws CatalogApiException {
+ testRemoveOverlappingBlockingStatesImpl(false);
+ }
+
+ public void testRemoveOverlappingBlockingStatesWithRegression() throws CatalogApiException {
+ testRemoveOverlappingBlockingStatesImpl(true);
+ }
+
+ public void testRemoveOverlappingBlockingStatesImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
clock.setDay(new LocalDate(2013, 1, 1));
final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -1159,12 +1255,13 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
allTransitions.add(tr1);
- final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
- DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
- false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
-
- blockingStates.add(bsCreate);
+ if (!regressionFlagForOlderVersionThan_0_17_X) {
+ final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+ blockingStates.add(bsCreate);
+ }
// Overlapping ENT_STATE_BLOCKED - should merge
effectiveDate = effectiveDate.plusDays(5);
@@ -1233,6 +1330,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
@Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/149")
public void testVariousBlockingStatesAtTheSameEffectiveDate() throws CatalogApiException {
+ testVariousBlockingStatesAtTheSameEffectiveDateImpl(false);
+ }
+
+ public void testVariousBlockingStatesAtTheSameEffectiveDateWithRegression() throws CatalogApiException {
+ testVariousBlockingStatesAtTheSameEffectiveDateImpl(true);
+ }
+
+ private void testVariousBlockingStatesAtTheSameEffectiveDateImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
clock.setDay(new LocalDate(2013, 1, 1));
final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -1250,12 +1355,13 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
allTransitions.add(tr1);
- final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
- DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
- false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
-
- blockingStates.add(bsCreate);
+ if (!regressionFlagForOlderVersionThan_0_17_X) {
+ final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+ blockingStates.add(bsCreate);
+ }
// 2013-02-10
effectiveDate = effectiveDate.plusDays(40);
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestRegessionSubscriptionApi.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestRegessionSubscriptionApi.java
new file mode 100644
index 0000000..4b772e3
--- /dev/null
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestRegessionSubscriptionApi.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.entitlement.api;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.killbill.billing.ErrorCode;
+import org.killbill.billing.OrderingType;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.account.api.AccountApiException;
+import org.killbill.billing.api.TestApiListener.NextEvent;
+import org.killbill.billing.catalog.api.BillingActionPolicy;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.PriceListSet;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.entitlement.EntitlementService;
+import org.killbill.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementActionPolicy;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
+import org.killbill.billing.junction.DefaultBlockingState;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.AuditLog;
+import org.killbill.billing.util.audit.ChangeType;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+public class TestRegessionSubscriptionApi extends EntitlementTestSuiteWithEmbeddedDB {
+
+ @Test(groups = "slow", description = "Verify behavior with or without ENT_STARTED event works as expected")
+ public void testRegressionForNew_ENT_STARTED_event() throws Exception {
+
+ final LocalDate initialDate = new LocalDate(2013, 8, 7);
+ clock.setDay(initialDate);
+
+ // Start the entitlement yesterday (does not m,ake sense, but we want to check later different of behavior)
+ final LocalDate entitlementEffectiveDate = initialDate.minusDays(1);
+
+ final Account account = createAccount(getAccountData(7));
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+ testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.randomUUID().toString(), null, entitlementEffectiveDate, null, ImmutableList.<PluginProperty>of(), callContext);
+ // Because of the BlockingState event ENT_STARTED, the entitlement date should be correctly set
+ Assert.assertEquals(entitlement.getEffectiveStartDate(), entitlementEffectiveDate);
+
+ final List<SubscriptionBundle> bundles = subscriptionApi.getSubscriptionBundlesForAccountId(account.getId(), callContext);
+ Assert.assertEquals(bundles.size(), 1);
+ subscriptionBundleChecker(bundles, initialDate, entitlement, 0);
+
+ // Let's do some surgery and inactivate the ENT_STARTED BlockingState
+ final List<BlockingState> blockingStates = blockingStateDao.getBlockingState(entitlement.getId(), BlockingStateType.SUBSCRIPTION, clock.getUTCNow(), internalCallContext);
+ assertEquals(blockingStates.size(), 1);
+ assertEquals(blockingStates.get(0).getStateName(), DefaultEntitlementApi.ENT_STATE_START);
+ blockingStateDao.unactiveBlockingState(blockingStates.get(0).getId(), internalCallContext);
+
+ final Entitlement oldSchoolEntitlement = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
+ // Because the ENT_STARTED BlockingState has been invalidated, the startDate should now default to the billingDate
+ Assert.assertEquals(oldSchoolEntitlement.getEffectiveStartDate(), initialDate);
+
+ final List<SubscriptionBundle> oldSchoolBundles = subscriptionApi.getSubscriptionBundlesForAccountId(account.getId(), callContext);
+ Assert.assertEquals(oldSchoolBundles.size(), 1);
+ subscriptionBundleChecker(oldSchoolBundles, initialDate, oldSchoolEntitlement, 0);
+ }
+
+ private void subscriptionBundleChecker(final List<SubscriptionBundle> bundles, final LocalDate billingStartDate, final Entitlement entitlement, final int idx) {
+ Assert.assertEquals(bundles.get(idx).getId(), entitlement.getBundleId());
+ Assert.assertEquals(bundles.get(idx).getSubscriptions().size(), 1);
+ Assert.assertEquals(bundles.get(idx).getSubscriptions().get(0).getId(), entitlement.getId());
+ Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().size(), 3);
+ Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(0).getEffectiveDate(), entitlement.getEffectiveStartDate());
+ Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(0).getSubscriptionEventType(), SubscriptionEventType.START_ENTITLEMENT);
+ Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(1).getEffectiveDate(), billingStartDate);
+ Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(1).getSubscriptionEventType(), SubscriptionEventType.START_BILLING);
+ Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(2).getEffectiveDate(), new LocalDate(2013, 9, 6));
+ Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(2).getSubscriptionEventType(), SubscriptionEventType.PHASE);
+ }
+
+}