killbill-aplcache
Changes
entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultInternalBlockingApi.java 4(+2 -2)
entitlement/src/main/resources/com/ning/billing/entitlement/dao/BlockingStateSqlDao.sql.stg 8(+4 -4)
entitlement/src/test/java/com/ning/billing/entitlement/dao/TestDefaultBlockingStateDao.java 16(+8 -8)
junction/pom.xml 10(+10 -0)
junction/src/main/java/com/ning/billing/junction/plumbing/billing/BlockingCalculator.java 62(+45 -17)
junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingEventSet.java 4(+0 -4)
junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultInternalBillingApi.java 10(+5 -5)
junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleWithEmbeddedDB.java 16(+16 -0)
junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java 95(+61 -34)
junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java 200(+200 -0)
junction/src/test/resources/catalog.xml 828(+828 -0)
Details
diff --git a/api/src/main/java/com/ning/billing/junction/BillingEventSet.java b/api/src/main/java/com/ning/billing/junction/BillingEventSet.java
index 8dfca08..237e658 100644
--- a/api/src/main/java/com/ning/billing/junction/BillingEventSet.java
+++ b/api/src/main/java/com/ning/billing/junction/BillingEventSet.java
@@ -25,7 +25,4 @@ public interface BillingEventSet extends SortedSet<BillingEvent> {
public abstract boolean isAccountAutoInvoiceOff();
public abstract List<UUID> getSubscriptionIdsWithAutoInvoiceOff();
-
- public boolean isLast(BillingEvent event);
-
}
diff --git a/api/src/main/java/com/ning/billing/junction/BlockingInternalApi.java b/api/src/main/java/com/ning/billing/junction/BlockingInternalApi.java
index 0d30cf4..e7b9720 100644
--- a/api/src/main/java/com/ning/billing/junction/BlockingInternalApi.java
+++ b/api/src/main/java/com/ning/billing/junction/BlockingInternalApi.java
@@ -28,7 +28,7 @@ public interface BlockingInternalApi {
public BlockingState getBlockingStateForService(UUID blockableId, BlockingStateType blockingStateType, String serviceName, InternalTenantContext context);
- public List<BlockingState> getBlockingAll(UUID blockableId, BlockingStateType blockingStateType, InternalTenantContext context);
+ public List<BlockingState> getBlockingAllForAccount(InternalTenantContext context);
public void setBlockingState(BlockingState state, InternalCallContext context);
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultInternalBlockingApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultInternalBlockingApi.java
index 22f7434..2ca84c0 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultInternalBlockingApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultInternalBlockingApi.java
@@ -55,8 +55,8 @@ public class DefaultInternalBlockingApi implements BlockingInternalApi {
}
@Override
- public List<BlockingState> getBlockingAll(final UUID overdueableId, final BlockingStateType blockingStateType, final InternalTenantContext context) {
- return dao.getBlockingAll(overdueableId, blockingStateType, context);
+ public List<BlockingState> getBlockingAllForAccount(final InternalTenantContext context) {
+ return dao.getBlockingAllForAccountRecordId(context);
}
@Override
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/dao/ProxyBlockingStateDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/dao/ProxyBlockingStateDao.java
index a154d28..8c96ed6 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/dao/ProxyBlockingStateDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/dao/ProxyBlockingStateDao.java
@@ -60,21 +60,38 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
private static final Logger log = LoggerFactory.getLogger(ProxyBlockingStateDao.class);
+ // Ordering is critical here, especially for Junction
private static final Ordering<BlockingState> BLOCKING_STATE_ORDERING = Ordering.<BlockingState>from(new Comparator<BlockingState>() {
@Override
public int compare(final BlockingState o1, final BlockingState o2) {
- final int blockableIdComparison = o1.getBlockedId().compareTo(o2.getBlockedId());
- if (blockableIdComparison == 0) {
- // effective_date column NOT NULL
- final int comparison = o1.getEffectiveDate().compareTo(o2.getEffectiveDate());
- if (comparison == 0) {
- // Keep a stable ordering for ties
- return o1.getCreatedDate().compareTo(o2.getCreatedDate());
+ // effective_date column NOT NULL
+ final int effectiveDateComparison = o1.getEffectiveDate().compareTo(o2.getEffectiveDate());
+ if (effectiveDateComparison != 0) {
+ return effectiveDateComparison;
+ } else {
+ final int blockableIdComparison = o1.getBlockedId().compareTo(o2.getBlockedId());
+ if (blockableIdComparison != 0) {
+ return blockableIdComparison;
} else {
- return comparison;
+ // Same date, same blockable id - make sure billing transitions are respected first (assume block -> clear transitions)
+ if (!o1.isBlockBilling() && o2.isBlockBilling()) {
+ return 1;
+ } else if (o1.isBlockBilling() && !o2.isBlockBilling()) {
+ return -1;
+ }
+
+ // Then respect other blocking states
+ if ((!o1.isBlockChange() && o2.isBlockChange()) ||
+ (!o1.isBlockEntitlement() && o2.isBlockEntitlement())) {
+ return 1;
+ } else if ((o1.isBlockChange() && !o2.isBlockChange()) ||
+ (o1.isBlockEntitlement() && !o2.isBlockEntitlement())) {
+ return -1;
+ }
+
+ // Otherwise, just respect the created date
+ return o1.getCreatedDate().compareTo(o2.getCreatedDate());
}
- } else {
- return blockableIdComparison;
}
}
});
@@ -204,11 +221,13 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
baseSubscriptionsToConsider = ImmutableList.<SubscriptionBase>of(baseSubscription);
} else {
// blockable id points to a base or standalone subscription, there is nothing to do
- return blockingStatesOnDisk;
+ // Simply return the sorted list
+ return BLOCKING_STATE_ORDERING.immutableSortedCopy(blockingStatesOnDisk);
}
} else {
// blockable id points to an account or bundle, in which case there are no extra blocking states to add
- return blockingStatesOnDisk;
+ // Simply return the sorted list
+ return BLOCKING_STATE_ORDERING.immutableSortedCopy(blockingStatesOnDisk);
}
} catch (SubscriptionBaseApiException e) {
log.error("Error retrieving subscriptions for account record id " + context.getAccountRecordId(), e);
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/dao/BlockingStateSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/dao/BlockingStateSqlDao.sql.stg
index 25067a4..484b8fd 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/dao/BlockingStateSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/dao/BlockingStateSqlDao.sql.stg
@@ -50,7 +50,7 @@ and effective_date \<= :effectiveDate
and is_active
<AND_CHECK_TENANT()>
-- We want the current state, hence the order desc and limit 1
-order by record_id desc
+order by effective_date desc, record_id desc
limit 1
;
>>
@@ -71,7 +71,7 @@ getBlockingState() ::= <<
group by service
) tmp
on t.record_id = tmp.record_id
- order by t.record_id asc
+ order by t.effective_date, t.record_id asc
;
>>
@@ -84,7 +84,7 @@ where blockable_id = :blockableId
and service = :service
and is_active
<AND_CHECK_TENANT()>
-order by record_id asc
+order by effective_date, record_id asc
;
>>
@@ -96,7 +96,7 @@ from
where blockable_id = :blockableId
and is_active
<AND_CHECK_TENANT()>
-order by record_id asc
+order by effective_date, record_id asc
;
>>
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/block/TestBlockingApi.java b/entitlement/src/test/java/com/ning/billing/entitlement/block/TestBlockingApi.java
index e227e1d..b17e7d8 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/block/TestBlockingApi.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/block/TestBlockingApi.java
@@ -90,7 +90,7 @@ public class TestBlockingApi extends EntitlementTestSuiteWithEmbeddedDB {
blockingInternalApi.setBlockingState(state2, internalCallContext);
assertListenerStatus();
- final List<BlockingState> blockingAll = blockingInternalApi.getBlockingAll(uuid, BlockingStateType.ACCOUNT, internalCallContext);
+ final List<BlockingState> blockingAll = blockingInternalApi.getBlockingAllForAccount(internalCallContext);
final List<BlockingState> history = ImmutableList.<BlockingState>copyOf(Collections2.<BlockingState>filter(blockingAll,
new Predicate<BlockingState>() {
@Override
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/dao/MockBlockingStateDao.java b/entitlement/src/test/java/com/ning/billing/entitlement/dao/MockBlockingStateDao.java
index f2bff3d..3e3c69d 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/dao/MockBlockingStateDao.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/dao/MockBlockingStateDao.java
@@ -32,6 +32,7 @@ import com.ning.billing.entitlement.api.BlockingStateType;
import com.ning.billing.entitlement.api.EntitlementApiException;
import com.ning.billing.util.entity.dao.MockEntityDaoBase;
+import com.google.common.base.Objects;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
@@ -39,6 +40,7 @@ import com.google.common.collect.ImmutableList;
public class MockBlockingStateDao extends MockEntityDaoBase<BlockingStateModelDao, BlockingState, EntitlementApiException> implements BlockingStateDao {
private final Map<UUID, List<BlockingState>> blockingStates = new HashMap<UUID, List<BlockingState>>();
+ private final Map<Long, List<BlockingState>> blockingStatesPerAccountRecordId = new HashMap<Long, List<BlockingState>>();
// TODO This mock class should also check that events are past or present except for getBlockingAll
@@ -100,7 +102,7 @@ public class MockBlockingStateDao extends MockEntityDaoBase<BlockingStateModelDa
@Override
public List<BlockingState> getBlockingAllForAccountRecordId(final InternalTenantContext context) {
- throw new UnsupportedOperationException();
+ return Objects.firstNonNull(blockingStatesPerAccountRecordId.get(context.getAccountRecordId()), ImmutableList.<BlockingState>of());
}
@Override
@@ -109,6 +111,11 @@ public class MockBlockingStateDao extends MockEntityDaoBase<BlockingStateModelDa
blockingStates.put(state.getBlockedId(), new ArrayList<BlockingState>());
}
blockingStates.get(state.getBlockedId()).add(state);
+
+ if (blockingStatesPerAccountRecordId.get(context.getAccountRecordId()) == null) {
+ blockingStatesPerAccountRecordId.put(context.getAccountRecordId(), new ArrayList<BlockingState>());
+ }
+ blockingStatesPerAccountRecordId.get(context.getAccountRecordId()).add(state);
}
@Override
@@ -116,11 +123,8 @@ public class MockBlockingStateDao extends MockEntityDaoBase<BlockingStateModelDa
throw new UnsupportedOperationException();
}
- public synchronized void setBlockingStates(final UUID blockedId, final List<BlockingState> states) {
- blockingStates.put(blockedId, states);
- }
-
public synchronized void clear() {
blockingStates.clear();
+ blockingStatesPerAccountRecordId.clear();
}
}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/dao/TestDefaultBlockingStateDao.java b/entitlement/src/test/java/com/ning/billing/entitlement/dao/TestDefaultBlockingStateDao.java
index ed26f65..9300fc7 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/dao/TestDefaultBlockingStateDao.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/dao/TestDefaultBlockingStateDao.java
@@ -136,12 +136,12 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
Assert.assertEquals(blockingStates5.size(), 2);
Assert.assertEquals(blockingStates5.get(0).getBlockedId(), blockableId);
Assert.assertEquals(blockingStates5.get(0).getStateName(), state);
- Assert.assertEquals(blockingStates5.get(0).getService(), serviceB);
- Assert.assertEquals(blockingStates5.get(0).getEffectiveDate(), stateDateTime);
+ Assert.assertEquals(blockingStates5.get(0).getService(), serviceA);
+ Assert.assertEquals(blockingStates5.get(0).getEffectiveDate(), stateDateTime3);
Assert.assertEquals(blockingStates5.get(1).getBlockedId(), blockableId);
Assert.assertEquals(blockingStates5.get(1).getStateName(), state);
- Assert.assertEquals(blockingStates5.get(1).getService(), serviceA);
- Assert.assertEquals(blockingStates5.get(1).getEffectiveDate(), stateDateTime3);
+ Assert.assertEquals(blockingStates5.get(1).getService(), serviceB);
+ Assert.assertEquals(blockingStates5.get(1).getEffectiveDate(), stateDateTime);
// Set a new state for service A
final DateTime state2DateTime = new DateTime(2013, 12, 6, 10, 11, 12, DateTimeZone.UTC);
@@ -151,12 +151,12 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
Assert.assertEquals(blockingStates6.size(), 3);
Assert.assertEquals(blockingStates6.get(0).getBlockedId(), blockableId);
Assert.assertEquals(blockingStates6.get(0).getStateName(), state);
- Assert.assertEquals(blockingStates6.get(0).getService(), serviceB);
- Assert.assertEquals(blockingStates6.get(0).getEffectiveDate(), stateDateTime);
+ Assert.assertEquals(blockingStates6.get(0).getService(), serviceA);
+ Assert.assertEquals(blockingStates6.get(0).getEffectiveDate(), stateDateTime3);
Assert.assertEquals(blockingStates6.get(1).getBlockedId(), blockableId);
Assert.assertEquals(blockingStates6.get(1).getStateName(), state);
- Assert.assertEquals(blockingStates6.get(1).getService(), serviceA);
- Assert.assertEquals(blockingStates6.get(1).getEffectiveDate(), stateDateTime3);
+ Assert.assertEquals(blockingStates6.get(1).getService(), serviceB);
+ Assert.assertEquals(blockingStates6.get(1).getEffectiveDate(), stateDateTime);
Assert.assertEquals(blockingStates6.get(2).getBlockedId(), blockableId);
Assert.assertEquals(blockingStates6.get(2).getStateName(), state2);
Assert.assertEquals(blockingStates6.get(2).getService(), serviceA);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/MockBillingEventSet.java b/invoice/src/test/java/com/ning/billing/invoice/MockBillingEventSet.java
index f32427d..6037eeb 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/MockBillingEventSet.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/MockBillingEventSet.java
@@ -36,11 +36,6 @@ public class MockBillingEventSet extends TreeSet<BillingEvent> implements Billin
}
@Override
- public boolean isLast(final BillingEvent event) {
- return event == last();
- }
-
- @Override
public boolean isAccountAutoInvoiceOff() {
return isAccountInvoiceOff;
}
junction/pom.xml 10(+10 -0)
diff --git a/junction/pom.xml b/junction/pom.xml
index 71d8d07..3a94722 100644
--- a/junction/pom.xml
+++ b/junction/pom.xml
@@ -50,6 +50,11 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>com.jayway.awaitility</groupId>
+ <artifactId>awaitility</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>com.ning.billing</groupId>
<artifactId>killbill-api</artifactId>
</dependency>
@@ -81,6 +86,11 @@
</dependency>
<dependency>
<groupId>com.ning.billing</groupId>
+ <artifactId>killbill-subscription</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
<artifactId>killbill-util</artifactId>
</dependency>
<dependency>
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BlockingCalculator.java b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BlockingCalculator.java
index e2cca24..bc98975 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BlockingCalculator.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BlockingCalculator.java
@@ -25,22 +25,23 @@ import java.util.TreeSet;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
+import javax.annotation.Nullable;
+
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import com.ning.billing.account.api.Account;
+import com.ning.billing.callcontext.InternalTenantContext;
import com.ning.billing.catalog.api.BillingPeriod;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
-import com.ning.billing.entitlement.api.BlockingStateType;
-import com.ning.billing.subscription.api.SubscriptionBaseTransitionType;
-import com.ning.billing.subscription.api.SubscriptionBase;
import com.ning.billing.entitlement.api.BlockingState;
-import com.ning.billing.callcontext.InternalTenantContext;
import com.ning.billing.junction.BillingEvent;
import com.ning.billing.junction.BillingModeType;
import com.ning.billing.junction.BlockingInternalApi;
+import com.ning.billing.subscription.api.SubscriptionBase;
+import com.ning.billing.subscription.api.SubscriptionBaseTransitionType;
import com.google.common.annotations.VisibleForTesting;
import com.google.inject.Inject;
@@ -52,8 +53,9 @@ public class BlockingCalculator {
private final BlockingInternalApi blockingApi;
protected static class DisabledDuration {
+
private final DateTime start;
- private final DateTime end;
+ private DateTime end;
public DisabledDuration(final DateTime start, final DateTime end) {
this.start = start;
@@ -68,6 +70,9 @@ public class BlockingCalculator {
return end;
}
+ public void setEnd(final DateTime end) {
+ this.end = end;
+ }
}
@Inject
@@ -92,11 +97,9 @@ public class BlockingCalculator {
final SortedSet<BillingEvent> billingEventsToAdd = new TreeSet<BillingEvent>();
final SortedSet<BillingEvent> billingEventsToRemove = new TreeSet<BillingEvent>();
+ final List<BlockingState> blockingEvents = blockingApi.getBlockingAllForAccount(context);
+ final List<DisabledDuration> blockingDurations = createBlockingDurations(blockingEvents);
for (final UUID bundleId : bundleMap.keySet()) {
- final List<BlockingState> blockingEvents = blockingApi.getBlockingAll(bundleId, BlockingStateType.SUBSCRIPTION_BUNDLE, context);
- blockingEvents.addAll(blockingApi.getBlockingAll(account.getId(), BlockingStateType.ACCOUNT, context));
- final List<DisabledDuration> blockingDurations = createBlockingDurations(blockingEvents);
-
for (final SubscriptionBase subscription : bundleMap.get(bundleId)) {
billingEventsToAdd.addAll(createNewEvents(blockingDurations, billingEvents, account, subscription));
billingEventsToRemove.addAll(eventsToRemove(blockingDurations, billingEvents, subscription));
@@ -252,31 +255,56 @@ public class BlockingCalculator {
return result;
}
-
// In ascending order
- protected List<DisabledDuration> createBlockingDurations(final List<BlockingState> overdueBundleEvents) {
+ protected List<DisabledDuration> createBlockingDurations(final Iterable<BlockingState> overdueBundleEvents) {
final List<DisabledDuration> result = new ArrayList<BlockingCalculator.DisabledDuration>();
// Earliest blocking event
BlockingState first = null;
+ int blockedNesting = 0;
+ BlockingState lastOne = null;
for (final BlockingState e : overdueBundleEvents) {
- if (e.isBlockBilling() && first == null) {
+ lastOne = e;
+ if (e.isBlockBilling() && blockedNesting == 0) {
// First blocking event of contiguous series of blocking events
first = e;
- } else if (first != null && !e.isBlockBilling()) {
- // End of the interval
- result.add(new DisabledDuration(first.getEffectiveDate(), e.getEffectiveDate()));
- first = null;
+ blockedNesting++;
+ } else if (e.isBlockBilling() && blockedNesting > 0) {
+ // Nest blocking states
+ blockedNesting++;
+ } else if (!e.isBlockBilling() && blockedNesting > 0) {
+ blockedNesting--;
+ if (blockedNesting == 0) {
+ // End of the interval
+ addDisabledDuration(result, first, e);
+ first = null;
+ }
}
}
if (first != null) { // found a transition to disabled with no terminating event
- result.add(new DisabledDuration(first.getEffectiveDate(), null));
+ addDisabledDuration(result, first, lastOne.isBlockBilling() ? null : lastOne);
}
return result;
}
+ private void addDisabledDuration(final List<DisabledDuration> result, final BlockingState firstBlocking, @Nullable final BlockingState firstNonBlocking) {
+ final DisabledDuration lastOne;
+ if (!result.isEmpty()) {
+ lastOne = result.get(result.size() - 1);
+ } else {
+ lastOne = null;
+ }
+
+ final DateTime endDate = firstNonBlocking == null ? null : firstNonBlocking.getEffectiveDate();
+ if (lastOne != null && lastOne.getEnd().compareTo(firstBlocking.getEffectiveDate()) == 0) {
+ lastOne.setEnd(endDate);
+ } else {
+ result.add(new DisabledDuration(firstBlocking.getEffectiveDate(), endDate));
+ }
+ }
+
@VisibleForTesting
static AtomicLong getGlobalTotalOrder() {
return globaltotalOrder;
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingEventSet.java b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingEventSet.java
index 03df1e7..c55aaa3 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingEventSet.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingEventSet.java
@@ -55,10 +55,6 @@ public class DefaultBillingEventSet extends TreeSet<BillingEvent> implements Sor
this.subscriptionIdsWithAutoInvoiceOff = subscriptionIdsWithAutoInvoiceOff;
}
- public boolean isLast(final BillingEvent event) {
- return last() == event;
- }
-
@Override
public String toString() {
return "DefaultBillingEventSet [accountAutoInvoiceOff=" + accountAutoInvoiceOff
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultInternalBillingApi.java b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
index 83ef135..defc439 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
@@ -28,18 +28,18 @@ import org.slf4j.LoggerFactory;
import com.ning.billing.ObjectType;
import com.ning.billing.account.api.Account;
import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.account.api.AccountInternalApi;
import com.ning.billing.account.api.MutableAccountData;
+import com.ning.billing.callcontext.InternalCallContext;
import com.ning.billing.catalog.api.CatalogApiException;
import com.ning.billing.catalog.api.CatalogService;
-import com.ning.billing.subscription.api.SubscriptionBase;
-import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
-import com.ning.billing.callcontext.InternalCallContext;
import com.ning.billing.events.EffectiveSubscriptionInternalEvent;
-import com.ning.billing.account.api.AccountInternalApi;
-import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
import com.ning.billing.junction.BillingEvent;
import com.ning.billing.junction.BillingEventSet;
import com.ning.billing.junction.BillingInternalApi;
+import com.ning.billing.subscription.api.SubscriptionBase;
+import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
+import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
import com.ning.billing.tag.TagInternalApi;
import com.ning.billing.util.tag.ControlTagType;
import com.ning.billing.util.tag.Tag;
diff --git a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModule.java b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModule.java
index 0d4307d..b74b247 100644
--- a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModule.java
+++ b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModule.java
@@ -18,19 +18,16 @@ package com.ning.billing.junction.glue;
import org.skife.config.ConfigSource;
-import com.ning.billing.catalog.MockCatalogModule;
import com.ning.billing.entitlement.api.svcs.DefaultInternalBlockingApi;
import com.ning.billing.entitlement.block.BlockingChecker;
import com.ning.billing.entitlement.block.MockBlockingChecker;
import com.ning.billing.entitlement.dao.BlockingStateDao;
import com.ning.billing.entitlement.dao.MockBlockingStateDao;
-import com.ning.billing.mock.glue.MockAccountModule;
+import com.ning.billing.junction.BlockingInternalApi;
import com.ning.billing.mock.glue.MockEntitlementModule;
-import com.ning.billing.mock.glue.MockSubscriptionModule;
import com.ning.billing.util.glue.CacheModule;
import com.ning.billing.util.glue.CallContextModule;
import com.ning.billing.util.glue.MetricsModule;
-import com.ning.billing.junction.BlockingInternalApi;
public class TestJunctionModule extends DefaultJunctionModule {
@@ -45,10 +42,6 @@ public class TestJunctionModule extends DefaultJunctionModule {
install(new MetricsModule());
install(new CacheModule(configSource));
install(new CallContextModule());
- install(new MockAccountModule());
- install(new MockCatalogModule());
- install(new MockSubscriptionModule());
- install(new MockEntitlementModuleForJunction());
}
public class MockEntitlementModuleForJunction extends MockEntitlementModule {
@@ -67,6 +60,5 @@ public class TestJunctionModule extends DefaultJunctionModule {
public void installBlockingChecker() {
bind(BlockingChecker.class).to(MockBlockingChecker.class).asEagerSingleton();
}
-
}
}
diff --git a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleNoDB.java b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleNoDB.java
index faf6939..081d6aa 100644
--- a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleNoDB.java
+++ b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleNoDB.java
@@ -20,7 +20,10 @@ import org.skife.config.ConfigSource;
import org.skife.config.ConfigurationObjectFactory;
import com.ning.billing.GuicyKillbillTestNoDBModule;
+import com.ning.billing.catalog.MockCatalogModule;
+import com.ning.billing.mock.glue.MockAccountModule;
import com.ning.billing.mock.glue.MockNonEntityDaoModule;
+import com.ning.billing.mock.glue.MockSubscriptionModule;
import com.ning.billing.mock.glue.MockTagModule;
import com.ning.billing.notificationq.MockNotificationQueueService;
import com.ning.billing.notificationq.api.NotificationQueueConfig;
@@ -42,6 +45,10 @@ public class TestJunctionModuleNoDB extends TestJunctionModule {
install(new GuicyKillbillTestNoDBModule());
install(new MockNonEntityDaoModule());
install(new InMemoryBusModule(configSource));
+ install(new MockAccountModule());
+ install(new MockCatalogModule());
+ install(new MockSubscriptionModule());
+ install(new MockEntitlementModuleForJunction());
install(new MockTagModule());
installNotificationQueue();
}
diff --git a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleWithEmbeddedDB.java b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleWithEmbeddedDB.java
index ccac19f..32dff2d 100644
--- a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleWithEmbeddedDB.java
+++ b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleWithEmbeddedDB.java
@@ -19,9 +19,17 @@ package com.ning.billing.junction.glue;
import org.skife.config.ConfigSource;
import com.ning.billing.GuicyKillbillTestWithEmbeddedDBModule;
+import com.ning.billing.account.glue.DefaultAccountModule;
+import com.ning.billing.api.TestApiListener;
+import com.ning.billing.api.TestListenerStatus;
+import com.ning.billing.catalog.glue.CatalogModule;
+import com.ning.billing.entitlement.glue.DefaultEntitlementModule;
+import com.ning.billing.junction.JunctionTestListenerStatus;
+import com.ning.billing.subscription.glue.DefaultSubscriptionModule;
import com.ning.billing.util.glue.BusModule;
import com.ning.billing.util.glue.MetricsModule;
import com.ning.billing.util.glue.NonEntityDaoModule;
+import com.ning.billing.util.glue.NotificationQueueModule;
import com.ning.billing.util.glue.TagStoreModule;
public class TestJunctionModuleWithEmbeddedDB extends TestJunctionModule {
@@ -36,8 +44,16 @@ public class TestJunctionModuleWithEmbeddedDB extends TestJunctionModule {
install(new GuicyKillbillTestWithEmbeddedDBModule());
install(new NonEntityDaoModule());
+ install(new CatalogModule(configSource));
+ install(new DefaultAccountModule(configSource));
+ install(new DefaultEntitlementModule(configSource));
+ install(new NotificationQueueModule(configSource));
+ install(new DefaultSubscriptionModule(configSource));
install(new BusModule(configSource));
install(new MetricsModule());
install(new TagStoreModule());
+
+ bind(TestListenerStatus.class).to(JunctionTestListenerStatus.class).asEagerSingleton();
+ bind(TestApiListener.class).asEagerSingleton();
}
}
diff --git a/junction/src/test/java/com/ning/billing/junction/JunctionTestListenerStatus.java b/junction/src/test/java/com/ning/billing/junction/JunctionTestListenerStatus.java
new file mode 100644
index 0000000..a54976d
--- /dev/null
+++ b/junction/src/test/java/com/ning/billing/junction/JunctionTestListenerStatus.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2010-2013 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.junction;
+
+import javax.inject.Inject;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+
+import com.ning.billing.api.TestListenerStatus;
+
+public class JunctionTestListenerStatus implements TestListenerStatus {
+
+ private final Logger log = LoggerFactory.getLogger(JunctionTestListenerStatus.class);
+
+ private boolean isListenerFailed;
+ private String listenerFailedMsg;
+
+ @Inject
+ public JunctionTestListenerStatus() {
+ isListenerFailed = false;
+ }
+
+ @Override
+ public void failed(final String msg) {
+ this.isListenerFailed = true;
+ this.listenerFailedMsg = msg;
+ }
+
+ @Override
+ public void resetTestListenerStatus() {
+ this.isListenerFailed = false;
+ this.listenerFailedMsg = null;
+ }
+
+ public void assertListenerStatus() {
+ if (isListenerFailed) {
+ log.error(listenerFailedMsg);
+ Assert.fail(listenerFailedMsg);
+ }
+ }
+}
\ No newline at end of file
diff --git a/junction/src/test/java/com/ning/billing/junction/JunctionTestSuiteWithEmbeddedDB.java b/junction/src/test/java/com/ning/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
index 1c989e2..7f98ec3 100644
--- a/junction/src/test/java/com/ning/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
+++ b/junction/src/test/java/com/ning/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
@@ -16,34 +16,62 @@
package com.ning.billing.junction;
+import java.net.URL;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import com.ning.billing.GuicyKillbillTestSuiteWithEmbeddedDB;
+import com.ning.billing.account.api.AccountData;
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.api.TestApiListener;
+import com.ning.billing.api.TestListenerStatus;
import com.ning.billing.bus.api.PersistentBus;
+import com.ning.billing.catalog.DefaultCatalogService;
+import com.ning.billing.catalog.api.Catalog;
import com.ning.billing.catalog.api.CatalogService;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.clock.ClockMock;
+import com.ning.billing.entitlement.DefaultEntitlementService;
+import com.ning.billing.entitlement.EntitlementService;
+import com.ning.billing.entitlement.api.EntitlementApi;
import com.ning.billing.junction.glue.TestJunctionModuleWithEmbeddedDB;
-import com.ning.billing.junction.plumbing.billing.BlockingCalculator;
-import com.ning.billing.account.api.AccountInternalApi;
+import com.ning.billing.mock.MockAccountBuilder;
import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
-import com.ning.billing.tag.TagInternalApi;
-import com.ning.billing.util.tag.dao.TagDao;
+import com.ning.billing.subscription.api.SubscriptionBaseService;
+import com.ning.billing.subscription.engine.core.DefaultSubscriptionBaseService;
+import com.ning.billing.util.svcsapi.bus.BusService;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
+import com.google.inject.Stage;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
public abstract class JunctionTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWithEmbeddedDB {
+ protected static final Logger log = LoggerFactory.getLogger(JunctionTestSuiteWithEmbeddedDB.class);
+
+ // Be generous...
+ protected static final Long DELAY = 20000L;
+
@Inject
- protected AccountInternalApi accountInternalApi;
+ protected AccountUserApi accountApi;
@Inject
- protected BillingInternalApi billingInternalApi;
+ protected BlockingInternalApi blockingInternalApi;
@Inject
- protected BlockingCalculator blockingCalculator;
+ protected EntitlementApi entitlementApi;
@Inject
- protected BlockingInternalApi blockingInternalApi;
+ protected BillingInternalApi billingInternalApi;
@Inject
protected CatalogService catalogService;
@Inject
@@ -51,24 +79,143 @@ public abstract class JunctionTestSuiteWithEmbeddedDB extends GuicyKillbillTestS
@Inject
protected PersistentBus bus;
@Inject
- protected TagDao tagDao;
+ protected TestApiListener testListener;
+ @Inject
+ protected TestListenerStatus testListenerStatus;
+ @Inject
+ protected BusService busService;
@Inject
- protected TagInternalApi tagInternalApi;
+ protected SubscriptionBaseService subscriptionBaseService;
+ @Inject
+ protected EntitlementService entitlementService;
+
+ protected Catalog catalog;
+
+ private void loadSystemPropertiesFromClasspath(final String resource) {
+ final URL url = JunctionTestSuiteWithEmbeddedDB.class.getResource(resource);
+ Assert.assertNotNull(url);
+
+ configSource.merge(url);
+ }
@BeforeClass(groups = "slow")
protected void beforeClass() throws Exception {
- final Injector injector = Guice.createInjector(new TestJunctionModuleWithEmbeddedDB(configSource));
+ loadSystemPropertiesFromClasspath("/junction.properties");
+ final Injector injector = Guice.createInjector(Stage.PRODUCTION, new TestJunctionModuleWithEmbeddedDB(configSource));
injector.injectMembers(this);
}
@BeforeMethod(groups = "slow")
public void beforeMethod() throws Exception {
super.beforeMethod();
- bus.start();
+ startTestFamework();
+ this.catalog = initCatalog(catalogService);
+
+ // Make sure we start with a clean state
+ assertListenerStatus();
}
@AfterMethod(groups = "slow")
- public void afterMethod() {
- bus.stop();
+ public void afterMethod() throws Exception {
+ // Make sure we finish in a clean state
+ assertListenerStatus();
+
+ stopTestFramework();
+ }
+
+ private Catalog initCatalog(final CatalogService catalogService) throws Exception {
+ ((DefaultCatalogService) catalogService).loadCatalog();
+ final Catalog catalog = catalogService.getFullCatalog();
+ assertNotNull(catalog);
+ return catalog;
+ }
+
+ private void startTestFamework() throws Exception {
+ log.debug("STARTING TEST FRAMEWORK");
+
+ resetTestListener(testListener, testListenerStatus);
+
+ resetClockToStartOfTest(clock);
+
+ startBusAndRegisterListener(busService, testListener);
+
+ restartSubscriptionService(subscriptionBaseService);
+ restartEntitlementService(entitlementService);
+
+ log.debug("STARTED TEST FRAMEWORK");
+ }
+
+ private void stopTestFramework() throws Exception {
+ log.debug("STOPPING TEST FRAMEWORK");
+ stopBusAndUnregisterListener(busService, testListener);
+ stopSubscriptionService(subscriptionBaseService);
+ stopEntitlementService(entitlementService);
+ log.debug("STOPPED TEST FRAMEWORK");
+ }
+
+ private void resetTestListener(final TestApiListener testListener, final TestListenerStatus testListenerStatus) {
+ // RESET LIST OF EXPECTED EVENTS
+ if (testListener != null) {
+ testListener.reset();
+ testListenerStatus.resetTestListenerStatus();
+ }
+ }
+
+ private void resetClockToStartOfTest(final ClockMock clock) {
+ clock.resetDeltaFromReality();
+
+ // Date at which all tests start-- we create the date object here after the system properties which set the JVM in UTC have been set.
+ final DateTime testStartDate = new DateTime(2012, 5, 7, 0, 3, 42, 0);
+ clock.setDeltaFromReality(testStartDate.getMillis() - clock.getUTCNow().getMillis());
+ }
+
+ private void startBusAndRegisterListener(final BusService busService, final TestApiListener testListener) throws Exception {
+ busService.getBus().start();
+ busService.getBus().register(testListener);
+ }
+
+ private void restartSubscriptionService(final SubscriptionBaseService subscriptionBaseService) {
+ // START NOTIFICATION QUEUE FOR SUBSCRIPTION
+ ((DefaultSubscriptionBaseService) subscriptionBaseService).initialize();
+ ((DefaultSubscriptionBaseService) subscriptionBaseService).start();
+ }
+
+ private void restartEntitlementService(final EntitlementService entitlementService) {
+ // START NOTIFICATION QUEUE FOR ENTITLEMENT
+ ((DefaultEntitlementService) entitlementService).initialize();
+ ((DefaultEntitlementService) entitlementService).start();
+ }
+
+ private void stopBusAndUnregisterListener(final BusService busService, final TestApiListener testListener) throws Exception {
+ busService.getBus().unregister(testListener);
+ busService.getBus().stop();
+ }
+
+ private void stopSubscriptionService(final SubscriptionBaseService subscriptionBaseService) throws Exception {
+ ((DefaultSubscriptionBaseService) subscriptionBaseService).stop();
+ }
+
+ private void stopEntitlementService(final EntitlementService entitlementService) throws Exception {
+ ((DefaultEntitlementService) entitlementService).stop();
+ }
+
+ protected AccountData getAccountData(final int billingDay) {
+ return new MockAccountBuilder().name(UUID.randomUUID().toString().substring(1, 8))
+ .firstNameLength(6)
+ .email(UUID.randomUUID().toString().substring(1, 8))
+ .phone(UUID.randomUUID().toString().substring(1, 8))
+ .migrated(false)
+ .isNotifiedForInvoices(false)
+ .externalKey(UUID.randomUUID().toString().substring(1, 8))
+ .billingCycleDayLocal(billingDay)
+ .currency(Currency.USD)
+ .paymentMethodId(UUID.randomUUID())
+ .timeZone(DateTimeZone.UTC)
+ .build();
+ }
+
+ protected void assertListenerStatus() {
+ assertTrue(testListener.isCompleted(DELAY));
+ ((JunctionTestListenerStatus) testListenerStatus).assertListenerStatus();
}
}
diff --git a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillingApi.java b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillingApi.java
index cd4ac59..c2354e9 100644
--- a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillingApi.java
+++ b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillingApi.java
@@ -181,11 +181,9 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
final Account account = createAccount(32);
- final List<BlockingState> blockingStates = new ArrayList<BlockingState>();
- blockingStates.add(new DefaultBlockingState(bunId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)));
- blockingStates.add(new DefaultBlockingState(bunId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(2)));
+ blockingStateDao.setBlockingState(new DefaultBlockingState(bunId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)), clock, internalCallContext);
+ blockingStateDao.setBlockingState(new DefaultBlockingState(bunId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(2)), clock, internalCallContext);
- ((MockBlockingStateDao) blockingStateDao).setBlockingStates(bunId, blockingStates);
final SortedSet<BillingEvent> events = billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), internalCallContext);
Assert.assertEquals(events.size(), 3);
diff --git a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java
index 409ce5f..dbf0294 100644
--- a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java
+++ b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java
@@ -16,6 +16,22 @@
package com.ning.billing.junction.plumbing.billing;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+import org.mockito.Mockito;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
import com.ning.billing.account.api.Account;
import com.ning.billing.catalog.MockPlan;
import com.ning.billing.catalog.MockPlanPhase;
@@ -23,31 +39,16 @@ import com.ning.billing.catalog.api.BillingPeriod;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.entitlement.api.BlockingState;
import com.ning.billing.entitlement.api.BlockingStateType;
import com.ning.billing.entitlement.dao.MockBlockingStateDao;
-import com.ning.billing.junction.JunctionTestSuiteNoDB;
-import com.ning.billing.entitlement.api.BlockingState;
-import com.ning.billing.junction.plumbing.billing.BlockingCalculator.DisabledDuration;
-import com.ning.billing.subscription.api.SubscriptionBaseTransitionType;
-import com.ning.billing.subscription.api.SubscriptionBase;
import com.ning.billing.junction.BillingEvent;
import com.ning.billing.junction.BillingModeType;
import com.ning.billing.junction.DefaultBlockingState;
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
-import org.joda.time.LocalDate;
-import org.mockito.Mockito;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.List;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import java.util.UUID;
+import com.ning.billing.junction.JunctionTestSuiteNoDB;
+import com.ning.billing.junction.plumbing.billing.BlockingCalculator.DisabledDuration;
+import com.ning.billing.subscription.api.SubscriptionBase;
+import com.ning.billing.subscription.api.SubscriptionBaseTransitionType;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
@@ -114,9 +115,9 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
final List<BlockingState> blockingStates = new ArrayList<BlockingState>();
blockingStates.add(new DefaultBlockingState(bundleId1, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now));
- blockingStates.add(new DefaultBlockingState(bundleId1, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(2)));
+ blockingStates.add(new DefaultBlockingState(bundleId1, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(2)));
- setBlockingStates(bundleId1, blockingStates);
+ setBlockingStates(blockingStates);
blockingCalculator.insertBlockingEvents(billingEvents, internalCallContext);
@@ -516,7 +517,6 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
assertNotNull(minus5andAHalf);
assertEquals(minus5andAHalf.getEffectiveDate(), now.minusDays(6));
-
}
protected BillingEvent createRealEvent(final DateTime effectiveDate, final SubscriptionBase subscription) {
@@ -538,9 +538,9 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
final DateTimeZone tz = DateTimeZone.UTC;
return new DefaultBillingEvent(account, subscription, effectiveDate, plan, planPhase,
- fixedPrice, recurringPrice, currency,
- billingPeriod, billCycleDay, billingModeType,
- description, totalOrdering, type, tz);
+ fixedPrice, recurringPrice, currency,
+ billingPeriod, billCycleDay, billingModeType,
+ description, totalOrdering, type, tz);
}
@Test(groups = "fast")
@@ -610,7 +610,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
public MockBillingEvent() {
super(account, subscription1, clock.getUTCNow(), null, null, BigDecimal.ZERO, BigDecimal.TEN, Currency.USD, BillingPeriod.ANNUAL,
- 4, BillingModeType.IN_ADVANCE, "", 3L, SubscriptionBaseTransitionType.CREATE, DateTimeZone.UTC);
+ 4, BillingModeType.IN_ADVANCE, "", 3L, SubscriptionBaseTransitionType.CREATE, DateTimeZone.UTC);
}
}
@@ -642,9 +642,10 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
public void testCreateDisablePairs() {
List<BlockingState> blockingEvents;
final UUID ovdId = UUID.randomUUID();
+ final UUID ovdId2 = UUID.randomUUID();
final DateTime now = clock.getUTCNow();
- //simple events open clear -> disabled
+ // Simple events open clear -> disabled
blockingEvents = new ArrayList<BlockingState>();
blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now));
blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)));
@@ -655,7 +656,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
assertEquals(pairs.get(0).getStart(), now.plusDays(1));
assertNull(pairs.get(0).getEnd());
- //simple events closed clear -> disabled
+ // Simple events closed clear -> disabled
blockingEvents = new ArrayList<BlockingState>();
blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now));
blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)));
@@ -668,7 +669,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
assertNotNull(pairs.get(0).getEnd());
assertEquals(pairs.get(0).getEnd(), now.plusDays(2));
- //simple BUNDLE events closed clear -> disabled
+ // Simple BUNDLE events closed clear -> disabled
blockingEvents = new ArrayList<BlockingState>();
blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now));
blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)));
@@ -681,7 +682,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
assertNotNull(pairs.get(0).getEnd());
assertEquals(pairs.get(0).getEnd(), now.plusDays(2));
- //two or more disableds in a row
+ // Two or more disabled in a row
blockingEvents = new ArrayList<BlockingState>();
blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now));
blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)));
@@ -708,6 +709,30 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
assertEquals(pairs.get(0).getStart(), now.plusDays(1));
assertNotNull(pairs.get(0).getEnd());
assertEquals(pairs.get(0).getEnd(), now.plusDays(4));
+
+ // Verify ordering at the same effective date doesn't matter. This is to work around nondeterministic ordering
+ // behavior in ProxyBlockingStateDao#BLOCKING_STATE_ORDERING. See also TestDefaultInternalBillingApi.
+ blockingEvents = new ArrayList<BlockingState>();
+ blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)));
+ blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(2)));
+ blockingEvents.add(new DefaultBlockingState(ovdId2, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(2)));
+ blockingEvents.add(new DefaultBlockingState(ovdId2, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(3)));
+
+ pairs = blockingCalculator.createBlockingDurations(blockingEvents);
+ assertEquals(pairs.size(), 1);
+ assertEquals(pairs.get(0).getStart(), now.plusDays(1));
+ assertEquals(pairs.get(0).getEnd(), now.plusDays(3));
+
+ blockingEvents = new ArrayList<BlockingState>();
+ blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)));
+ blockingEvents.add(new DefaultBlockingState(ovdId2, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(2)));
+ blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(2)));
+ blockingEvents.add(new DefaultBlockingState(ovdId2, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(3)));
+
+ pairs = blockingCalculator.createBlockingDurations(blockingEvents);
+ assertEquals(pairs.size(), 1);
+ assertEquals(pairs.get(0).getStart(), now.plusDays(1));
+ assertEquals(pairs.get(0).getEnd(), now.plusDays(3));
}
@Test(groups = "fast")
@@ -728,7 +753,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, new LocalDate(2012, 7, 25).toDateTimeAtStartOfDay(DateTimeZone.UTC)));
blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, new LocalDate(2012, 7, 25).toDateTimeAtStartOfDay(DateTimeZone.UTC)));
- setBlockingStates(bundleId1, blockingEvents);
+ setBlockingStates(blockingEvents);
blockingCalculator.insertBlockingEvents(billingEvents, internalCallContext);
@@ -746,7 +771,9 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
assertEquals(events.get(4).getTransitionType(), SubscriptionBaseTransitionType.CHANGE);
}
- private void setBlockingStates(final UUID blockedId, final List<BlockingState> blockingStates) {
- ((MockBlockingStateDao) blockingStateDao).setBlockingStates(blockedId, blockingStates);
+ private void setBlockingStates(final List<BlockingState> blockingStates) {
+ for (final BlockingState blockingState : blockingStates) {
+ blockingStateDao.setBlockingState(blockingState, clock, internalCallContext);
+ }
}
}
diff --git a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
new file mode 100644
index 0000000..99ed652
--- /dev/null
+++ b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2010-2013 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.junction.plumbing.billing;
+
+import java.util.List;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.api.TestApiListener.NextEvent;
+import com.ning.billing.catalog.api.BillingPeriod;
+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.EntitlementService;
+import com.ning.billing.entitlement.api.BlockingStateType;
+import com.ning.billing.entitlement.api.DefaultEntitlementApi;
+import com.ning.billing.entitlement.api.Entitlement;
+import com.ning.billing.junction.BillingEvent;
+import com.ning.billing.junction.DefaultBlockingState;
+import com.ning.billing.junction.JunctionTestSuiteWithEmbeddedDB;
+import com.ning.billing.subscription.api.SubscriptionBase;
+import com.ning.billing.subscription.api.SubscriptionBaseTransitionType;
+
+import com.google.common.collect.ImmutableList;
+
+public class TestDefaultInternalBillingApi extends JunctionTestSuiteWithEmbeddedDB {
+
+ // See https://github.com/killbill/killbill/issues/123
+ //
+ // Pierre, why do we have invocationCount > 0 here?
+ //
+ // This test will exercise ProxyBlockingStateDao#BLOCKING_STATE_ORDERING - unfortunately, for some reason,
+ // the ordering doesn't seem deterministic. In some scenarii,
+ // BlockingState(idA, effectiveDate1, BLOCK), BlockingState(idA, effectiveDate2, CLEAR), BlockingState(idB, effectiveDate2, BLOCK), BlockingState(idB, effectiveDate3, CLEAR)
+ // is ordered
+ // BlockingState(idA, effectiveDate1, BLOCK), BlockingState(idB, effectiveDate2, BLOCK), BlockingState(idA, effectiveDate2, CLEAR), BlockingState(idB, effectiveDate3, CLEAR)
+ // The code BlockingCalculator#createBlockingDurations has been updated to support it, but we still want to make sure it actually works in both scenarii
+ // (invocationCount = 10 will trigger both usecases in my testing).
+ @Test(groups = "slow", description = "Check blocking states with same effective date are correctly handled", invocationCount = 10)
+ public void testBlockingStatesWithSameEffectiveDate() throws Exception {
+ final LocalDate initialDate = new LocalDate(2013, 8, 7);
+ clock.setDay(initialDate);
+
+ final Account account = accountApi.createAccount(getAccountData(7), callContext);
+ testListener.pushExpectedEvent(NextEvent.CREATE);
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+ final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), initialDate, callContext);
+ final SubscriptionBase subscription = subscriptionInternalApi.getSubscriptionFromId(entitlement.getId(), internalCallContext);
+ assertListenerStatus();
+
+ final DateTime block1Date = clock.getUTCNow();
+ testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.BLOCK);
+ final DefaultBlockingState state1 = new DefaultBlockingState(account.getId(),
+ BlockingStateType.ACCOUNT,
+ DefaultEntitlementApi.ENT_STATE_BLOCKED,
+ EntitlementService.ENTITLEMENT_SERVICE_NAME,
+ true,
+ true,
+ true,
+ block1Date);
+ blockingInternalApi.setBlockingState(state1, internalCallContext);
+ // Same date, we'll order by record id asc
+ final DefaultBlockingState state2 = new DefaultBlockingState(account.getId(),
+ BlockingStateType.ACCOUNT,
+ DefaultEntitlementApi.ENT_STATE_CLEAR,
+ EntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false,
+ false,
+ false,
+ block1Date);
+ blockingInternalApi.setBlockingState(state2, internalCallContext);
+ assertListenerStatus();
+
+ clock.addDays(5);
+
+ final DateTime block2Date = clock.getUTCNow();
+ testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.BLOCK);
+ final DefaultBlockingState state3 = new DefaultBlockingState(entitlement.getBundleId(),
+ BlockingStateType.SUBSCRIPTION_BUNDLE,
+ DefaultEntitlementApi.ENT_STATE_BLOCKED,
+ EntitlementService.ENTITLEMENT_SERVICE_NAME,
+ true,
+ true,
+ true,
+ block2Date);
+ blockingInternalApi.setBlockingState(state3, internalCallContext);
+ // Same date, we'll order by record id asc
+ final DefaultBlockingState state4 = new DefaultBlockingState(entitlement.getBundleId(),
+ BlockingStateType.SUBSCRIPTION_BUNDLE,
+ DefaultEntitlementApi.ENT_STATE_CLEAR,
+ EntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false,
+ false,
+ false,
+ block2Date);
+ blockingInternalApi.setBlockingState(state4, internalCallContext);
+ assertListenerStatus();
+
+ final DateTime block3Date = block2Date.plusDays(3);
+
+ // Pass the phase
+ testListener.pushExpectedEvent(NextEvent.PHASE);
+ clock.addDays(50);
+ assertListenerStatus();
+
+ final DateTime block4Date = clock.getUTCNow();
+ final DateTime block5Date = block4Date.plusDays(3);
+ // Only one event on the bus (for state5)
+ testListener.pushExpectedEvents(NextEvent.BLOCK);
+ // Insert the clear state first, to make sure the order in which we insert blocking states doesn't matter
+ // Since we are already in an ENT_STATE_CLEAR state for service ENTITLEMENT_SERVICE_NAME, we need to use a different
+ // state name to simulate this behavior (otherwise, by design, this event won't be created)
+ final DefaultBlockingState state6 = new DefaultBlockingState(entitlement.getBundleId(),
+ BlockingStateType.SUBSCRIPTION_BUNDLE,
+ DefaultEntitlementApi.ENT_STATE_CLEAR + "-something",
+ EntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false,
+ false,
+ false,
+ block5Date);
+ blockingInternalApi.setBlockingState(state6, internalCallContext);
+ final DefaultBlockingState state5 = new DefaultBlockingState(entitlement.getBundleId(),
+ BlockingStateType.SUBSCRIPTION_BUNDLE,
+ DefaultEntitlementApi.ENT_STATE_BLOCKED + "-something",
+ EntitlementService.ENTITLEMENT_SERVICE_NAME,
+ true,
+ true,
+ true,
+ block4Date);
+ blockingInternalApi.setBlockingState(state5, internalCallContext);
+ assertListenerStatus();
+
+ // Now, add back blocking states at an earlier date, for a different blockable id, to make sure the effective
+ // date ordering is correctly respected when computing blocking durations
+ testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.BLOCK);
+ final DefaultBlockingState state7 = new DefaultBlockingState(account.getId(),
+ BlockingStateType.ACCOUNT,
+ DefaultEntitlementApi.ENT_STATE_BLOCKED + "-something2",
+ EntitlementService.ENTITLEMENT_SERVICE_NAME,
+ true,
+ true,
+ true,
+ block3Date);
+ blockingInternalApi.setBlockingState(state7, internalCallContext);
+ final DefaultBlockingState state8 = new DefaultBlockingState(account.getId(),
+ BlockingStateType.ACCOUNT,
+ DefaultEntitlementApi.ENT_STATE_CLEAR + "-something2",
+ EntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false,
+ false,
+ false,
+ block4Date);
+ blockingInternalApi.setBlockingState(state8, internalCallContext);
+ assertListenerStatus();
+
+ // Advance for state6 to be active
+ testListener.pushExpectedEvents(NextEvent.BLOCK);
+ clock.addDays(5);
+ assertListenerStatus();
+
+ // Expected blocking durations:
+ // * 2013-08-07 to 2013-08-07 (block1Date)
+ // * 2013-08-12 to 2013-08-12 (block2Date)
+ // * 2013-08-15 to 2013-10-04 [2013-08-15 to 2013-10-01 (block3Date -> block4Date) and 2013-10-01 to 2013-10-04 (block4Date -> block5Date)]
+ final List<BillingEvent> events = ImmutableList.<BillingEvent>copyOf(billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), internalCallContext));
+ Assert.assertEquals(events.size(), 7);
+ Assert.assertEquals(events.get(0).getTransitionType(), SubscriptionBaseTransitionType.CREATE);
+ Assert.assertEquals(events.get(0).getEffectiveDate(), subscription.getStartDate());
+ Assert.assertEquals(events.get(1).getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
+ Assert.assertEquals(events.get(1).getEffectiveDate(), block1Date);
+ Assert.assertEquals(events.get(2).getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
+ Assert.assertEquals(events.get(2).getEffectiveDate(), block1Date);
+ Assert.assertEquals(events.get(3).getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
+ Assert.assertEquals(events.get(3).getEffectiveDate(), block2Date);
+ Assert.assertEquals(events.get(4).getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
+ Assert.assertEquals(events.get(4).getEffectiveDate(), block2Date);
+ Assert.assertEquals(events.get(5).getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
+ Assert.assertEquals(events.get(5).getEffectiveDate(), block3Date);
+ Assert.assertEquals(events.get(6).getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
+ Assert.assertEquals(events.get(6).getEffectiveDate(), block5Date);
+ }
+}
junction/src/test/resources/catalog.xml 828(+828 -0)
diff --git a/junction/src/test/resources/catalog.xml b/junction/src/test/resources/catalog.xml
new file mode 100644
index 0000000..973afd0
--- /dev/null
+++ b/junction/src/test/resources/catalog.xml
@@ -0,0 +1,828 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- ~ 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. -->
+
+<!-- Use cases covered so far: Tiered Product (Pistol/Shotgun/Assault-Rifle)
+ Multiple changeEvent plan policies Multiple PlanAlignment (see below, trial
+ add-on alignments and rescue discount package) Product transition rules Add
+ on (Scopes, Hoster) Multi-pack addon (Extra-Ammo) Addon Trial aligned to
+ base plan (holster-monthly-regular) Addon Trial aligned to creation (holster-monthly-special)
+ Rescue discount package (assault-rifle-annual-rescue) Plan phase with a reccurring
+ and a one off (refurbish-maintenance) Phan with more than 2 phase (gunclub
+ discount plans) Use Cases to do: Tiered Add On Riskfree period -->
+<catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
+
+ <effectiveDate>2011-01-01T00:00:00+00:00</effectiveDate>
+ <catalogName>Firearms</catalogName>
+
+ <currencies>
+ <currency>USD</currency>
+ <currency>EUR</currency>
+ <currency>GBP</currency>
+ </currencies>
+
+ <products>
+ <product name="Pistol">
+ <category>BASE</category>
+ </product>
+ <product name="Shotgun">
+ <category>BASE</category>
+ <available>
+ <addonProduct>Telescopic-Scope</addonProduct>
+ <addonProduct>Laser-Scope</addonProduct>
+ </available>
+ </product>
+ <product name="Assault-Rifle">
+ <category>BASE</category>
+ <included>
+ <addonProduct>Telescopic-Scope</addonProduct>
+ </included>
+ <available>
+ <addonProduct>Laser-Scope</addonProduct>
+ </available>
+ </product>
+ <product name="Telescopic-Scope">
+ <category>ADD_ON</category>
+ </product>
+ <product name="Laser-Scope">
+ <category>ADD_ON</category>
+ </product>
+ <product name="Holster">
+ <category>ADD_ON</category>
+ </product>
+ <product name="Extra-Ammo">
+ <category>ADD_ON</category>
+ </product>
+ <product name="Refurbish-Maintenance">
+ <category>ADD_ON</category>
+ </product>
+ </products>
+
+ <rules>
+ <changePolicy>
+ <changePolicyCase>
+ <phaseType>TRIAL</phaseType>
+ <policy>IMMEDIATE</policy>
+ </changePolicyCase>
+ <changePolicyCase>
+ <toPriceList>rescue</toPriceList>
+ <policy>END_OF_TERM</policy>
+ </changePolicyCase>
+ <changePolicyCase>
+ <toProduct>Assault-Rifle</toProduct>
+ <policy>IMMEDIATE</policy>
+ </changePolicyCase>
+ <changePolicyCase>
+ <fromProduct>Pistol</fromProduct>
+ <toProduct>Shotgun</toProduct>
+ <policy>IMMEDIATE</policy>
+ </changePolicyCase>
+ <changePolicyCase>
+ <fromBillingPeriod>MONTHLY</fromBillingPeriod>
+ <toBillingPeriod>ANNUAL</toBillingPeriod>
+ <policy>IMMEDIATE</policy>
+ </changePolicyCase>
+ <changePolicyCase>
+ <fromBillingPeriod>ANNUAL</fromBillingPeriod>
+ <toBillingPeriod>MONTHLY</toBillingPeriod>
+ <policy>END_OF_TERM</policy>
+ </changePolicyCase>
+ <changePolicyCase>
+ <policy>END_OF_TERM</policy>
+ </changePolicyCase>
+ </changePolicy>
+ <changeAlignment>
+ <changeAlignmentCase>
+ <toPriceList>rescue</toPriceList>
+ <alignment>CHANGE_OF_PLAN</alignment>
+ </changeAlignmentCase>
+ <changeAlignmentCase>
+ <fromPriceList>rescue</fromPriceList>
+ <toPriceList>rescue</toPriceList>
+ <alignment>CHANGE_OF_PRICELIST</alignment>
+ </changeAlignmentCase>
+ <changeAlignmentCase>
+ <alignment>START_OF_SUBSCRIPTION</alignment>
+ </changeAlignmentCase>
+ </changeAlignment>
+ <cancelPolicy>
+ <cancelPolicyCase>
+ <phaseType>TRIAL</phaseType>
+ <policy>IMMEDIATE</policy>
+ </cancelPolicyCase>
+ <cancelPolicyCase>
+ <policy>END_OF_TERM</policy>
+ </cancelPolicyCase>
+ </cancelPolicy>
+ <createAlignment>
+ <createAlignmentCase>
+ <product>Laser-Scope</product>
+ <alignment>START_OF_SUBSCRIPTION</alignment>
+ </createAlignmentCase>
+ <createAlignmentCase>
+ <alignment>START_OF_BUNDLE</alignment>
+ </createAlignmentCase>
+ </createAlignment>
+ <billingAlignment>
+ <billingAlignmentCase>
+ <productCategory>ADD_ON</productCategory>
+ <alignment>BUNDLE</alignment>
+ </billingAlignmentCase>
+ <billingAlignmentCase>
+ <billingPeriod>ANNUAL</billingPeriod>
+ <alignment>SUBSCRIPTION</alignment>
+ </billingAlignmentCase>
+ <billingAlignmentCase>
+ <alignment>ACCOUNT</alignment>
+ </billingAlignmentCase>
+ </billingAlignment>
+ <priceList>
+ <priceListCase>
+ <fromPriceList>rescue</fromPriceList>
+ <toPriceList>DEFAULT</toPriceList>
+ </priceListCase>
+ </priceList>
+ </rules>
+
+ <plans>
+ <plan name="pistol-monthly">
+ <product>Pistol</product>
+ <initialPhases>
+ <phase type="TRIAL">
+ <duration>
+ <unit>DAYS</unit>
+ <number>30</number>
+ </duration>
+ <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+ <fixedPrice> <!-- empty price implies $0 -->
+ </fixedPrice>
+ </phase>
+ </initialPhases>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ </duration>
+ <billingPeriod>MONTHLY</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>GBP</currency>
+ <value>29.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>29.95</value>
+ </price>
+ <price>
+ <currency>USD</currency>
+ <value>29.95</value>
+ </price>
+ </recurringPrice>
+ </finalPhase>
+ </plan>
+ <plan name="shotgun-monthly">
+ <product>Shotgun</product>
+ <initialPhases>
+ <phase type="TRIAL">
+ <duration>
+ <unit>DAYS</unit>
+ <number>30</number>
+ </duration>
+ <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+ <fixedPrice></fixedPrice>
+ <!-- no price implies $0 -->
+ </phase>
+ </initialPhases>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ <number>-1</number>
+ </duration>
+ <billingPeriod>MONTHLY</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>249.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>149.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>169.95</value>
+ </price>
+ </recurringPrice>
+ </finalPhase>
+ </plan>
+ <plan name="assault-rifle-monthly">
+ <product>Assault-Rifle</product>
+ <initialPhases>
+ <phase type="TRIAL">
+ <duration>
+ <unit>DAYS</unit>
+ <number>30</number>
+ </duration>
+ <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+ <fixedPrice>
+ </fixedPrice>
+ </phase>
+ </initialPhases>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ </duration>
+ <billingPeriod>MONTHLY</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>599.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>349.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>399.95</value>
+ </price>
+ </recurringPrice>
+ </finalPhase>
+ </plan>
+ <plan name="pistol-annual">
+ <product>Pistol</product>
+ <initialPhases>
+ <phase type="TRIAL">
+ <duration>
+ <unit>DAYS</unit>
+ <number>30</number>
+ </duration>
+ <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+ <fixedPrice>
+ </fixedPrice>
+ </phase>
+ </initialPhases>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ </duration>
+ <billingPeriod>ANNUAL</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>199.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>199.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>199.95</value>
+ </price>
+ </recurringPrice>
+ </finalPhase>
+ </plan>
+ <plan name="shotgun-annual">
+ <product>Shotgun</product>
+ <initialPhases>
+ <phase type="TRIAL">
+ <duration>
+ <unit>DAYS</unit>
+ <number>30</number>
+ </duration>
+ <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+ <fixedPrice>
+ </fixedPrice>
+ </phase>
+ </initialPhases>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ </duration>
+ <billingPeriod>ANNUAL</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>2399.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>1499.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>1699.95</value>
+ </price>
+ </recurringPrice>
+ </finalPhase>
+ </plan>
+ <plan name="assault-rifle-annual">
+ <product>Assault-Rifle</product>
+ <initialPhases>
+ <phase type="TRIAL">
+ <duration>
+ <unit>DAYS</unit>
+ <number>30</number>
+ </duration>
+ <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+ <fixedPrice>
+ </fixedPrice>
+ </phase>
+ </initialPhases>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ </duration>
+ <billingPeriod>ANNUAL</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>5999.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>3499.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>3999.95</value>
+ </price>
+ </recurringPrice>
+ </finalPhase>
+ </plan>
+ <plan name="pistol-annual-gunclub-discount">
+ <product>Pistol</product>
+ <initialPhases>
+ <phase type="TRIAL">
+ <duration>
+ <unit>DAYS</unit>
+ <number>30</number>
+ </duration>
+ <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+ <fixedPrice>
+ </fixedPrice>
+ </phase>
+ <phase type="DISCOUNT">
+ <duration>
+ <unit>MONTHS</unit>
+ <number>6</number>
+ </duration>
+ <billingPeriod>MONTHLY</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>9.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>9.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>9.95</value>
+ </price>
+ </recurringPrice>
+ </phase>
+ </initialPhases>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ </duration>
+ <billingPeriod>ANNUAL</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>199.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>199.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>199.95</value>
+ </price>
+ </recurringPrice>
+ </finalPhase>
+ </plan>
+ <plan name="shotgun-annual-gunclub-discount">
+ <product>Shotgun</product>
+ <initialPhases>
+ <phase type="TRIAL">
+ <duration>
+ <unit>DAYS</unit>
+ <number>30</number>
+ </duration>
+ <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+ <fixedPrice>
+ </fixedPrice>
+ </phase>
+ <phase type="DISCOUNT">
+ <duration>
+ <unit>MONTHS</unit>
+ <number>6</number>
+ </duration>
+ <billingPeriod>MONTHLY</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>19.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>49.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>69.95</value>
+ </price>
+ </recurringPrice>
+ </phase>
+ </initialPhases>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ </duration>
+ <billingPeriod>ANNUAL</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>2399.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>1499.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>1699.95</value>
+ </price>
+ </recurringPrice>
+ </finalPhase>
+ </plan>
+ <plan name="assault-rifle-annual-gunclub-discount">
+ <product>Assault-Rifle</product>
+ <initialPhases>
+ <phase type="TRIAL">
+ <duration>
+ <unit>DAYS</unit>
+ <number>30</number>
+ </duration>
+ <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+ <fixedPrice>
+ </fixedPrice>
+ </phase>
+ <phase type="DISCOUNT">
+ <duration>
+ <unit>MONTHS</unit>
+ <number>6</number>
+ </duration>
+ <billingPeriod>MONTHLY</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>99.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>99.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>99.95</value>
+ </price>
+ </recurringPrice>
+ </phase>
+ </initialPhases>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ </duration>
+ <billingPeriod>ANNUAL</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>5999.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>3499.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>3999.95</value>
+ </price>
+ </recurringPrice>
+ </finalPhase>
+ </plan>
+ <plan name="laser-scope-monthly">
+ <product>Laser-Scope</product>
+ <initialPhases>
+ <phase type="DISCOUNT">
+ <duration>
+ <unit>MONTHS</unit>
+ <number>1</number>
+ </duration>
+ <billingPeriod>MONTHLY</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>999.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>499.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>999.95</value>
+ </price>
+ </recurringPrice>
+ </phase>
+ </initialPhases>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ </duration>
+ <billingPeriod>MONTHLY</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>1999.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>1499.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>1999.95</value>
+ </price>
+ </recurringPrice>
+ </finalPhase>
+ </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>
+ </duration>
+ <billingPeriod>MONTHLY</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>999.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>499.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>999.95</value>
+ </price>
+ </recurringPrice>
+ </finalPhase>
+ </plan>
+ <plan name="extra-ammo-monthly">
+ <product>Extra-Ammo</product>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ </duration>
+ <billingPeriod>MONTHLY</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>999.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>499.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>999.95</value>
+ </price>
+ </recurringPrice>
+ </finalPhase>
+ <plansAllowedInBundle>-1</plansAllowedInBundle> <!-- arbitrary number of these (multipack) -->
+ </plan>
+ <plan name="holster-monthly-regular">
+ <product>Holster</product>
+ <initialPhases>
+ <phase type="TRIAL">
+ <duration>
+ <unit>DAYS</unit>
+ <number>30</number>
+ </duration>
+ <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+ <fixedPrice>
+ </fixedPrice>
+ </phase>
+ </initialPhases>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ </duration>
+ <billingPeriod>ANNUAL</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>199.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>199.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>199.95</value>
+ </price>
+ </recurringPrice>
+ </finalPhase>
+ </plan>
+ <plan name="holster-monthly-special">
+ <product>Holster</product>
+ <initialPhases>
+ <phase type="TRIAL">
+ <duration>
+ <unit>DAYS</unit>
+ <number>30</number>
+ </duration>
+ <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+ <fixedPrice>
+ </fixedPrice>
+ </phase>
+ </initialPhases>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ </duration>
+ <billingPeriod>ANNUAL</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>199.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>199.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>199.95</value>
+ </price>
+ </recurringPrice>
+ </finalPhase>
+ </plan>
+ <plan name="assault-rifle-annual-rescue">
+ <product>Assault-Rifle</product>
+ <initialPhases>
+ <phase type="DISCOUNT">
+ <duration>
+ <unit>YEARS</unit>
+ <number>1</number>
+ </duration>
+ <billingPeriod>ANNUAL</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>5999.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>3499.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>3999.95</value>
+ </price>
+ </recurringPrice>
+ </phase>
+ </initialPhases>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ </duration>
+ <billingPeriod>ANNUAL</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>5999.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>3499.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>3999.95</value>
+ </price>
+ </recurringPrice>
+ </finalPhase>
+ </plan>
+ <plan name="refurbish-maintenance">
+ <product>Refurbish-Maintenance</product>
+ <finalPhase type="FIXEDTERM">
+ <duration>
+ <unit>MONTHS</unit>
+ <number>12</number>
+ </duration>
+ <billingPeriod>MONTHLY</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>199.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>199.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>199.95</value>
+ </price>
+ </recurringPrice>
+ <fixedPrice>
+ <price>
+ <currency>USD</currency>
+ <value>599.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>599.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>599.95</value>
+ </price>
+ </fixedPrice>
+ </finalPhase>
+ </plan>
+ </plans>
+ <priceLists>
+ <defaultPriceList name="DEFAULT">
+ <plans>
+ <plan>pistol-monthly</plan>
+ <plan>shotgun-monthly</plan>
+ <plan>assault-rifle-monthly</plan>
+ <plan>pistol-annual</plan>
+ <plan>shotgun-annual</plan>
+ <plan>assault-rifle-annual</plan>
+ <plan>laser-scope-monthly</plan>
+ <plan>telescopic-scope-monthly</plan>
+ <plan>extra-ammo-monthly</plan>
+ <plan>holster-monthly-regular</plan>
+ <plan>refurbish-maintenance</plan>
+ </plans>
+ </defaultPriceList>
+ <childPriceList name="gunclubDiscount">
+ <plans>
+ <plan>pistol-monthly</plan>
+ <plan>shotgun-monthly</plan>
+ <plan>assault-rifle-monthly</plan>
+ <plan>pistol-annual-gunclub-discount</plan>
+ <plan>shotgun-annual-gunclub-discount</plan>
+ <plan>assault-rifle-annual-gunclub-discount</plan>
+ <plan>holster-monthly-special</plan>
+ </plans>
+ </childPriceList>
+ <childPriceList name="rescue">
+ <plans>
+ <plan>assault-rifle-annual-rescue</plan>
+ </plans>
+ </childPriceList>
+ </priceLists>
+
+</catalog>
diff --git a/overdue/src/test/java/com/ning/billing/overdue/glue/ApplicatorMockJunctionModule.java b/overdue/src/test/java/com/ning/billing/overdue/glue/ApplicatorMockJunctionModule.java
index 12625ab..1d4bf63 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/glue/ApplicatorMockJunctionModule.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/glue/ApplicatorMockJunctionModule.java
@@ -54,7 +54,7 @@ public class ApplicatorMockJunctionModule extends AbstractModule {
}
@Override
- public List<BlockingState> getBlockingAll(final UUID blockableId, final BlockingStateType blockingStateType, final InternalTenantContext context) {
+ public List<BlockingState> getBlockingAllForAccount(final InternalTenantContext context) {
throw new UnsupportedOperationException();
}