killbill-aplcache
Changes
api/src/main/java/com/ning/billing/entitlement/api/repair/EntitlementRepairException.java 41(+41 -0)
beatrix/src/test/resources/catalogSample.xml 585(+395 -190)
entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultChargeThruApi.java 21(+6 -15)
entitlement/src/main/java/com/ning/billing/entitlement/api/migration/AccountMigrationData.java 2(+1 -1)
entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java 6(+3 -3)
entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultDeletedEvent.java 42(+42 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultEntitlementRepairApi.java 468(+468 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultRepairEntitlementEvent.java 61(+61 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultSubscriptionRepair.java 288(+288 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairEntitlementLifecycleDao.java 28(+28 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairSubscriptionApiService.java 36(+36 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairSubscriptionFactory.java 56(+56 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/repair/SubscriptionDataRepair.java 212(+212 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java 33(+9 -24)
entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionApiService.java 33(+17 -16)
entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultSubscriptionFactory.java 14(+9 -5)
entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBundleData.java 19(+14 -5)
entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java 293(+175 -118)
entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionData.java 5(+5 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionDataIterator.java 2(+1 -1)
entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java 104(+82 -22)
entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java 249(+249 -0)
entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java 16(+12 -4)
entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg 27(+20 -7)
entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg 26(+23 -3)
entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigrationSql.java 10(+4 -6)
entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestApiBaseRepair.java 215(+215 -0)
entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairWithAO.java 745(+745 -0)
entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairWithError.java 428(+428 -0)
entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java 84(+42 -42)
entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserCustomFieldsSql.java 8(+4 -4)
entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java 19(+16 -3)
entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java 11(+6 -5)
entitlement/src/test/resources/testInput.xml 515(+351 -164)
invoice/pom.xml 22(+22 -0)
invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDatePoster.java 3(+2 -1)
invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java 2(+1 -1)
invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java 77(+51 -26)
junction/pom.xml 7(+0 -7)
junction/src/main/java/com/ning/billing/junction/plumbing/billing/BillCycleDayCalculator.java 2(+1 -1)
junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultBillingEvent.java 7(+5 -2)
junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultEntitlementBillingApi.java 29(+15 -14)
pom.xml 5(+1 -4)
server/pom.xml 14(+9 -5)
Details
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/billing/BillingEvent.java b/api/src/main/java/com/ning/billing/entitlement/api/billing/BillingEvent.java
index fa2a46a..eb8410f 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/billing/BillingEvent.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/billing/BillingEvent.java
@@ -25,8 +25,8 @@ 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.SubscriptionTransitionType;
import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition.SubscriptionTransitionType;
public interface BillingEvent extends Comparable<BillingEvent> {
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/EntitlementService.java b/api/src/main/java/com/ning/billing/entitlement/api/EntitlementService.java
index bebffb0..50d45a1 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/EntitlementService.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/EntitlementService.java
@@ -19,6 +19,4 @@ package com.ning.billing.entitlement.api;
import com.ning.billing.lifecycle.KillbillService;
public interface EntitlementService extends KillbillService {
-
-
}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/repair/BundleRepair.java b/api/src/main/java/com/ning/billing/entitlement/api/repair/BundleRepair.java
new file mode 100644
index 0000000..27dadd6
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/api/repair/BundleRepair.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import java.util.List;
+import java.util.UUID;
+
+public interface BundleRepair {
+
+ String getViewId();
+
+ UUID getBundleId();
+
+ List<SubscriptionRepair> getSubscriptions();
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/repair/EntitlementRepairApi.java b/api/src/main/java/com/ning/billing/entitlement/api/repair/EntitlementRepairApi.java
new file mode 100644
index 0000000..de6ae59
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/api/repair/EntitlementRepairApi.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import java.util.UUID;
+
+import com.ning.billing.util.callcontext.CallContext;
+
+public interface EntitlementRepairApi {
+
+ public BundleRepair getBundleRepair(final UUID bundleId) throws EntitlementRepairException;
+
+ public BundleRepair repairBundle(final BundleRepair input, final boolean dryRun, final CallContext context) throws EntitlementRepairException;
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/repair/EntitlementRepairException.java b/api/src/main/java/com/ning/billing/entitlement/api/repair/EntitlementRepairException.java
new file mode 100644
index 0000000..e99a283
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/api/repair/EntitlementRepairException.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import com.ning.billing.BillingExceptionBase;
+import com.ning.billing.ErrorCode;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
+
+public class EntitlementRepairException extends BillingExceptionBase {
+
+ private static final long serialVersionUID = 19067233L;
+
+ public EntitlementRepairException(EntitlementUserApiException e) {
+ super(e, e.getCode(), e.getMessage());
+ }
+
+ public EntitlementRepairException(CatalogApiException e) {
+ super(e, e.getCode(), e.getMessage());
+ }
+ public EntitlementRepairException(Throwable e, ErrorCode code, Object...args) {
+ super(e, code, args);
+ }
+
+ public EntitlementRepairException(ErrorCode code, Object...args) {
+ super(code, args);
+ }
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/repair/RepairEntitlementEvent.java b/api/src/main/java/com/ning/billing/entitlement/api/repair/RepairEntitlementEvent.java
new file mode 100644
index 0000000..e5bebad
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/api/repair/RepairEntitlementEvent.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.util.bus.BusEvent;
+
+public interface RepairEntitlementEvent extends BusEvent {
+
+ public UUID getAccountId();
+
+ public UUID getBundleId();
+
+ public DateTime getEffectiveDate();
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/repair/SubscriptionRepair.java b/api/src/main/java/com/ning/billing/entitlement/api/repair/SubscriptionRepair.java
new file mode 100644
index 0000000..adab3b5
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/api/repair/SubscriptionRepair.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
+
+public interface SubscriptionRepair {
+
+ public UUID getId();
+
+ public List<DeletedEvent> getDeletedEvents();
+
+ public List<NewEvent> getNewEvents();
+
+ public List<ExistingEvent> getExistingEvents();
+
+ public interface DeletedEvent {
+ public UUID getEventId();
+ }
+
+ public interface NewEvent {
+ public PlanPhaseSpecifier getPlanPhaseSpecifier();
+ public DateTime getRequestedDate();
+ public SubscriptionTransitionType getSubscriptionTransitionType();
+
+ }
+
+ public interface ExistingEvent extends DeletedEvent, NewEvent {
+ public DateTime getEffectiveDate();
+ }
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/SubscriptionTransitionType.java b/api/src/main/java/com/ning/billing/entitlement/api/SubscriptionTransitionType.java
new file mode 100644
index 0000000..8e75675
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/api/SubscriptionTransitionType.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+package com.ning.billing.entitlement.api;
+
+public enum SubscriptionTransitionType {
+ MIGRATE_ENTITLEMENT,
+ CREATE,
+ MIGRATE_BILLING,
+ CHANGE,
+ RE_CREATE,
+ CANCEL,
+ UNCANCEL,
+ PHASE
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApiException.java b/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApiException.java
index 693938b..08f2906 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApiException.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApiException.java
@@ -34,5 +34,4 @@ public class EntitlementUserApiException extends BillingExceptionBase {
public EntitlementUserApiException(ErrorCode code, Object...args) {
super(code, args);
}
-
}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionEventTransition.java b/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionEventTransition.java
index bd93598..eae5701 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionEventTransition.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionEventTransition.java
@@ -22,23 +22,16 @@ import org.joda.time.DateTime;
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
+
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
+
import com.ning.billing.catalog.api.PriceList;
+
import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
import com.ning.billing.util.bus.BusEvent;
public interface SubscriptionEventTransition extends BusEvent {
- public enum SubscriptionTransitionType {
- MIGRATE_ENTITLEMENT,
- CREATE,
- MIGRATE_BILLING,
- CHANGE,
- RE_CREATE,
- CANCEL,
- UNCANCEL,
- PHASE
- }
-
UUID getId();
SubscriptionTransitionType getTransitionType();
diff --git a/api/src/main/java/com/ning/billing/ErrorCode.java b/api/src/main/java/com/ning/billing/ErrorCode.java
index 4b25ac6..398d472 100644
--- a/api/src/main/java/com/ning/billing/ErrorCode.java
+++ b/api/src/main/java/com/ning/billing/ErrorCode.java
@@ -18,6 +18,8 @@ package com.ning.billing;
public enum ErrorCode {
+
+
/*
* Range 0 : COMMON EXCEPTIONS
*/
@@ -56,6 +58,24 @@ public enum ErrorCode {
ENT_GET_NO_BUNDLE_FOR_SUBSCRIPTION(1080, "Could not find a bundle for subscription %s"),
ENT_GET_INVALID_BUNDLE_ID(1081, "Could not find a bundle matching id %s"),
ENT_INVALID_SUBSCRIPTION_ID(1082, "Unknown subscription %s"),
+
+ /* Repair */
+ ENT_REPAIR_INVALID_DELETE_SET(1091, "Event %s is not deleted for subscription %s but prior events were"),
+ ENT_REPAIR_NON_EXISTENT_DELETE_EVENT(1092, "Event %s does not exist for subscription %s"),
+ ENT_REPAIR_MISSING_AO_DELETE_EVENT(1093, "Event %s should be in deleted set for subscription %s because BP events got deleted earlier"),
+ ENT_REPAIR_NEW_EVENT_BEFORE_LAST_BP_REMAINING(1094, "New event %s for subscription %s is before last remaining event for BP"),
+ ENT_REPAIR_NEW_EVENT_BEFORE_LAST_AO_REMAINING(1095, "New event %s for subscription %s is before last remaining event"),
+ ENT_REPAIR_UNKNOWN_TYPE(1096, "Unknown new event type %s for subscription %s"),
+ ENT_REPAIR_UNKNOWN_BUNDLE(1097, "Unknown bundle %s"),
+ ENT_REPAIR_UNKNOWN_SUBSCRIPTION(1098, "Unknown subscription %s"),
+ ENT_REPAIR_NO_ACTIVE_SUBSCRIPTIONS(1099, "No active subscriptions on bundle %s"),
+ ENT_REPAIR_VIEW_CHANGED(1100, "View for bundle %s has changed from %s to %s"),
+ ENT_REPAIR_SUB_RECREATE_NOT_EMPTY(1101, "Subscription %s with recreation for bundle %s should specify all existing events to be deleted"),
+ ENT_REPAIR_SUB_EMPTY(1102, "Subscription %s with recreation for bundle %s should specify all existing events to be deleted"),
+ ENT_REPAIR_BP_RECREATE_MISSING_AO(1103, "BP recreation for bundle %s implies repair all subscriptions"),
+ ENT_REPAIR_BP_RECREATE_MISSING_AO_CREATE(1104, "BP recreation for bundle %s implies that all AO should be start also with a CREATE"),
+ ENT_REPAIR_AO_CREATE_BEFORE_BP_START(1105, "Can't recreate AO %s for bundle %s before BP starts"),
+
ENT_BUNDLE_IS_OVERDUE_BLOCKED(1090, "Changes to this bundle are blocked by overdue enforcement (%s : %s)"),
ENT_ACCOUNT_IS_OVERDUE_BLOCKED(1091, "Changes to this account are blocked by overdue enforcement (%s)"),
@@ -185,6 +205,7 @@ public enum ErrorCode {
MISSING_TRANSLATION_RESOURCE(9010, "The resources for %s translation could not be found."),
MISSING_DEFAULT_TRANSLATION_RESOURCE(9011, "The default resource for %s translation could not be found.");
+
private int code;
private String format;
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoiceNotifier.java b/api/src/main/java/com/ning/billing/invoice/api/InvoiceNotifier.java
index 7a7fbc4..cd0b7b6 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoiceNotifier.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoiceNotifier.java
@@ -19,6 +19,8 @@ package com.ning.billing.invoice.api;
import com.ning.billing.BillingExceptionBase;
import com.ning.billing.account.api.Account;
+import java.io.IOException;
+
public interface InvoiceNotifier {
- public void notify(Account account, Invoice invoice) throws BillingExceptionBase;
+ public void notify(Account account, Invoice invoice) throws InvoiceApiException;
}
diff --git a/api/src/main/java/com/ning/billing/junction/api/BillingApi.java b/api/src/main/java/com/ning/billing/junction/api/BillingApi.java
index a1b3657..4128811 100644
--- a/api/src/main/java/com/ning/billing/junction/api/BillingApi.java
+++ b/api/src/main/java/com/ning/billing/junction/api/BillingApi.java
@@ -19,8 +19,11 @@ package com.ning.billing.junction.api;
import java.util.SortedSet;
import java.util.UUID;
+import org.joda.time.DateTime;
+
import com.ning.billing.entitlement.api.billing.BillingEvent;
import com.ning.billing.entitlement.api.billing.ChargeThruApi;
+import com.ning.billing.util.callcontext.CallContext;
public interface BillingApi extends ChargeThruApi {
/**
@@ -30,6 +33,4 @@ public interface BillingApi extends ChargeThruApi {
*
*/
public SortedSet<BillingEvent> getBillingEventsForAccountAndUpdateAccountBCD(UUID accountId);
-
-
}
diff --git a/api/src/main/java/com/ning/billing/util/bus/BusEvent.java b/api/src/main/java/com/ning/billing/util/bus/BusEvent.java
index d44e1b5..d969440 100644
--- a/api/src/main/java/com/ning/billing/util/bus/BusEvent.java
+++ b/api/src/main/java/com/ning/billing/util/bus/BusEvent.java
@@ -24,6 +24,7 @@ public interface BusEvent {
ACCOUNT_CREATE,
ACCOUNT_CHANGE,
SUBSCRIPTION_TRANSITION,
+ BUNDLE_REPAIR,
INVOICE_EMPTY,
INVOICE_CREATION,
PAYMENT_INFO,
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBusHandler.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBusHandler.java
index ffa25e6..e4a8f43 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBusHandler.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBusHandler.java
@@ -27,6 +27,7 @@ import org.testng.Assert;
import com.google.common.base.Joiner;
import com.google.common.eventbus.Subscribe;
+import com.ning.billing.entitlement.api.repair.RepairEntitlementEvent;
import com.ning.billing.entitlement.api.user.SubscriptionEventTransition;
import com.ning.billing.invoice.api.InvoiceCreationEvent;
import com.ning.billing.payment.api.PaymentErrorEvent;
@@ -38,11 +39,14 @@ public class TestBusHandler {
private final List<NextEvent> nextExpectedEvent;
+ private final TestFailure testFailure;
+
private volatile boolean completed;
- public TestBusHandler() {
+ public TestBusHandler(TestFailure testFailure) {
nextExpectedEvent = new Stack<NextEvent>();
this.completed = false;
+ this.testFailure = testFailure;
}
public enum NextEvent {
@@ -55,7 +59,15 @@ public class TestBusHandler {
RESUME,
PHASE,
INVOICE,
- PAYMENT
+ PAYMENT,
+ REPAIR_BUNDLE
+ }
+
+ @Subscribe
+ public void handleEntitlementEvents(RepairEntitlementEvent event) {
+ log.info(String.format("TestBusHandler Got RepairEntitlementEvent event %s", event.toString()));
+ assertEqualsNicely(NextEvent.REPAIR_BUNDLE);
+ notifyIfStackEmpty();
}
@Subscribe
@@ -184,7 +196,9 @@ public class TestBusHandler {
if (!foundIt) {
Joiner joiner = Joiner.on(" ");
log.error("TestBusHandler Received event " + received + "; expected " + joiner.join(nextExpectedEvent));
- Assert.fail();
+ if (testFailure != null) {
+ testFailure.failed("TestBusHandler Received event " + received + "; expected " + joiner.join(nextExpectedEvent));
+ }
}
}
}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestFailure.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestFailure.java
new file mode 100644
index 0000000..fd33a6b
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestFailure.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+package com.ning.billing.beatrix.integration;
+
+public interface TestFailure {
+
+ public void failed(String msg);
+
+ public void reset();
+}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
index 552e3d8..8da1bb8 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
@@ -19,229 +19,37 @@ package com.ning.billing.beatrix.integration;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
-import static org.testng.Assert.fail;
-import java.io.IOException;
import java.math.BigDecimal;
-import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.account.api.AccountData;
import com.ning.billing.catalog.api.PhaseType;
-import com.ning.billing.dbi.MysqlTestingHelper;
import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
import com.ning.billing.invoice.api.Invoice;
-import com.ning.billing.invoice.api.InvoiceItem;
-import com.ning.billing.invoice.model.InvoicingConfiguration;
-import com.ning.billing.util.callcontext.CallContext;
-import com.ning.billing.util.callcontext.CallOrigin;
-import com.ning.billing.util.callcontext.UserType;
-import com.ning.billing.util.callcontext.DefaultCallContextFactory;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang.RandomStringUtils;
import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
+
import org.joda.time.Interval;
-import org.skife.jdbi.v2.Handle;
-import org.skife.jdbi.v2.IDBI;
-import org.skife.jdbi.v2.TransactionCallback;
-import org.skife.jdbi.v2.TransactionStatus;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.AfterSuite;
-
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
-import com.google.inject.Inject;
import com.ning.billing.account.api.Account;
-import com.ning.billing.account.api.AccountData;
-import com.ning.billing.account.api.AccountService;
-import com.ning.billing.account.api.AccountUserApi;
import com.ning.billing.beatrix.integration.TestBusHandler.NextEvent;
-import com.ning.billing.beatrix.lifecycle.Lifecycle;
import com.ning.billing.catalog.api.BillingPeriod;
import com.ning.billing.catalog.api.Currency;
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.api.EntitlementService;
-import com.ning.billing.entitlement.api.user.EntitlementUserApi;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.entitlement.api.user.SubscriptionData;
-import com.ning.billing.invoice.api.InvoiceService;
-import com.ning.billing.invoice.api.InvoiceUserApi;
-
-import com.ning.billing.util.clock.ClockMock;
-import com.ning.billing.util.bus.BusService;
@Test(groups = "slow")
@Guice(modules = {MockModule.class})
-public class TestIntegration {
- private static final int NUMBER_OF_DECIMALS = InvoicingConfiguration.getNumberOfDecimals();
- private static final int ROUNDING_METHOD = InvoicingConfiguration.getRoundingMode();
-
- private static final BigDecimal ONE = new BigDecimal("1.0000").setScale(NUMBER_OF_DECIMALS);
- private static final BigDecimal TWENTY_NINE = new BigDecimal("29.0000").setScale(NUMBER_OF_DECIMALS);
- private static final BigDecimal THIRTY = new BigDecimal("30.0000").setScale(NUMBER_OF_DECIMALS);
- private static final BigDecimal THIRTY_ONE = new BigDecimal("31.0000").setScale(NUMBER_OF_DECIMALS);
-
- private static final Logger log = LoggerFactory.getLogger(TestIntegration.class);
- private static long AT_LEAST_ONE_MONTH_MS = 31L * 24L * 3600L * 1000L;
-
- private static final long DELAY = 5000;
-
- @Inject IDBI dbi;
-
- @Inject
- private ClockMock clock;
- private CallContext context;
-
- @Inject
- private Lifecycle lifecycle;
-
- @Inject
- private BusService busService;
-
- @Inject
- private MysqlTestingHelper helper;
-
- @Inject
- private EntitlementUserApi entitlementUserApi;
-
- @Inject
- private InvoiceUserApi invoiceUserApi;
-
- @Inject
- private AccountUserApi accountUserApi;
-
- private TestBusHandler busHandler;
-
- private void setupMySQL() throws IOException
- {
- final String accountDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
- final String entitlementDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/entitlement/ddl.sql"));
- final String invoiceDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
- final String paymentDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/payment/ddl.sql"));
- final String utilDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
-
- helper.startMysql();
-
- helper.initDb(accountDdl);
- helper.initDb(entitlementDdl);
- helper.initDb(invoiceDdl);
- helper.initDb(paymentDdl);
- helper.initDb(utilDdl);
- }
-
- @BeforeSuite(groups = "slow")
- public void setup() throws Exception{
-
- setupMySQL();
-
- context = new DefaultCallContextFactory(clock).createCallContext("Integration Test", CallOrigin.TEST, UserType.TEST);
-
- /**
- * Initialize lifecyle for subset of services
- */
- busHandler = new TestBusHandler();
- lifecycle.fireStartupSequencePriorEventRegistration();
- busService.getBus().register(busHandler);
- lifecycle.fireStartupSequencePostEventRegistration();
- }
-
- @AfterSuite(groups = "slow")
- public void tearDown() throws Exception {
- lifecycle.fireShutdownSequencePriorEventUnRegistration();
- busService.getBus().unregister(busHandler);
- lifecycle.fireShutdownSequencePostEventUnRegistration();
- if (helper != null) {
- helper.stopMysql();
- }
- }
-
- @BeforeMethod(groups = "slow")
- public void setupTest() {
-
- log.warn("\n");
- log.warn("RESET TEST FRAMEWORK\n\n");
- busHandler.reset();
- clock.resetDeltaFromReality();
- cleanupData();
- }
-
- @AfterMethod(groups = "slow")
- public void cleanupTest() {
- log.warn("DONE WITH TEST\n");
- }
-
- private void cleanupData() {
- dbi.inTransaction(new TransactionCallback<Void>() {
- @Override
- public Void inTransaction(Handle h, TransactionStatus status)
- throws Exception {
- h.execute("truncate table accounts");
- h.execute("truncate table entitlement_events");
- h.execute("truncate table subscriptions");
- h.execute("truncate table bundles");
- h.execute("truncate table notifications");
- h.execute("truncate table claimed_notifications");
- h.execute("truncate table invoices");
- h.execute("truncate table fixed_invoice_items");
- h.execute("truncate table recurring_invoice_items");
- h.execute("truncate table tag_definitions");
- h.execute("truncate table tags");
- h.execute("truncate table custom_fields");
- h.execute("truncate table invoice_payments");
- h.execute("truncate table payment_attempts");
- h.execute("truncate table payments");
- return null;
- }
- });
- }
-
- private void verifyTestResult(UUID accountId, UUID subscriptionId,
- DateTime startDate, DateTime endDate,
- BigDecimal amount, DateTime chargeThroughDate,
- int totalInvoiceItemCount) {
- SubscriptionData subscription = (SubscriptionData) entitlementUserApi.getSubscriptionFromId(subscriptionId);
-
- List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(accountId);
- List<InvoiceItem> invoiceItems = new ArrayList<InvoiceItem>();
- for (Invoice invoice : invoices) {
- invoiceItems.addAll(invoice.getInvoiceItems());
- }
- assertEquals(invoiceItems.size(), totalInvoiceItemCount);
-
- boolean wasFound = false;
-
- for (InvoiceItem item : invoiceItems) {
- if (item.getStartDate().compareTo(startDate) == 0) {
- if (item.getEndDate().compareTo(endDate) == 0) {
- if (item.getAmount().compareTo(amount) == 0) {
- wasFound = true;
- break;
- }
- }
- }
- }
-
- if (!wasFound) {
- fail();
- }
-
- DateTime ctd = subscription.getChargedThroughDate();
- assertNotNull(ctd);
- log.info("Checking CTD: " + ctd.toString() + "; clock is " + clock.getUTCNow().toString());
- assertTrue(clock.getUTCNow().isBefore(ctd));
- assertTrue(ctd.compareTo(chargeThroughDate) == 0);
- }
+public class TestIntegration extends TestIntegrationBase {
+
@Test(groups = "slow", enabled = true)
public void testBasePlanCompleteWithBillingDayInPast() throws Exception {
@@ -291,7 +99,61 @@ public class TestIntegration {
}
}
- @Test(groups = "slow", enabled = true)
+ @Test(groups = "slow", enabled = true)
+ public void testRepairChangeBPWithAddonIncluded() throws Exception {
+
+ DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+ clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+ Account account = accountUserApi.createAccount(getAccountData(25), null, null, context);
+ assertNotNull(account);
+
+ SubscriptionBundle bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever", context);
+
+ String productName = "Shotgun";
+ BillingPeriod term = BillingPeriod.MONTHLY;
+ String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+ busHandler.pushExpectedEvent(NextEvent.CREATE);
+ busHandler.pushExpectedEvent(NextEvent.INVOICE);
+ SubscriptionData baseSubscription = (SubscriptionData) entitlementUserApi.createSubscription(bundle.getId(),
+ new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null), null, context);
+ assertNotNull(baseSubscription);
+ assertTrue(busHandler.isCompleted(DELAY));
+
+ // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
+ Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(3));
+ clock.addDeltaFromReality(it.toDurationMillis());
+
+ busHandler.pushExpectedEvent(NextEvent.CREATE);
+ busHandler.pushExpectedEvent(NextEvent.INVOICE);
+ busHandler.pushExpectedEvent(NextEvent.PAYMENT);
+ SubscriptionData aoSubscription = (SubscriptionData) entitlementUserApi.createSubscription(bundle.getId(),
+ new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null), null, context);
+ assertTrue(busHandler.isCompleted(DELAY));
+
+ busHandler.pushExpectedEvent(NextEvent.CREATE);
+ busHandler.pushExpectedEvent(NextEvent.INVOICE);
+ busHandler.pushExpectedEvent(NextEvent.PAYMENT);
+ SubscriptionData aoSubscription2 = (SubscriptionData) entitlementUserApi.createSubscription(bundle.getId(),
+ new PlanPhaseSpecifier("Laser-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null), null, context);
+ assertTrue(busHandler.isCompleted(DELAY));
+
+
+ // MOVE CLOCK A LITTLE BIT MORE -- EITHER STAY IN TRIAL OR GET OUT
+ int duration = 35;
+ it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(duration));
+ busHandler.pushExpectedEvent(NextEvent.PHASE);
+ busHandler.pushExpectedEvent(NextEvent.PHASE);
+ busHandler.pushExpectedEvent(NextEvent.PHASE);
+ busHandler.pushExpectedEvent(NextEvent.INVOICE);
+ busHandler.pushExpectedEvent(NextEvent.PAYMENT);
+ clock.addDeltaFromReality(it.toDurationMillis());
+ assertTrue(busHandler.isCompleted(DELAY));
+ }
+
+
+ @Test(groups = "slow", enabled = false)
public void testWithRecreatePlan() throws Exception {
DateTime initialDate = new DateTime(2012, 2, 1, 0, 3, 42, 0);
@@ -364,7 +226,8 @@ public class TestIntegration {
boolean proRationExpected) throws Exception {
log.info("Beginning test with BCD of " + billingDay);
- Account account = accountUserApi.createAccount(getAccountData(billingDay), null, null, context);
+ AccountData accountData = getAccountData(billingDay);
+ Account account = accountUserApi.createAccount(accountData, null, null, context);
UUID accountId = account.getId();
assertNotNull(account);
@@ -628,99 +491,4 @@ public class TestIntegration {
assertNotNull(invoices);
assertEquals(invoices.size(), 3);
}
-
- protected AccountData getAccountData(final int billingDay) {
-
- final String someRandomKey = RandomStringUtils.randomAlphanumeric(10);
- return new AccountData() {
- @Override
- public String getName() {
- return "firstName lastName";
- }
- @Override
- public int getFirstNameLength() {
- return "firstName".length();
- }
- @Override
- public String getEmail() {
- return someRandomKey + "@laposte.fr";
- }
- @Override
- public String getPhone() {
- return "4152876341";
- }
-
- @Override
- public boolean isMigrated() {
- return false;
- }
-
- @Override
- public boolean isNotifiedForInvoices() {
- return false;
- }
-
- @Override
- public String getExternalKey() {
- return someRandomKey;
- }
- @Override
- public int getBillCycleDay() {
- return billingDay;
- }
- @Override
- public Currency getCurrency() {
- return Currency.USD;
- }
- @Override
- public String getPaymentProviderName() {
- return MockModule.PLUGIN_NAME;
- }
-
- @Override
- public DateTimeZone getTimeZone() {
- return null;
- }
-
- @Override
- public String getLocale() {
- return null;
- }
-
- @Override
- public String getAddress1() {
- return null;
- }
-
- @Override
- public String getAddress2() {
- return null;
- }
-
- @Override
- public String getCompanyName() {
- return null;
- }
-
- @Override
- public String getCity() {
- return null;
- }
-
- @Override
- public String getStateOrProvince() {
- return null;
- }
-
- @Override
- public String getPostalCode() {
- return null;
- }
-
- @Override
- public String getCountry() {
- return null;
- }
- };
- }
}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
new file mode 100644
index 0000000..b7868ac
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
@@ -0,0 +1,361 @@
+/*
+ * 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.
+ */
+package com.ning.billing.beatrix.integration;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.RandomStringUtils;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.skife.jdbi.v2.Handle;
+import org.skife.jdbi.v2.IDBI;
+import org.skife.jdbi.v2.TransactionCallback;
+import org.skife.jdbi.v2.TransactionStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+
+import com.google.inject.Inject;
+import com.ning.billing.account.api.AccountData;
+import com.ning.billing.account.api.AccountService;
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.beatrix.lifecycle.Lifecycle;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.entitlement.api.EntitlementService;
+import com.ning.billing.entitlement.api.repair.EntitlementRepairApi;
+import com.ning.billing.entitlement.api.user.EntitlementUserApi;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceService;
+import com.ning.billing.invoice.api.InvoiceUserApi;
+import com.ning.billing.invoice.model.InvoicingConfiguration;
+import com.ning.billing.util.bus.BusService;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.DefaultCallContextFactory;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.clock.ClockMock;
+
+public class TestIntegrationBase implements TestFailure {
+
+ protected static final int NUMBER_OF_DECIMALS = InvoicingConfiguration.getNumberOfDecimals();
+ protected static final int ROUNDING_METHOD = InvoicingConfiguration.getRoundingMode();
+
+ protected static final BigDecimal ONE = new BigDecimal("1.0000").setScale(NUMBER_OF_DECIMALS);
+ protected static final BigDecimal TWENTY_NINE = new BigDecimal("29.0000").setScale(NUMBER_OF_DECIMALS);
+ protected static final BigDecimal THIRTY = new BigDecimal("30.0000").setScale(NUMBER_OF_DECIMALS);
+ protected static final BigDecimal THIRTY_ONE = new BigDecimal("31.0000").setScale(NUMBER_OF_DECIMALS);
+
+ protected static final Logger log = LoggerFactory.getLogger(TestIntegration.class);
+ protected static long AT_LEAST_ONE_MONTH_MS = 31L * 24L * 3600L * 1000L;
+
+ protected static final long DELAY = 5000;
+
+ @Inject
+ protected IDBI dbi;
+
+ @Inject
+ protected ClockMock clock;
+
+ protected CallContext context;
+
+ @Inject
+ protected Lifecycle lifecycle;
+
+ @Inject
+ protected BusService busService;
+
+ @Inject
+ protected EntitlementService entitlementService;
+
+ @Inject
+ protected InvoiceService invoiceService;
+
+ @Inject
+ protected AccountService accountService;
+
+ @Inject
+ protected MysqlTestingHelper helper;
+ @Inject
+ protected EntitlementUserApi entitlementUserApi;
+
+ @Inject
+ protected EntitlementRepairApi repairApi;
+
+ @Inject
+ protected InvoiceUserApi invoiceUserApi;
+
+ @Inject
+ protected AccountUserApi accountUserApi;
+
+ protected TestBusHandler busHandler;
+
+
+ private boolean currentTestStatusSuccess;
+ private String currentTestFailedMsg;
+
+ @Override
+ public void failed(String msg) {
+ currentTestStatusSuccess = false;
+ currentTestFailedMsg = msg;
+ }
+
+ @Override
+ public void reset() {
+ currentTestStatusSuccess = true;
+ currentTestFailedMsg = null;
+ }
+
+ protected void assertFailureFromBusHandler() {
+ if (!currentTestStatusSuccess) {
+ log.error(currentTestFailedMsg);
+ fail();
+ }
+ }
+
+ protected void setupMySQL() throws IOException
+ {
+ final String accountDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
+ final String entitlementDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/entitlement/ddl.sql"));
+ final String invoiceDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
+ final String paymentDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/payment/ddl.sql"));
+ final String utilDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
+
+ helper.startMysql();
+
+ helper.initDb(accountDdl);
+ helper.initDb(entitlementDdl);
+ helper.initDb(invoiceDdl);
+ helper.initDb(paymentDdl);
+ helper.initDb(utilDdl);
+ }
+
+ @BeforeClass(groups = "slow")
+ public void setup() throws Exception{
+
+ setupMySQL();
+
+ cleanupData();
+
+ context = new DefaultCallContextFactory(clock).createCallContext("Integration Test", CallOrigin.TEST, UserType.TEST);
+
+ /**
+ * Initialize lifecyle for subset of services
+ */
+ busHandler = new TestBusHandler(this);
+ lifecycle.fireStartupSequencePriorEventRegistration();
+ busService.getBus().register(busHandler);
+ lifecycle.fireStartupSequencePostEventRegistration();
+ }
+
+ @AfterClass(groups = "slow")
+ public void tearDown() throws Exception {
+ lifecycle.fireShutdownSequencePriorEventUnRegistration();
+ busService.getBus().unregister(busHandler);
+ lifecycle.fireShutdownSequencePostEventUnRegistration();
+ helper.stopMysql();
+ }
+
+
+ @BeforeMethod(groups = "slow")
+ public void setupTest() {
+
+ log.warn("\n");
+ log.warn("RESET TEST FRAMEWORK\n\n");
+ cleanupData();
+ busHandler.reset();
+ clock.resetDeltaFromReality();
+ reset();
+ }
+
+ @AfterMethod(groups = "slow")
+ public void cleanupTest() {
+ log.warn("DONE WITH TEST\n");
+ }
+
+ protected void cleanupData() {
+ dbi.inTransaction(new TransactionCallback<Void>() {
+ @Override
+ public Void inTransaction(Handle h, TransactionStatus status)
+ throws Exception {
+ h.execute("truncate table accounts");
+ h.execute("truncate table entitlement_events");
+ h.execute("truncate table subscriptions");
+ h.execute("truncate table bundles");
+ h.execute("truncate table notifications");
+ h.execute("truncate table claimed_notifications");
+ h.execute("truncate table invoices");
+ h.execute("truncate table fixed_invoice_items");
+ h.execute("truncate table recurring_invoice_items");
+ h.execute("truncate table tag_definitions");
+ h.execute("truncate table tags");
+ h.execute("truncate table custom_fields");
+ h.execute("truncate table invoice_payments");
+ h.execute("truncate table payment_attempts");
+ h.execute("truncate table payments");
+ return null;
+ }
+ });
+ }
+
+ protected void verifyTestResult(UUID accountId, UUID subscriptionId,
+ DateTime startDate, DateTime endDate,
+ BigDecimal amount, DateTime chargeThroughDate,
+ int totalInvoiceItemCount) {
+ SubscriptionData subscription = (SubscriptionData) entitlementUserApi.getSubscriptionFromId(subscriptionId);
+
+ List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(accountId);
+ List<InvoiceItem> invoiceItems = new ArrayList<InvoiceItem>();
+ for (Invoice invoice : invoices) {
+ invoiceItems.addAll(invoice.getInvoiceItems());
+ }
+ assertEquals(invoiceItems.size(), totalInvoiceItemCount);
+
+ boolean wasFound = false;
+
+ for (InvoiceItem item : invoiceItems) {
+ if (item.getStartDate().compareTo(startDate) == 0) {
+ if (item.getEndDate().compareTo(endDate) == 0) {
+ if (item.getAmount().compareTo(amount) == 0) {
+ wasFound = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!wasFound) {
+ fail();
+ }
+
+ DateTime ctd = subscription.getChargedThroughDate();
+ assertNotNull(ctd);
+ log.info("Checking CTD: " + ctd.toString() + "; clock is " + clock.getUTCNow().toString());
+ assertTrue(clock.getUTCNow().isBefore(ctd));
+ assertTrue(ctd.compareTo(chargeThroughDate) == 0);
+ }
+
+
+ protected AccountData getAccountData(final int billingDay) {
+
+ final String someRandomKey = RandomStringUtils.randomAlphanumeric(10);
+ return new AccountData() {
+ @Override
+ public String getName() {
+ return "firstName lastName";
+ }
+ @Override
+ public int getFirstNameLength() {
+ return "firstName".length();
+ }
+ @Override
+ public String getEmail() {
+ return someRandomKey + "@laposte.fr";
+ }
+ @Override
+ public String getPhone() {
+ return "4152876341";
+ }
+
+ @Override
+ public boolean isMigrated() {
+ return false;
+ }
+
+ @Override
+ public boolean isNotifiedForInvoices() {
+ return false;
+ }
+
+ @Override
+ public String getExternalKey() {
+ return someRandomKey;
+ }
+ @Override
+ public int getBillCycleDay() {
+ return billingDay;
+ }
+ @Override
+ public Currency getCurrency() {
+ return Currency.USD;
+ }
+ @Override
+ public String getPaymentProviderName() {
+ return MockModule.PLUGIN_NAME;
+ }
+
+ @Override
+ public DateTimeZone getTimeZone() {
+ return null;
+ }
+
+ @Override
+ public String getLocale() {
+ return null;
+ }
+
+ @Override
+ public String getAddress1() {
+ return null;
+ }
+
+ @Override
+ public String getAddress2() {
+ return null;
+ }
+
+ @Override
+ public String getCompanyName() {
+ return null;
+ }
+
+ @Override
+ public String getCity() {
+ return null;
+ }
+
+ @Override
+ public String getStateOrProvince() {
+ return null;
+ }
+
+ @Override
+ public String getPostalCode() {
+ return null;
+ }
+
+ @Override
+ public String getCountry() {
+ return null;
+ }
+ };
+ }
+}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestRepairIntegration.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestRepairIntegration.java
new file mode 100644
index 0000000..bae9197
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestRepairIntegration.java
@@ -0,0 +1,328 @@
+/*
+ * 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.
+ */
+package com.ning.billing.beatrix.integration;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.Interval;
+import org.testng.Assert;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.beatrix.integration.TestBusHandler.NextEvent;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.repair.BundleRepair;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.DeletedEvent;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.ExistingEvent;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.NewEvent;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.SubscriptionEvents;
+import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+
+@Test(groups = "slow")
+@Guice(modules = {MockModule.class})
+public class TestRepairIntegration extends TestIntegrationBase {
+
+
+ @Test(groups={"slow"}, enabled=true)
+ public void testRepairChangeBPWithAddonIncludedIntrial() throws Exception {
+ testRepairChangeBPWithAddonIncluded(true);
+ }
+
+ @Test(groups={"slow"}, enabled=true)
+ public void testRepairChangeBPWithAddonIncludedOutOfTrial() throws Exception {
+ testRepairChangeBPWithAddonIncluded(false);
+ }
+
+ private void testRepairChangeBPWithAddonIncluded(boolean inTrial) throws Exception {
+
+ DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+ clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+ Account account = accountUserApi.createAccount(getAccountData(25), null, null, context);
+ assertNotNull(account);
+
+ SubscriptionBundle bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever", context);
+
+ String productName = "Shotgun";
+ BillingPeriod term = BillingPeriod.MONTHLY;
+ String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+ busHandler.pushExpectedEvent(NextEvent.CREATE);
+ busHandler.pushExpectedEvent(NextEvent.INVOICE);
+ SubscriptionData baseSubscription = (SubscriptionData) entitlementUserApi.createSubscription(bundle.getId(),
+ new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null), null, context);
+ assertNotNull(baseSubscription);
+ assertTrue(busHandler.isCompleted(DELAY));
+
+ // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
+ Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(3));
+ clock.addDeltaFromReality(it.toDurationMillis());
+
+ busHandler.pushExpectedEvent(NextEvent.CREATE);
+ busHandler.pushExpectedEvent(NextEvent.INVOICE);
+ busHandler.pushExpectedEvent(NextEvent.PAYMENT);
+ SubscriptionData aoSubscription = (SubscriptionData) entitlementUserApi.createSubscription(bundle.getId(),
+ new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null), null, context);
+ assertTrue(busHandler.isCompleted(DELAY));
+
+ busHandler.pushExpectedEvent(NextEvent.CREATE);
+ busHandler.pushExpectedEvent(NextEvent.INVOICE);
+ busHandler.pushExpectedEvent(NextEvent.PAYMENT);
+ SubscriptionData aoSubscription2 = (SubscriptionData) entitlementUserApi.createSubscription(bundle.getId(),
+ new PlanPhaseSpecifier("Laser-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null), null, context);
+ assertTrue(busHandler.isCompleted(DELAY));
+
+
+ // MOVE CLOCK A LITTLE BIT MORE -- EITHER STAY IN TRIAL OR GET OUT
+ int duration = inTrial ? 3 : 35;
+ it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(duration));
+ if (!inTrial) {
+ busHandler.pushExpectedEvent(NextEvent.PHASE);
+ busHandler.pushExpectedEvent(NextEvent.PHASE);
+ busHandler.pushExpectedEvent(NextEvent.PHASE);
+ busHandler.pushExpectedEvent(NextEvent.INVOICE);
+ busHandler.pushExpectedEvent(NextEvent.PAYMENT);
+ }
+ clock.addDeltaFromReality(it.toDurationMillis());
+ if (!inTrial) {
+ assertTrue(busHandler.isCompleted(DELAY));
+ }
+ boolean ifRepair = false;
+ if (ifRepair) {
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+
+ // Quick check
+ SubscriptionRepair bpRepair = getSubscriptionRepair(baseSubscription.getId(), bundleRepair);
+ assertEquals(bpRepair.getExistingEvents().size(), 2);
+
+ SubscriptionRepair aoRepair = getSubscriptionRepair(aoSubscription.getId(), bundleRepair);
+ assertEquals(aoRepair.getExistingEvents().size(), 2);
+
+ SubscriptionRepair aoRepair2 = getSubscriptionRepair(aoSubscription2.getId(), bundleRepair);
+ assertEquals(aoRepair2.getExistingEvents().size(), 2);
+
+ DateTime bpChangeDate = clock.getUTCNow().minusDays(1);
+
+ List<DeletedEvent> des = new LinkedList<SubscriptionRepair.DeletedEvent>();
+ des.add(createDeletedEvent(bpRepair.getExistingEvents().get(1).getEventId()));
+
+ PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.TRIAL);
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, bpChangeDate, spec);
+
+ bpRepair = createSubscriptionReapir(baseSubscription.getId(), des, Collections.singletonList(ne));
+
+ bundleRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(bpRepair));
+
+ // TIME TO REPAIR
+ busHandler.pushExpectedEvent(NextEvent.INVOICE);
+ busHandler.pushExpectedEvent(NextEvent.PAYMENT);
+ busHandler.pushExpectedEvent(NextEvent.REPAIR_BUNDLE);
+ repairApi.repairBundle(bundleRepair, false, context);
+ assertTrue(busHandler.isCompleted(DELAY));
+
+ SubscriptionData newAoSubscription = (SubscriptionData) entitlementUserApi.getSubscriptionFromId(aoSubscription.getId());
+ assertEquals(newAoSubscription.getState(), SubscriptionState.CANCELLED);
+ assertEquals(newAoSubscription.getAllTransitions().size(), 2);
+ assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+
+ SubscriptionData newAoSubscription2 = (SubscriptionData) entitlementUserApi.getSubscriptionFromId(aoSubscription2.getId());
+ assertEquals(newAoSubscription2.getState(), SubscriptionState.ACTIVE);
+ assertEquals(newAoSubscription2.getAllTransitions().size(), 2);
+ assertEquals(newAoSubscription2.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+
+
+ SubscriptionData newBaseSubscription = (SubscriptionData) entitlementUserApi.getSubscriptionFromId(baseSubscription.getId());
+ assertEquals(newBaseSubscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(newBaseSubscription.getAllTransitions().size(), 3);
+ assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+
+ assertFailureFromBusHandler();
+ }
+ }
+
+ protected SubscriptionRepair createSubscriptionReapir(final UUID id, final List<DeletedEvent> deletedEvents, final List<NewEvent> newEvents) {
+ return new SubscriptionRepair() {
+ @Override
+ public UUID getId() {
+ return id;
+ }
+ @Override
+ public List<NewEvent> getNewEvents() {
+ return newEvents;
+ }
+ @Override
+ public List<ExistingEvent> getExistingEvents() {
+ return null;
+ }
+ @Override
+ public List<DeletedEvent> getDeletedEvents() {
+ return deletedEvents;
+ }
+ };
+ }
+
+
+ protected BundleRepair createBundleRepair(final UUID bundleId, final String viewId, final List<SubscriptionRepair> subscriptionRepair) {
+ return new BundleRepair() {
+ @Override
+ public String getViewId() {
+ return viewId;
+ }
+ @Override
+ public List<SubscriptionRepair> getSubscriptions() {
+ return subscriptionRepair;
+ }
+ @Override
+ public UUID getBundleId() {
+ return bundleId;
+ }
+ };
+ }
+
+ protected ExistingEvent createExistingEventForAssertion(final SubscriptionTransitionType type,
+ final String productName, final PhaseType phaseType, final ProductCategory category, final String priceListName, final BillingPeriod billingPeriod,
+ final DateTime effectiveDateTime) {
+
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, category, billingPeriod, priceListName, phaseType);
+ ExistingEvent ev = new ExistingEvent() {
+ @Override
+ public SubscriptionTransitionType getSubscriptionTransitionType() {
+ return type;
+ }
+ @Override
+ public DateTime getRequestedDate() {
+ return null;
+ }
+ @Override
+ public PlanPhaseSpecifier getPlanPhaseSpecifier() {
+ return spec;
+ }
+ @Override
+ public UUID getEventId() {
+ return null;
+ }
+ @Override
+ public DateTime getEffectiveDate() {
+ return effectiveDateTime;
+ }
+ };
+ return ev;
+ }
+
+ protected SubscriptionRepair getSubscriptionRepair(final UUID id, final BundleRepair bundleRepair) {
+ for (SubscriptionRepair cur : bundleRepair.getSubscriptions()) {
+ if (cur.getId().equals(id)) {
+ return cur;
+ }
+ }
+ Assert.fail("Failed to find SubscriptionReapir " + id);
+ return null;
+ }
+ protected void validateExistingEventForAssertion(final ExistingEvent expected, final ExistingEvent input) {
+
+ log.info(String.format("Got %s -> Expected %s", input.getPlanPhaseSpecifier().getProductName(), expected.getPlanPhaseSpecifier().getProductName()));
+ assertEquals(input.getPlanPhaseSpecifier().getProductName(), expected.getPlanPhaseSpecifier().getProductName());
+ log.info(String.format("Got %s -> Expected %s", input.getPlanPhaseSpecifier().getPhaseType(), expected.getPlanPhaseSpecifier().getPhaseType()));
+ assertEquals(input.getPlanPhaseSpecifier().getPhaseType(), expected.getPlanPhaseSpecifier().getPhaseType());
+ log.info(String.format("Got %s -> Expected %s", input.getPlanPhaseSpecifier().getProductCategory(), expected.getPlanPhaseSpecifier().getProductCategory()));
+ assertEquals(input.getPlanPhaseSpecifier().getProductCategory(), expected.getPlanPhaseSpecifier().getProductCategory());
+ log.info(String.format("Got %s -> Expected %s", input.getPlanPhaseSpecifier().getPriceListName(), expected.getPlanPhaseSpecifier().getPriceListName()));
+ assertEquals(input.getPlanPhaseSpecifier().getPriceListName(), expected.getPlanPhaseSpecifier().getPriceListName());
+ log.info(String.format("Got %s -> Expected %s", input.getPlanPhaseSpecifier().getBillingPeriod(), expected.getPlanPhaseSpecifier().getBillingPeriod()));
+ assertEquals(input.getPlanPhaseSpecifier().getBillingPeriod(), expected.getPlanPhaseSpecifier().getBillingPeriod());
+ log.info(String.format("Got %s -> Expected %s", input.getEffectiveDate(), expected.getEffectiveDate()));
+ assertEquals(input.getEffectiveDate(), expected.getEffectiveDate());
+ }
+
+ protected DeletedEvent createDeletedEvent(final UUID eventId) {
+ return new DeletedEvent() {
+ @Override
+ public UUID getEventId() {
+ return eventId;
+ }
+ };
+ }
+
+ protected NewEvent createNewEvent(final SubscriptionTransitionType type, final DateTime requestedDate, final PlanPhaseSpecifier spec) {
+
+ return new NewEvent() {
+ @Override
+ public SubscriptionTransitionType getSubscriptionTransitionType() {
+ return type;
+ }
+ @Override
+ public DateTime getRequestedDate() {
+ return requestedDate;
+ }
+ @Override
+ public PlanPhaseSpecifier getPlanPhaseSpecifier() {
+ return spec;
+ }
+ };
+ }
+
+ protected void sortEventsOnBundle(final BundleRepair bundle) {
+ if (bundle.getSubscriptions() == null) {
+ return;
+ }
+ for (SubscriptionRepair cur : bundle.getSubscriptions()) {
+ if (cur.getExistingEvents() != null) {
+ sortExistingEvent(cur.getExistingEvents());
+ }
+ if (cur.getNewEvents() != null) {
+ sortNewEvent(cur.getNewEvents());
+ }
+ }
+ }
+
+ protected void sortExistingEvent(final List<ExistingEvent> events) {
+ Collections.sort(events, new Comparator<ExistingEvent>() {
+ @Override
+ public int compare(ExistingEvent arg0, ExistingEvent arg1) {
+ return arg0.getEffectiveDate().compareTo(arg1.getEffectiveDate());
+ }
+ });
+ }
+ protected void sortNewEvent(final List<NewEvent> events) {
+ Collections.sort(events, new Comparator<NewEvent>() {
+ @Override
+ public int compare(NewEvent arg0, NewEvent arg1) {
+ return arg0.getRequestedDate().compareTo(arg1.getRequestedDate());
+ }
+ });
+ }
+
+
+}
beatrix/src/test/resources/catalogSample.xml 585(+395 -190)
diff --git a/beatrix/src/test/resources/catalogSample.xml b/beatrix/src/test/resources/catalogSample.xml
index 5b7aeaf..8c13707 100644
--- a/beatrix/src/test/resources/catalogSample.xml
+++ b/beatrix/src/test/resources/catalogSample.xml
@@ -1,43 +1,23 @@
<?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.
- -->
+<!-- ~ 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, Holster)
- 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 recurring and a one off (refurbish-maintenance)
- Plan with more than 2 phase (gunclub discount plans)
-
-Use Cases to do:
- Tiered Add On
- Riskfree period
-
-
-
- -->
+<!-- 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">
+ xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
<effectiveDate>2011-01-01T00:00:00+00:00</effectiveDate>
<catalogName>Firearms</catalogName>
@@ -47,24 +27,24 @@ Use Cases to do:
<currency>EUR</currency>
<currency>GBP</currency>
</currencies>
-
+
<products>
+ <product name="Blowdart">
+ <category>BASE</category>
+ </product>
<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="Blowdart">
- <category>BASE</category>
- </product>
- <product name="Shotgun">
- <category>BASE</category>
- </product>
<product name="Assault-Rifle">
<category>BASE</category>
- <included>
+ <included>
<addonProduct>Telescopic-Scope</addonProduct>
</included>
<available>
@@ -87,60 +67,42 @@ Use Cases to do:
<category>ADD_ON</category>
</product>
</products>
-
+
<rules>
<changePolicy>
- <changePolicyCase>
+ <changePolicyCase>
<phaseType>TRIAL</phaseType>
<policy>IMMEDIATE</policy>
</changePolicyCase>
- <changePolicyCase>
- <toProduct>Pistol</toProduct>
- <policy>END_OF_TERM</policy>
+ <changePolicyCase>
+ <toProduct>Assault-Rifle</toProduct>
+ <policy>IMMEDIATE</policy>
</changePolicyCase>
- <changePolicyCase>
- <toPriceList>rescue</toPriceList>
- <policy>END_OF_TERM</policy>
- </changePolicyCase>
- <changePolicyCase>
+ <changePolicyCase>
<fromProduct>Pistol</fromProduct>
<toProduct>Shotgun</toProduct>
<policy>IMMEDIATE</policy>
</changePolicyCase>
- <changePolicyCase>
- <fromProduct>Assault-Rifle</fromProduct>
- <toProduct>Shotgun</toProduct>
- <policy>END_OF_TERM</policy>
- </changePolicyCase>
- <changePolicyCase>
- <fromBillingPeriod>MONTHLY</fromBillingPeriod>
- <toProduct>Assault-Rifle</toProduct>
- <toBillingPeriod>MONTHLY</toBillingPeriod>
+ <changePolicyCase>
+ <toPriceList>rescue</toPriceList>
<policy>END_OF_TERM</policy>
</changePolicyCase>
- <changePolicyCase>
- <toProduct>Assault-Rifle</toProduct>
- <policy>IMMEDIATE</policy>
- </changePolicyCase>
- <changePolicyCase>
+ <changePolicyCase>
<fromBillingPeriod>MONTHLY</fromBillingPeriod>
<toBillingPeriod>ANNUAL</toBillingPeriod>
<policy>IMMEDIATE</policy>
</changePolicyCase>
- <changePolicyCase>
+ <changePolicyCase>
<fromBillingPeriod>ANNUAL</fromBillingPeriod>
<toBillingPeriod>MONTHLY</toBillingPeriod>
<policy>END_OF_TERM</policy>
</changePolicyCase>
- <changePolicyCase>
+ <changePolicyCase>
<policy>END_OF_TERM</policy>
</changePolicyCase>
</changePolicy>
<changeAlignment>
<changeAlignmentCase>
- <alignment>START_OF_SUBSCRIPTION</alignment>
- </changeAlignmentCase>
- <changeAlignmentCase>
<toPriceList>rescue</toPriceList>
<alignment>CHANGE_OF_PLAN</alignment>
</changeAlignmentCase>
@@ -149,18 +111,25 @@ Use Cases to do:
<toPriceList>rescue</toPriceList>
<alignment>CHANGE_OF_PRICELIST</alignment>
</changeAlignmentCase>
+ <changeAlignmentCase>
+ <alignment>START_OF_SUBSCRIPTION</alignment>
+ </changeAlignmentCase>
</changeAlignment>
<cancelPolicy>
<cancelPolicyCase>
- <policy>END_OF_TERM</policy>
- </cancelPolicyCase>
- <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>
@@ -186,21 +155,7 @@ Use Cases to do:
</rules>
<plans>
- <plan name="pistol-monthly-no-trial">
- <product>Pistol</product>
- <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="blowdart-monthly">
+ <plan name="blowdart-monthly">
<product>Blowdart</product>
<initialPhases>
<phase type="TRIAL">
@@ -219,9 +174,18 @@ Use Cases to do:
</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>
+ <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>
@@ -231,25 +195,34 @@ Use Cases to do:
</duration>
<billingPeriod>MONTHLY</billingPeriod>
<recurringPrice>
- <price><currency>USD</currency><value>29.95</value></price>
- <price><currency>EUR</currency><value>29.95</value></price>
- <price><currency>GBP</currency><value>29.95</value></price>
+ <price>
+ <currency>USD</currency>
+ <value>29.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>29.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>29.95</value>
+ </price>
</recurringPrice>
</finalPhase>
</plan>
+
<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>
- </fixedPrice>
- <!-- no price implies $0 -->
- </phase>
+ <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>
@@ -257,9 +230,18 @@ Use Cases to do:
</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>
+ <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>
@@ -271,10 +253,9 @@ Use Cases to do:
<unit>DAYS</unit>
<number>30</number>
</duration>
- <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
- <fixedPrice>
- </fixedPrice>
- <!-- no price implies $0 -->
+ <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+ <fixedPrice></fixedPrice>
+ <!-- no price implies $0 -->
</phase>
</initialPhases>
<finalPhase type="EVERGREEN">
@@ -284,25 +265,33 @@ Use Cases to do:
</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>
+ <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>
- <!-- no price implies $0 -->
- </phase>
+ <phase type="TRIAL">
+ <duration>
+ <unit>DAYS</unit>
+ <number>30</number>
+ </duration>
+ <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+ <fixedPrice>
+ </fixedPrice>
+ </phase>
</initialPhases>
<finalPhase type="EVERGREEN">
<duration>
@@ -310,9 +299,18 @@ Use Cases to do:
</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>
+ <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>
@@ -335,9 +333,18 @@ Use Cases to do:
</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>
+ <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>
@@ -360,9 +367,18 @@ Use Cases to do:
</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>
+ <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>
@@ -385,9 +401,18 @@ Use Cases to do:
</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>
+ <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>
@@ -410,9 +435,18 @@ Use Cases to do:
</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>
+ <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>
@@ -422,9 +456,18 @@ Use Cases to do:
</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>
+ <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>
@@ -447,9 +490,18 @@ Use Cases to do:
</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>
+ <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>
@@ -459,9 +511,18 @@ Use Cases to do:
</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>
+ <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>
@@ -484,10 +545,19 @@ Use Cases to do:
</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>
+ <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">
@@ -496,37 +566,110 @@ Use Cases to do:
</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>
+ <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>
+ <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>
+ <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>
+ <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>
@@ -538,9 +681,18 @@ Use Cases to do:
</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>
+ <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) -->
@@ -564,9 +716,18 @@ Use Cases to do:
</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>
+ <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>
@@ -589,9 +750,18 @@ Use Cases to do:
</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>
+ <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>
@@ -605,9 +775,18 @@ Use Cases to do:
</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>
+ <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>
@@ -617,9 +796,18 @@ Use Cases to do:
</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>
+ <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>
@@ -632,23 +820,40 @@ Use Cases to do:
</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>
+ <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>
+ <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">
+ <defaultPriceList name="DEFAULT">
<plans>
- <plan>blowdart-monthly</plan>
+ <plan>blowdart-monthly</plan>
<plan>pistol-monthly</plan>
<plan>shotgun-monthly</plan>
<plan>assault-rifle-monthly</plan>
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java
index 5517eb8..5e0ded9 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java
@@ -129,6 +129,7 @@ public class PlanAligner {
// If we never had any Plan change, borrow the logics for createPlan alignment
case MIGRATE_ENTITLEMENT:
case CREATE:
+ case RE_CREATE:
List<TimedPhase> timedPhases = getTimedPhaseOnCreate(subscription.getStartDate(),
subscription.getBundleStartDate(),
lastPlanTransition.getNextPlan(),
@@ -211,11 +212,6 @@ public class PlanAligner {
Catalog catalog = catalogService.getFullCatalog();
ProductCategory currentCategory = currentPlan.getProduct().getCategory();
- // STEPH tiered ADDON not implemented yet
- if (currentCategory != ProductCategory.BASE) {
- throw new EntitlementError(String.format("Only implemented changePlan for BasePlan"));
- }
-
PlanPhaseSpecifier fromPlanPhaseSpecifier = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
currentCategory,
currentPlan.getBillingPeriod(),
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultChargeThruApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultChargeThruApi.java
index 3de7b6c..7bde4a2 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultChargeThruApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultChargeThruApi.java
@@ -26,9 +26,9 @@ import org.slf4j.LoggerFactory;
import com.google.inject.Inject;
import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.entitlement.api.user.SubscriptionData;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.entitlement.engine.dao.EntitlementDao;
import com.ning.billing.entitlement.engine.dao.SubscriptionSqlDao;
import com.ning.billing.util.ChangeType;
@@ -38,10 +38,8 @@ import com.ning.billing.util.callcontext.CallContextFactory;
public class DefaultChargeThruApi implements ChargeThruApi {
private static final Logger log = LoggerFactory.getLogger(DefaultChargeThruApi.class);
- private static final String API_USER_NAME = "Entitlement Billing Api";
- private final CallContextFactory factory;
+
private final EntitlementDao entitlementDao;
- private final AccountUserApi accountApi;
private final SubscriptionFactory subscriptionFactory;
private static final String SUBSCRIPTION_TABLE_NAME = "subscriptions";
@@ -49,10 +47,8 @@ public class DefaultChargeThruApi implements ChargeThruApi {
@Inject
public DefaultChargeThruApi(final CallContextFactory factory, final SubscriptionFactory subscriptionFactory, final EntitlementDao dao, final AccountUserApi accountApi) {
super();
- this.factory = factory;
this.subscriptionFactory = subscriptionFactory;
this.entitlementDao = dao;
- this.accountApi = accountApi;
}
@Override
@@ -67,8 +63,7 @@ public class DefaultChargeThruApi implements ChargeThruApi {
SubscriptionBuilder builder = new SubscriptionBuilder(subscription)
.setChargedThroughDate(ctd)
.setPaidThroughDate(subscription.getPaidThroughDate());
-
- entitlementDao.updateSubscription(new SubscriptionData(builder), context);
+ entitlementDao.updateChargedThroughDate(new SubscriptionData(builder), context);
}
@Override
@@ -80,17 +75,13 @@ public class DefaultChargeThruApi implements ChargeThruApi {
if (subscription == null) {
log.warn("Subscription not found when setting CTD.");
} else {
- Date paidThroughDate = (subscription.getPaidThroughDate() == null) ? null : subscription.getPaidThroughDate().toDate();
-
DateTime chargedThroughDate = subscription.getChargedThroughDate();
if (chargedThroughDate == null || chargedThroughDate.isBefore(ctd)) {
- subscriptionSqlDao.updateSubscription(subscriptionId.toString(), subscription.getActiveVersion(),
- ctd.toDate(), paidThroughDate, context);
+ subscriptionSqlDao.updateChargedThroughDate(subscriptionId.toString(),
+ ctd.toDate(), context);
AuditSqlDao auditSqlDao = transactionalDao.become(AuditSqlDao.class);
auditSqlDao.insertAuditFromTransaction(SUBSCRIPTION_TABLE_NAME, subscriptionId.toString(), ChangeType.UPDATE, context);
}
}
}
-
-
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/AccountMigrationData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/AccountMigrationData.java
index 24c4e07..07a0034 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/AccountMigrationData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/AccountMigrationData.java
@@ -20,7 +20,7 @@ import java.util.List;
import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
import com.ning.billing.entitlement.api.user.SubscriptionData;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.entitlement.events.EntitlementEvent;
import com.ning.billing.entitlement.events.user.ApiEventMigrateBilling;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
index 8390504..b09fc17 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
@@ -31,12 +31,12 @@ import com.google.inject.Inject;
import com.ning.billing.catalog.api.ProductCategory;
import com.ning.billing.entitlement.alignment.MigrationPlanAligner;
import com.ning.billing.entitlement.alignment.TimedMigration;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
import com.ning.billing.entitlement.api.migration.AccountMigrationData.BundleMigrationData;
import com.ning.billing.entitlement.api.migration.AccountMigrationData.SubscriptionMigrationData;
import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
import com.ning.billing.entitlement.api.user.SubscriptionData;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.entitlement.engine.dao.EntitlementDao;
import com.ning.billing.entitlement.events.EntitlementEvent;
import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
@@ -86,7 +86,7 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
for (final EntitlementBundleMigration curBundle : toBeMigrated.getBundles()) {
- SubscriptionBundleData bundleData = new SubscriptionBundleData(curBundle.getBundleKey(), accountId);
+ SubscriptionBundleData bundleData = new SubscriptionBundleData(curBundle.getBundleKey(), accountId, clock.getUTCNow());
List<SubscriptionMigrationData> bundleSubscriptionData = new LinkedList<AccountMigrationData.SubscriptionMigrationData>();
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultDeletedEvent.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultDeletedEvent.java
new file mode 100644
index 0000000..0a74fda
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultDeletedEvent.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.DeletedEvent;
+
+public class DefaultDeletedEvent implements DeletedEvent {
+
+ private final UUID id;
+ private final DateTime effectiveDate;
+
+ public DefaultDeletedEvent(UUID id, DateTime effectiveDate) {
+ this.id = id;
+ this.effectiveDate = effectiveDate;
+ }
+
+ @Override
+ public UUID getEventId() {
+ return id;
+ }
+
+ public DateTime getEffectiveDate() {
+ return effectiveDate;
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultEntitlementRepairApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultEntitlementRepairApi.java
new file mode 100644
index 0000000..a3efd7a
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultEntitlementRepairApi.java
@@ -0,0 +1,468 @@
+/*
+ * 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.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+import com.ning.billing.ErrorCode;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.CatalogService;
+import com.ning.billing.catalog.api.CatalogUserApi;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.ExistingEvent;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.NewEvent;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.api.user.SubscriptionEventTransition;
+import com.ning.billing.entitlement.api.user.SubscriptionTransitionData;
+import com.ning.billing.entitlement.engine.dao.EntitlementDao;
+import com.ning.billing.entitlement.events.EntitlementEvent;
+import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
+import com.ning.billing.entitlement.exceptions.EntitlementError;
+import com.ning.billing.entitlement.glue.EntitlementModule;
+import com.ning.billing.util.callcontext.CallContext;
+
+public class DefaultEntitlementRepairApi implements EntitlementRepairApi {
+
+ private final EntitlementDao dao;
+ private final SubscriptionFactory factory;
+ private final RepairEntitlementLifecycleDao repairDao;
+ private final CatalogService catalogService;
+
+
+ private enum RepairType {
+ BASE_REPAIR,
+ ADD_ON_REPAIR,
+ STANDALONE_REPAIR
+ }
+
+
+ @Inject
+ public DefaultEntitlementRepairApi(@Named(EntitlementModule.REPAIR_NAMED) final SubscriptionFactory factory, final CatalogService catalogService,
+ @Named(EntitlementModule.REPAIR_NAMED) final RepairEntitlementLifecycleDao repairDao, final EntitlementDao dao) {
+ this.catalogService = catalogService;
+ this.dao = dao;
+ this.repairDao = repairDao;
+ this.factory = factory;
+ }
+
+
+ @Override
+ public BundleRepair getBundleRepair(final UUID bundleId)
+ throws EntitlementRepairException {
+
+ try {
+ SubscriptionBundle bundle = dao.getSubscriptionBundleFromId(bundleId);
+ if (bundle == null) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_UNKNOWN_BUNDLE, bundleId);
+ }
+ final List<Subscription> subscriptions = dao.getSubscriptions(factory, bundleId);
+ if (subscriptions.size() == 0) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_NO_ACTIVE_SUBSCRIPTIONS, bundleId);
+ }
+ final String viewId = getViewId(((SubscriptionBundleData) bundle).getLastSysUpdateTime(), subscriptions);
+ final List<SubscriptionRepair> repairs = createGetSubscriptionRepairList(subscriptions, Collections.<SubscriptionRepair>emptyList());
+ return createGetBundleRepair(bundleId, viewId, repairs);
+ } catch (CatalogApiException e) {
+ throw new EntitlementRepairException(e);
+ }
+ }
+
+
+
+ @Override
+ public BundleRepair repairBundle(final BundleRepair input, final boolean dryRun, final CallContext context)
+ throws EntitlementRepairException {
+
+ try {
+
+ SubscriptionBundle bundle = dao.getSubscriptionBundleFromId(input.getBundleId());
+ if (bundle == null) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_UNKNOWN_BUNDLE, input.getBundleId());
+ }
+
+
+ // Subscriptions are ordered with BASE subscription first-- if exists
+ final List<Subscription> subscriptions = dao.getSubscriptions(factory, input.getBundleId());
+ if (subscriptions.size() == 0) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_NO_ACTIVE_SUBSCRIPTIONS, input.getBundleId());
+ }
+
+ final String viewId = getViewId(((SubscriptionBundleData) bundle).getLastSysUpdateTime(), subscriptions);
+ if (!viewId.equals(input.getViewId())) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_VIEW_CHANGED,input.getBundleId(), input.getViewId(), viewId);
+ }
+
+ DateTime firstDeletedBPEventTime = null;
+ DateTime lastRemainingBPEventTime = null;
+
+ boolean isBasePlanRecreate = false;
+ DateTime newBundleStartDate = null;
+
+ SubscriptionDataRepair baseSubscriptionRepair = null;
+ List<SubscriptionDataRepair> addOnSubscriptionInRepair = new LinkedList<SubscriptionDataRepair>();
+ List<SubscriptionDataRepair> inRepair = new LinkedList<SubscriptionDataRepair>();
+ for (Subscription cur : subscriptions) {
+
+ //
+ SubscriptionRepair curRepair = findAndCreateSubscriptionRepair(cur.getId(), input.getSubscriptions());
+ if (curRepair != null) {
+ SubscriptionDataRepair curInputRepair = ((SubscriptionDataRepair) cur);
+ final List<EntitlementEvent> remaining = getRemainingEventsAndValidateDeletedEvents(curInputRepair, firstDeletedBPEventTime, curRepair.getDeletedEvents());
+
+ final boolean isPlanRecreate = (curRepair.getNewEvents().size() > 0
+ && (curRepair.getNewEvents().get(0).getSubscriptionTransitionType() == SubscriptionTransitionType.CREATE
+ || curRepair.getNewEvents().get(0).getSubscriptionTransitionType() == SubscriptionTransitionType.RE_CREATE));
+
+ final DateTime newSubscriptionStartDate = isPlanRecreate ? curRepair.getNewEvents().get(0).getRequestedDate() : null;
+
+ if (isPlanRecreate && remaining.size() != 0) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_SUB_RECREATE_NOT_EMPTY, cur.getId(), cur.getBundleId());
+ }
+
+ if (!isPlanRecreate && remaining.size() == 0) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_SUB_EMPTY, cur.getId(), cur.getBundleId());
+ }
+
+ if (cur.getCategory() == ProductCategory.BASE) {
+
+ int bpTransitionSize =((SubscriptionData) cur).getAllTransitions().size();
+ lastRemainingBPEventTime = (remaining.size() > 0) ? curInputRepair.getAllTransitions().get(remaining.size() - 1).getEffectiveTransitionTime() : null;
+ firstDeletedBPEventTime = (remaining.size() < bpTransitionSize) ? curInputRepair.getAllTransitions().get(remaining.size()).getEffectiveTransitionTime() : null;
+
+ isBasePlanRecreate = isPlanRecreate;
+ newBundleStartDate = newSubscriptionStartDate;
+ }
+
+ if (curRepair.getNewEvents().size() > 0) {
+ DateTime lastRemainingEventTime = (remaining.size() == 0) ? null : curInputRepair.getAllTransitions().get(remaining.size() - 1).getEffectiveTransitionTime();
+ validateFirstNewEvent(curInputRepair, curRepair.getNewEvents().get(0), lastRemainingBPEventTime, lastRemainingEventTime);
+ }
+
+
+ SubscriptionDataRepair curOutputRepair = createSubscriptionDataRepair(curInputRepair, newBundleStartDate, newSubscriptionStartDate, remaining);
+ repairDao.initializeRepair(curInputRepair.getId(), remaining);
+ inRepair.add(curOutputRepair);
+ if (curOutputRepair.getCategory() == ProductCategory.ADD_ON) {
+ // Check if ADD_ON RE_CREATE is before BP start
+ if (isPlanRecreate && subscriptions.get(0).getStartDate().isAfter(curRepair.getNewEvents().get(0).getRequestedDate())) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_AO_CREATE_BEFORE_BP_START, cur.getId(), cur.getBundleId());
+ }
+ addOnSubscriptionInRepair.add(curOutputRepair);
+ } else if (curOutputRepair.getCategory() == ProductCategory.BASE) {
+ baseSubscriptionRepair = curOutputRepair;
+ }
+ }
+ }
+
+ final RepairType repairType = getRepairType(subscriptions.get(0), (baseSubscriptionRepair != null));
+ switch(repairType) {
+ case BASE_REPAIR:
+ // We need to add any existing addon that are not in the input repair list
+ for (Subscription cur : subscriptions) {
+ if (cur.getCategory() == ProductCategory.ADD_ON && !inRepair.contains(cur)) {
+ SubscriptionDataRepair curOutputRepair = createSubscriptionDataRepair((SubscriptionDataRepair) cur, newBundleStartDate, null, ((SubscriptionDataRepair) cur).getEvents());
+ repairDao.initializeRepair(curOutputRepair.getId(), ((SubscriptionDataRepair) cur).getEvents());
+ inRepair.add(curOutputRepair);
+ addOnSubscriptionInRepair.add(curOutputRepair);
+ }
+ }
+
+ break;
+ case ADD_ON_REPAIR:
+ // We need to set the baseSubscription as it is useful to calculate addon validity
+ SubscriptionDataRepair baseSubscription = (SubscriptionDataRepair) subscriptions.get(0);
+ baseSubscriptionRepair = createSubscriptionDataRepair(baseSubscription, baseSubscription.getBundleStartDate(), baseSubscription.getStartDate(), baseSubscription.getEvents());
+ break;
+ case STANDALONE_REPAIR:
+ default:
+ break;
+
+ }
+
+ validateBasePlanRecreate(isBasePlanRecreate, subscriptions, input.getSubscriptions());
+ validateInputSubscriptionsKnown(subscriptions, input.getSubscriptions());
+
+
+ Collection<NewEvent> newEvents = createOrderedNewEventInput(input.getSubscriptions());
+ Iterator<NewEvent> it = newEvents.iterator();
+ while (it.hasNext()) {
+ DefaultNewEvent cur = (DefaultNewEvent) it.next();
+ SubscriptionDataRepair curDataRepair = findSubscriptionDataRepair(cur.getSubscriptionId(), inRepair);
+ if (curDataRepair == null) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_UNKNOWN_SUBSCRIPTION, cur.getSubscriptionId());
+ }
+ curDataRepair.addNewRepairEvent(cur, baseSubscriptionRepair, addOnSubscriptionInRepair, context);
+ }
+
+ if (dryRun) {
+
+ baseSubscriptionRepair.addFutureAddonCancellation(addOnSubscriptionInRepair, context);
+
+ final List<SubscriptionRepair> repairs = createGetSubscriptionRepairList(subscriptions, convertDataRepair(inRepair));
+ return createGetBundleRepair(input.getBundleId(), input.getViewId(), repairs);
+ } else {
+ dao.repair(bundle.getAccountId(), input.getBundleId(), inRepair, context);
+ return getBundleRepair(input.getBundleId());
+ }
+ } catch (CatalogApiException e) {
+ throw new EntitlementRepairException(e);
+ } finally {
+ repairDao.cleanup();
+ }
+ }
+
+
+
+
+ private RepairType getRepairType(final Subscription firstSubscription, final boolean gotBaseSubscription) {
+ if (firstSubscription.getCategory() == ProductCategory.BASE) {
+ return gotBaseSubscription ? RepairType.BASE_REPAIR : RepairType.ADD_ON_REPAIR;
+ } else {
+ return RepairType.STANDALONE_REPAIR;
+ }
+ }
+
+ private void validateBasePlanRecreate(boolean isBasePlanRecreate, List<Subscription> subscriptions, List<SubscriptionRepair> input)
+ throws EntitlementRepairException {
+
+ if (!isBasePlanRecreate) {
+ return;
+ }
+ if (subscriptions.size() != input.size()) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_BP_RECREATE_MISSING_AO, subscriptions.get(0).getBundleId());
+ }
+ for (SubscriptionRepair cur : input) {
+ if (cur.getNewEvents().size() != 0
+ && (cur.getNewEvents().get(0).getSubscriptionTransitionType() != SubscriptionTransitionType.CREATE
+ && cur.getNewEvents().get(0).getSubscriptionTransitionType() != SubscriptionTransitionType.RE_CREATE)) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_BP_RECREATE_MISSING_AO_CREATE, subscriptions.get(0).getBundleId());
+ }
+ }
+ }
+
+
+ private void validateInputSubscriptionsKnown(List<Subscription> subscriptions, List<SubscriptionRepair> input)
+ throws EntitlementRepairException {
+
+ for (SubscriptionRepair cur : input) {
+ boolean found = false;
+ for (Subscription s : subscriptions) {
+ if (s.getId().equals(cur.getId())) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_UNKNOWN_SUBSCRIPTION, cur.getId());
+ }
+ }
+ }
+
+ private void validateFirstNewEvent(final SubscriptionData data, final NewEvent firstNewEvent, final DateTime lastBPRemainingTime, final DateTime lastRemainingTime)
+ throws EntitlementRepairException {
+ if (lastBPRemainingTime != null &&
+ firstNewEvent.getRequestedDate().isBefore(lastBPRemainingTime)) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_NEW_EVENT_BEFORE_LAST_BP_REMAINING, firstNewEvent.getSubscriptionTransitionType(), data.getId());
+ }
+ if (lastRemainingTime != null &&
+ firstNewEvent.getRequestedDate().isBefore(lastRemainingTime)) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_NEW_EVENT_BEFORE_LAST_AO_REMAINING, firstNewEvent.getSubscriptionTransitionType(), data.getId());
+ }
+
+ }
+
+ private Collection<NewEvent> createOrderedNewEventInput(List<SubscriptionRepair> subscriptionsReapir) {
+ TreeSet<NewEvent> newEventSet = new TreeSet<SubscriptionRepair.NewEvent>(new Comparator<NewEvent>() {
+ @Override
+ public int compare(NewEvent o1, NewEvent o2) {
+ return o1.getRequestedDate().compareTo(o2.getRequestedDate());
+ }
+ });
+ for (SubscriptionRepair cur : subscriptionsReapir) {
+ for (NewEvent e : cur.getNewEvents()) {
+ newEventSet.add(new DefaultNewEvent(cur.getId(), e.getPlanPhaseSpecifier(), e.getRequestedDate(), e.getSubscriptionTransitionType()));
+ }
+ }
+ return newEventSet;
+ }
+
+
+ private List<EntitlementEvent> getRemainingEventsAndValidateDeletedEvents(final SubscriptionDataRepair data, final DateTime firstBPDeletedTime,
+ final List<SubscriptionRepair.DeletedEvent> deletedEvents)
+ throws EntitlementRepairException {
+
+ if (deletedEvents == null || deletedEvents.size() == 0) {
+ return data.getEvents();
+ }
+
+ int nbDeleted = 0;
+ LinkedList<EntitlementEvent> result = new LinkedList<EntitlementEvent>();
+ for (EntitlementEvent cur : data.getEvents()) {
+
+ boolean foundDeletedEvent = false;
+ for (SubscriptionRepair.DeletedEvent d : deletedEvents) {
+ if (cur.getId().equals(d.getEventId())) {
+ foundDeletedEvent = true;
+ nbDeleted++;
+ break;
+ }
+ }
+ if (!foundDeletedEvent && nbDeleted > 0) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_INVALID_DELETE_SET, cur.getId(), data.getId());
+ }
+ if (firstBPDeletedTime != null &&
+ ! cur.getEffectiveDate().isBefore(firstBPDeletedTime) &&
+ ! foundDeletedEvent) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_MISSING_AO_DELETE_EVENT, cur.getId(), data.getId());
+ }
+
+ if (nbDeleted == 0) {
+ result.add(cur);
+ }
+ }
+ if (nbDeleted != deletedEvents.size()) {
+ for (SubscriptionRepair.DeletedEvent d : deletedEvents) {
+ boolean found = false;
+ for (SubscriptionTransitionData cur : data.getAllTransitions()) {
+ if (cur.getId().equals(d.getEventId())) {
+ found = true;
+ continue;
+ }
+ }
+ if (!found) {
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_NON_EXISTENT_DELETE_EVENT, d.getEventId(), data.getId());
+ }
+ }
+
+ }
+ return result;
+ }
+
+
+ private String getViewId(DateTime lastUpdateBundleDate, List<Subscription> subscriptions) {
+ StringBuilder tmp = new StringBuilder();
+ long lastOrderedId = -1;
+ for (Subscription cur : subscriptions) {
+ lastOrderedId = lastOrderedId < ((SubscriptionData) cur).getLastEventOrderedId() ? ((SubscriptionData) cur).getLastEventOrderedId() : lastOrderedId;
+ }
+ tmp.append(lastOrderedId);
+ tmp.append("-");
+ tmp.append(lastUpdateBundleDate.toDate().getTime());
+ return tmp.toString();
+ }
+
+ private BundleRepair createGetBundleRepair(final UUID bundleId, final String viewId, final List<SubscriptionRepair> repairList) {
+ return new BundleRepair() {
+ @Override
+ public String getViewId() {
+ return viewId;
+ }
+ @Override
+ public List<SubscriptionRepair> getSubscriptions() {
+ return repairList;
+ }
+ @Override
+ public UUID getBundleId() {
+ return bundleId;
+ }
+ };
+
+ }
+
+ private List<SubscriptionRepair> createGetSubscriptionRepairList(final List<Subscription> subscriptions, final List<SubscriptionRepair> inRepair) throws CatalogApiException {
+
+ final List<SubscriptionRepair> result = new LinkedList<SubscriptionRepair>();
+ Set<UUID> repairIds = new TreeSet<UUID>();
+ for (final SubscriptionRepair cur : inRepair) {
+ repairIds.add(cur.getId());
+ result.add(cur);
+ }
+ for (final Subscription cur : subscriptions) {
+ if ( !repairIds.contains(cur.getId())) {
+ result.add(new DefaultSubscriptionRepair((SubscriptionDataRepair) cur, catalogService.getFullCatalog()));
+ }
+ }
+ return result;
+ }
+
+
+ private List<SubscriptionRepair> convertDataRepair(List<SubscriptionDataRepair> input) throws CatalogApiException {
+ List<SubscriptionRepair> result = new LinkedList<SubscriptionRepair>();
+ for (SubscriptionDataRepair cur : input) {
+ result.add(new DefaultSubscriptionRepair(cur, catalogService.getFullCatalog()));
+ }
+ return result;
+ }
+
+ private SubscriptionDataRepair findSubscriptionDataRepair(final UUID targetId, final List<SubscriptionDataRepair> input) {
+ for (SubscriptionDataRepair cur : input) {
+ if (cur.getId().equals(targetId)) {
+ return cur;
+ }
+ }
+ return null;
+ }
+
+
+ private SubscriptionDataRepair createSubscriptionDataRepair(final SubscriptionData curData, final DateTime newBundleStartDate, final DateTime newSubscriptionStartDate, final List<EntitlementEvent> initialEvents) {
+ SubscriptionBuilder builder = new SubscriptionBuilder(curData);
+ builder.setActiveVersion(curData.getActiveVersion() + 1);
+ if (newBundleStartDate != null) {
+ builder.setBundleStartDate(newBundleStartDate);
+ }
+ if (newSubscriptionStartDate != null) {
+ builder.setStartDate(newSubscriptionStartDate);
+ }
+ if (initialEvents.size() > 0) {
+ for (EntitlementEvent cur : initialEvents) {
+ cur.setActiveVersion(builder.getActiveVersion());
+ }
+ }
+ SubscriptionDataRepair result = (SubscriptionDataRepair) factory.createSubscription(builder, initialEvents);
+ return result;
+ }
+
+
+ private SubscriptionRepair findAndCreateSubscriptionRepair(final UUID target, final List<SubscriptionRepair> input) {
+ for (SubscriptionRepair cur : input) {
+ if (target.equals(cur.getId())) {
+ return new DefaultSubscriptionRepair(cur);
+ }
+ }
+ return null;
+ }
+}
+
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultNewEvent.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultNewEvent.java
new file mode 100644
index 0000000..8b2e3bf
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultNewEvent.java
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.NewEvent;
+
+public class DefaultNewEvent implements NewEvent {
+
+ private final UUID subscriptionId;
+ private final PlanPhaseSpecifier spec;
+ private final DateTime requestedDate;
+ private final SubscriptionTransitionType transitionType;
+
+ public DefaultNewEvent(final UUID subscriptionId, final PlanPhaseSpecifier spec, final DateTime requestedDate, final SubscriptionTransitionType transitionType) {
+ this.subscriptionId = subscriptionId;
+ this.spec = spec;
+ this.requestedDate = requestedDate;
+ this.transitionType = transitionType;
+ }
+
+ @Override
+ public PlanPhaseSpecifier getPlanPhaseSpecifier() {
+ return spec;
+ }
+
+ @Override
+ public DateTime getRequestedDate() {
+ return requestedDate;
+ }
+
+ @Override
+ public SubscriptionTransitionType getSubscriptionTransitionType() {
+ return transitionType;
+ }
+
+ public UUID getSubscriptionId() {
+ return subscriptionId;
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultRepairEntitlementEvent.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultRepairEntitlementEvent.java
new file mode 100644
index 0000000..47a475c
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultRepairEntitlementEvent.java
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+public class DefaultRepairEntitlementEvent implements RepairEntitlementEvent {
+
+ private final UUID userToken;
+ private final UUID bundleId;
+ private final UUID accountId;
+ private final DateTime efectiveDate;
+
+
+ public DefaultRepairEntitlementEvent(final UUID userToken, final UUID acountId, final UUID bundleId, final DateTime efectiveDate) {
+ this.userToken = userToken;
+ this.bundleId = bundleId;
+ this.accountId = acountId;
+ this.efectiveDate = efectiveDate;
+ }
+
+ @Override
+ public BusEventType getBusEventType() {
+ return BusEventType.BUNDLE_REPAIR;
+ }
+
+ @Override
+ public UUID getUserToken() {
+ return userToken;
+ }
+
+ @Override
+ public UUID getBundleId() {
+ return bundleId;
+ }
+
+ @Override
+ public UUID getAccountId() {
+ return accountId;
+ }
+
+ @Override
+ public DateTime getEffectiveDate() {
+ return efectiveDate;
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultSubscriptionRepair.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultSubscriptionRepair.java
new file mode 100644
index 0000000..b879225
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultSubscriptionRepair.java
@@ -0,0 +1,288 @@
+/*
+ * 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.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.Catalog;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.user.SubscriptionTransitionData;
+import com.ning.billing.entitlement.events.EntitlementEvent;
+import com.ning.billing.entitlement.events.phase.PhaseEvent;
+import com.ning.billing.entitlement.events.user.ApiEvent;
+import com.ning.billing.entitlement.events.user.ApiEventType;
+
+public class DefaultSubscriptionRepair implements SubscriptionRepair {
+
+ private final UUID id;
+ private final List<ExistingEvent> existingEvents;
+ private final List<NewEvent> newEvents;
+ private final List<DeletedEvent> deletedEvents;
+
+ public DefaultSubscriptionRepair(final UUID id) {
+ this.id = id;
+ this.existingEvents = Collections.<SubscriptionRepair.ExistingEvent>emptyList();
+ this.deletedEvents = Collections.<SubscriptionRepair.DeletedEvent>emptyList();
+ this.newEvents = Collections.<SubscriptionRepair.NewEvent>emptyList();
+ }
+
+ public DefaultSubscriptionRepair(SubscriptionRepair input) {
+ this.id = input.getId();
+ this.existingEvents = (input.getExistingEvents() != null) ? new ArrayList<SubscriptionRepair.ExistingEvent>(input.getExistingEvents()) :
+ Collections.<SubscriptionRepair.ExistingEvent>emptyList();
+ sortExistingEvent(this.existingEvents);
+ this.deletedEvents = (input.getDeletedEvents() != null) ? new ArrayList<SubscriptionRepair.DeletedEvent>(input.getDeletedEvents()) :
+ Collections.<SubscriptionRepair.DeletedEvent>emptyList();
+ this.newEvents = (input.getNewEvents() != null) ? new ArrayList<SubscriptionRepair.NewEvent>(input.getNewEvents()) :
+ Collections.<SubscriptionRepair.NewEvent>emptyList();
+ sortNewEvent(this.newEvents);
+ }
+
+ // CTOR for returning events only
+ public DefaultSubscriptionRepair(SubscriptionDataRepair input, Catalog catalog) throws CatalogApiException {
+ this.id = input.getId();
+ this.existingEvents = toExistingEvents(catalog, input.getActiveVersion(), input.getCategory(), input.getEvents());
+ this.deletedEvents = null;
+ this.newEvents = null;
+ }
+
+ private List<ExistingEvent> toExistingEvents(final Catalog catalog, final long activeVersion, final ProductCategory category, final List<EntitlementEvent> events)
+ throws CatalogApiException {
+
+ List<ExistingEvent> result = new LinkedList<SubscriptionRepair.ExistingEvent>();
+
+ String prevProductName = null;
+ BillingPeriod prevBillingPeriod = null;
+ String prevPriceListName = null;
+ PhaseType prevPhaseType = null;
+
+ DateTime startDate = null;
+
+ for (final EntitlementEvent cur : events) {
+
+ // First active event is used to figure out which catalog version to use.
+ //startDate = (startDate == null && cur.getActiveVersion() == activeVersion) ? cur.getEffectiveDate() : startDate;
+
+ // STEPH that needs tp be reviewed if we support mutli version events
+ if (cur.getActiveVersion() != activeVersion) {
+ continue;
+ }
+ startDate = (startDate == null) ? cur.getEffectiveDate() : startDate;
+
+
+ String productName = null;
+ BillingPeriod billingPeriod = null;
+ String priceListName = null;
+ PhaseType phaseType = null;
+
+ ApiEventType apiType = null;
+ switch (cur.getType()) {
+ case PHASE:
+ PhaseEvent phaseEV = (PhaseEvent) cur;
+ phaseType = catalog.findPhase(phaseEV.getPhase(), cur.getEffectiveDate(), startDate).getPhaseType();
+ productName = prevProductName;
+ billingPeriod = catalog.findPhase(phaseEV.getPhase(), cur.getEffectiveDate(), startDate).getBillingPeriod();
+ priceListName = prevPriceListName;
+ break;
+
+ case API_USER:
+ ApiEvent userEV = (ApiEvent) cur;
+ apiType = userEV.getEventType();
+ Plan plan = (userEV.getEventPlan() != null) ? catalog.findPlan(userEV.getEventPlan(), cur.getRequestedDate(), startDate) : null;
+ phaseType = (userEV.getEventPlanPhase() != null) ? catalog.findPhase(userEV.getEventPlanPhase(), cur.getEffectiveDate(), startDate).getPhaseType() : prevPhaseType;
+ productName = (plan != null) ? plan.getProduct().getName() : prevProductName;
+ billingPeriod = (userEV.getEventPlanPhase() != null) ? catalog.findPhase(userEV.getEventPlanPhase(), cur.getEffectiveDate(), startDate).getBillingPeriod() : prevBillingPeriod;
+ priceListName = (userEV.getPriceList() != null) ? userEV.getPriceList() : prevPriceListName;
+ break;
+ }
+
+ final SubscriptionTransitionType transitionType = SubscriptionTransitionData.toSubscriptionTransitionType(cur.getType(), apiType);
+
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, category, billingPeriod, priceListName, phaseType);
+ result.add(new ExistingEvent() {
+ @Override
+ public SubscriptionTransitionType getSubscriptionTransitionType() {
+ return transitionType;
+ }
+ @Override
+ public DateTime getRequestedDate() {
+ return cur.getRequestedDate();
+ }
+ @Override
+ public PlanPhaseSpecifier getPlanPhaseSpecifier() {
+ return spec;
+ }
+ @Override
+ public UUID getEventId() {
+ return cur.getId();
+ }
+ @Override
+ public DateTime getEffectiveDate() {
+ return cur.getEffectiveDate();
+ }
+ });
+
+ prevProductName = productName;
+ prevBillingPeriod = billingPeriod;
+ prevPriceListName = priceListName;
+ prevPhaseType = phaseType;
+
+ }
+ sortExistingEvent(result);
+ return result;
+ }
+
+
+ /*
+
+ private List<ExistingEvent> toExistingEvents(final Catalog catalog, final long processingVersion, final ProductCategory category, final List<EntitlementEvent> events, List<ExistingEvent> result)
+ throws CatalogApiException {
+
+
+ String prevProductName = null;
+ BillingPeriod prevBillingPeriod = null;
+ String prevPriceListName = null;
+ PhaseType prevPhaseType = null;
+
+ DateTime startDate = null;
+
+ for (final EntitlementEvent cur : events) {
+
+ if (processingVersion != cur.getActiveVersion()) {
+ continue;
+ }
+
+ // First active event is used to figure out which catalog version to use.
+ startDate = (startDate == null && cur.getActiveVersion() == processingVersion) ? cur.getEffectiveDate() : startDate;
+
+ String productName = null;
+ BillingPeriod billingPeriod = null;
+ String priceListName = null;
+ PhaseType phaseType = null;
+
+ ApiEventType apiType = null;
+ switch (cur.getType()) {
+ case PHASE:
+ PhaseEvent phaseEV = (PhaseEvent) cur;
+ phaseType = catalog.findPhase(phaseEV.getPhase(), cur.getEffectiveDate(), startDate).getPhaseType();
+ productName = prevProductName;
+ billingPeriod = prevBillingPeriod;
+ priceListName = prevPriceListName;
+ break;
+
+ case API_USER:
+ ApiEvent userEV = (ApiEvent) cur;
+ apiType = userEV.getEventType();
+ Plan plan = (userEV.getEventPlan() != null) ? catalog.findPlan(userEV.getEventPlan(), cur.getRequestedDate(), startDate) : null;
+ phaseType = (userEV.getEventPlanPhase() != null) ? catalog.findPhase(userEV.getEventPlanPhase(), cur.getEffectiveDate(), startDate).getPhaseType() : prevPhaseType;
+ productName = (plan != null) ? plan.getProduct().getName() : prevProductName;
+ billingPeriod = (plan != null) ? plan.getBillingPeriod() : prevBillingPeriod;
+ priceListName = (userEV.getPriceList() != null) ? userEV.getPriceList() : prevPriceListName;
+ break;
+ }
+
+ final SubscriptionTransitionType transitionType = SubscriptionTransitionData.toSubscriptionTransitionType(cur.getType(), apiType);
+
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, category, billingPeriod, priceListName, phaseType);
+ result.add(new ExistingEvent() {
+ @Override
+ public SubscriptionTransitionType getSubscriptionTransitionType() {
+ return transitionType;
+ }
+ @Override
+ public DateTime getRequestedDate() {
+ return cur.getRequestedDate();
+ }
+ @Override
+ public PlanPhaseSpecifier getPlanPhaseSpecifier() {
+ return spec;
+ }
+ @Override
+ public UUID getEventId() {
+ return cur.getId();
+ }
+ @Override
+ public DateTime getEffectiveDate() {
+ return cur.getEffectiveDate();
+ }
+ });
+ prevProductName = productName;
+ prevBillingPeriod = billingPeriod;
+ prevPriceListName = priceListName;
+ prevPhaseType = phaseType;
+ }
+ }
+ */
+
+
+
+
+
+
+ @Override
+ public UUID getId() {
+ return id;
+ }
+
+ @Override
+ public List<DeletedEvent> getDeletedEvents() {
+ return deletedEvents;
+ }
+
+ @Override
+ public List<NewEvent> getNewEvents() {
+ return newEvents;
+ }
+
+ @Override
+ public List<ExistingEvent> getExistingEvents() {
+ return existingEvents;
+ }
+
+ private void sortExistingEvent(final List<ExistingEvent> events) {
+ if (events != null) {
+ Collections.sort(events, new Comparator<ExistingEvent>() {
+ @Override
+ public int compare(ExistingEvent arg0, ExistingEvent arg1) {
+ return arg0.getEffectiveDate().compareTo(arg1.getEffectiveDate());
+ }
+ });
+ }
+ }
+ private void sortNewEvent(final List<NewEvent> events) {
+ if (events != null) {
+ Collections.sort(events, new Comparator<NewEvent>() {
+ @Override
+ public int compare(NewEvent arg0, NewEvent arg1) {
+ return arg0.getRequestedDate().compareTo(arg1.getRequestedDate());
+ }
+ });
+ }
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairEntitlementLifecycleDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairEntitlementLifecycleDao.java
new file mode 100644
index 0000000..cf482e5
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairEntitlementLifecycleDao.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import java.util.List;
+import java.util.UUID;
+
+import com.ning.billing.entitlement.events.EntitlementEvent;
+
+public interface RepairEntitlementLifecycleDao {
+
+ public void initializeRepair(final UUID subscriptionId, final List<EntitlementEvent> initialEvents);
+
+ public void cleanup();
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairSubscriptionApiService.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairSubscriptionApiService.java
new file mode 100644
index 0000000..dbcca32
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairSubscriptionApiService.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+import com.ning.billing.catalog.api.CatalogService;
+import com.ning.billing.entitlement.alignment.PlanAligner;
+import com.ning.billing.entitlement.api.SubscriptionApiService;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionApiService;
+import com.ning.billing.entitlement.engine.dao.EntitlementDao;
+import com.ning.billing.entitlement.glue.EntitlementModule;
+import com.ning.billing.util.clock.Clock;
+
+public class RepairSubscriptionApiService extends DefaultSubscriptionApiService implements SubscriptionApiService {
+
+ @Inject
+ public RepairSubscriptionApiService(Clock clock, @Named(EntitlementModule.REPAIR_NAMED) EntitlementDao dao,
+ CatalogService catalogService, PlanAligner planAligner) {
+ super(clock, dao, catalogService, planAligner);
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairSubscriptionFactory.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairSubscriptionFactory.java
new file mode 100644
index 0000000..01a9328
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/RepairSubscriptionFactory.java
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import java.util.List;
+
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+import com.ning.billing.catalog.api.CatalogService;
+import com.ning.billing.entitlement.api.SubscriptionApiService;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionApiService;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.engine.addon.AddonUtils;
+import com.ning.billing.entitlement.engine.dao.EntitlementDao;
+import com.ning.billing.entitlement.events.EntitlementEvent;
+import com.ning.billing.entitlement.glue.EntitlementModule;
+import com.ning.billing.util.clock.Clock;
+
+public class RepairSubscriptionFactory extends DefaultSubscriptionFactory implements SubscriptionFactory {
+
+ private final AddonUtils addonUtils;
+ private final EntitlementDao repairDao;
+
+ @Inject
+ public RepairSubscriptionFactory(@Named(EntitlementModule.REPAIR_NAMED) SubscriptionApiService apiService,
+ @Named(EntitlementModule.REPAIR_NAMED) EntitlementDao dao,
+ Clock clock, CatalogService catalogService, AddonUtils addonUtils) {
+ super(apiService, clock, catalogService);
+ this.addonUtils = addonUtils;
+ this.repairDao = dao;
+ }
+
+ @Override
+ public SubscriptionData createSubscription(SubscriptionBuilder builder,
+ List<EntitlementEvent> events) {
+ SubscriptionData subscription = new SubscriptionDataRepair(builder, events, apiService, repairDao, clock, addonUtils, catalogService);
+ subscription.rebuildTransitions(events, catalogService.getFullCatalog());
+ return subscription;
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/SubscriptionDataRepair.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/SubscriptionDataRepair.java
new file mode 100644
index 0000000..b79d658
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/SubscriptionDataRepair.java
@@ -0,0 +1,212 @@
+/*
+ * 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.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.joda.time.DateTime;
+import org.omg.CORBA.Request;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.ning.billing.ErrorCode;
+import com.ning.billing.catalog.api.Catalog;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.CatalogService;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.Product;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.SubscriptionApiService;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.SubscriptionEventTransition;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.engine.addon.AddonUtils;
+import com.ning.billing.entitlement.engine.dao.EntitlementDao;
+import com.ning.billing.entitlement.events.EntitlementEvent;
+import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
+import com.ning.billing.entitlement.events.user.ApiEventBuilder;
+import com.ning.billing.entitlement.events.user.ApiEventCancel;
+
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.clock.Clock;
+
+public class SubscriptionDataRepair extends SubscriptionData {
+
+ private final AddonUtils addonUtils;
+ private final Clock clock;
+ private final EntitlementDao repairDao;
+ private final CatalogService catalogService;
+
+ private final List<EntitlementEvent> initialEvents;
+
+ // Low level events are ONLY used for Repair APIs
+ protected List<EntitlementEvent> events;
+
+
+ public SubscriptionDataRepair(SubscriptionBuilder builder, List<EntitlementEvent> initialEvents, SubscriptionApiService apiService,
+ EntitlementDao dao, Clock clock, AddonUtils addonUtils, CatalogService catalogService) {
+ super(builder, apiService, clock);
+ this.repairDao = dao;
+ this.addonUtils = addonUtils;
+ this.clock = clock;
+ this.catalogService = catalogService;
+ this.initialEvents = initialEvents;
+ }
+
+
+ DateTime getLastUserEventEffectiveDate() {
+ DateTime res = null;
+ for (EntitlementEvent cur : events) {
+ if (cur.getActiveVersion() != getActiveVersion()) {
+ break;
+ }
+ if (cur.getType() == EventType.PHASE) {
+ continue;
+ }
+ res = cur.getEffectiveDate();
+ }
+ return res;
+ }
+
+ public void addNewRepairEvent(final DefaultNewEvent input, final SubscriptionDataRepair baseSubscription, final List<SubscriptionDataRepair> addonSubscriptions, final CallContext context)
+ throws EntitlementRepairException {
+
+ try {
+ final PlanPhaseSpecifier spec = input.getPlanPhaseSpecifier();
+ switch(input.getSubscriptionTransitionType()) {
+ case CREATE:
+ case RE_CREATE:
+ recreate(spec, input.getRequestedDate(), context);
+ checkAddonRights(baseSubscription);
+ break;
+ case CHANGE:
+ changePlan(spec.getProductName(), spec.getBillingPeriod(), spec.getPriceListName(), input.getRequestedDate(), context);
+ checkAddonRights(baseSubscription);
+ trickleDownBPEffectForAddon(addonSubscriptions, getLastUserEventEffectiveDate(), context);
+ break;
+ case CANCEL:
+ cancel(input.getRequestedDate(), false, context);
+ trickleDownBPEffectForAddon(addonSubscriptions, getLastUserEventEffectiveDate(), context);
+ break;
+ case PHASE:
+ break;
+ default:
+ throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_UNKNOWN_TYPE, input.getSubscriptionTransitionType(), id);
+ }
+ } catch (EntitlementUserApiException e) {
+ throw new EntitlementRepairException(e);
+ } catch (CatalogApiException e) {
+ throw new EntitlementRepairException(e);
+ }
+ }
+
+
+ public void addFutureAddonCancellation(List<SubscriptionDataRepair> addOnSubscriptionInRepair, final CallContext context) {
+
+ if (category != ProductCategory.BASE) {
+ return;
+ }
+
+ SubscriptionEventTransition pendingTransition = getPendingTransition();
+ if (pendingTransition == null) {
+ return;
+ }
+ Product baseProduct = (pendingTransition.getTransitionType() == SubscriptionTransitionType.CANCEL) ? null :
+ pendingTransition.getNextPlan().getProduct();
+
+ addAddonCancellationIfRequired(addOnSubscriptionInRepair, baseProduct, pendingTransition.getEffectiveTransitionTime(), context);
+ }
+
+ private void trickleDownBPEffectForAddon(final List<SubscriptionDataRepair> addOnSubscriptionInRepair, final DateTime effectiveDate, final CallContext context)
+ throws EntitlementUserApiException {
+
+ if (category != ProductCategory.BASE) {
+ return;
+ }
+
+ Product baseProduct = (getState() == SubscriptionState.CANCELLED ) ?
+ null : getCurrentPlan().getProduct();
+ addAddonCancellationIfRequired(addOnSubscriptionInRepair, baseProduct, effectiveDate, context);
+ }
+
+
+
+ private void addAddonCancellationIfRequired(final List<SubscriptionDataRepair> addOnSubscriptionInRepair, Product baseProduct, final DateTime effectiveDate, final CallContext context) {
+
+ DateTime now = clock.getUTCNow();
+ Iterator<SubscriptionDataRepair> it = addOnSubscriptionInRepair.iterator();
+ while (it.hasNext()) {
+ SubscriptionDataRepair cur = it.next();
+ if (cur.getState() == SubscriptionState.CANCELLED ||
+ cur.getCategory() != ProductCategory.ADD_ON) {
+ continue;
+ }
+ Plan addonCurrentPlan = cur.getCurrentPlan();
+ if (baseProduct == null ||
+ addonUtils.isAddonIncluded(baseProduct, addonCurrentPlan) ||
+ ! addonUtils.isAddonAvailable(baseProduct, addonCurrentPlan)) {
+
+ EntitlementEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
+ .setSubscriptionId(cur.getId())
+ .setActiveVersion(cur.getActiveVersion())
+ .setProcessedDate(now)
+ .setEffectiveDate(effectiveDate)
+ .setRequestedDate(now)
+ .setUserToken(context.getUserToken())
+ .setFromDisk(true));
+ repairDao.cancelSubscription(cur.getId(), cancelEvent, context, 0);
+ cur.rebuildTransitions(repairDao.getEventsForSubscription(cur.getId()), catalogService.getFullCatalog());
+ }
+ }
+ }
+
+ private void checkAddonRights(final SubscriptionDataRepair baseSubscription)
+ throws EntitlementUserApiException, CatalogApiException {
+ if (category == ProductCategory.ADD_ON) {
+ addonUtils.checkAddonCreationRights(baseSubscription, getCurrentPlan());
+ }
+ }
+
+ public void rebuildTransitions(final List<EntitlementEvent> inputEvents, final Catalog catalog) {
+ this.events = inputEvents;
+ super.rebuildTransitions(inputEvents, catalog);
+ }
+
+ public List<EntitlementEvent> getEvents() {
+ return events;
+ }
+
+ public List<EntitlementEvent> getInitialEvents() {
+ return initialEvents;
+ }
+
+
+ public Collection<EntitlementEvent> getNewEvents() {
+ Collection<EntitlementEvent> newEvents = Collections2.filter(events, new Predicate<EntitlementEvent>() {
+ @Override
+ public boolean apply(EntitlementEvent input) {
+ return ! initialEvents.contains(input);
+ }
+ });
+ return newEvents;
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/SubscriptionApiService.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/SubscriptionApiService.java
new file mode 100644
index 0000000..046b982
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/SubscriptionApiService.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+package com.ning.billing.entitlement.api;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.util.callcontext.CallContext;
+
+public interface SubscriptionApiService {
+
+ public SubscriptionData createPlan(SubscriptionBuilder builder, Plan plan, PhaseType initialPhase,
+ String realPriceList, DateTime requestedDate, DateTime effectiveDate, DateTime processedDate,
+ CallContext context)
+ throws EntitlementUserApiException;
+
+ public boolean recreatePlan(SubscriptionData subscription, PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
+ throws EntitlementUserApiException;
+
+
+ public boolean cancel(SubscriptionData subscription, DateTime requestedDate, boolean eot, CallContext context)
+ throws EntitlementUserApiException;
+
+ public boolean uncancel(SubscriptionData subscription, CallContext context)
+ throws EntitlementUserApiException;
+
+ public boolean changePlan(SubscriptionData subscription, String productName, BillingPeriod term,
+ String priceList, DateTime requestedDate, CallContext context)
+ throws EntitlementUserApiException;
+
+ public void commitCustomFields(SubscriptionData subscription, CallContext context);
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/SubscriptionFactory.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/SubscriptionFactory.java
new file mode 100644
index 0000000..2b12487
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/SubscriptionFactory.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+package com.ning.billing.entitlement.api;
+
+import java.util.List;
+
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.events.EntitlementEvent;
+
+public interface SubscriptionFactory {
+
+ public SubscriptionData createSubscription(SubscriptionBuilder builder, List<EntitlementEvent> events);
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
index 59040be..115c60b 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
@@ -31,8 +31,9 @@ import com.ning.billing.catalog.api.PlanPhase;
import com.ning.billing.catalog.api.PlanPhaseSpecifier;
import com.ning.billing.catalog.api.PriceListSet;
import com.ning.billing.catalog.api.Product;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.entitlement.engine.addon.AddonUtils;
import com.ning.billing.entitlement.engine.dao.EntitlementDao;
import com.ning.billing.entitlement.exceptions.EntitlementError;
@@ -44,13 +45,13 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
private final Clock clock;
private final EntitlementDao dao;
private final CatalogService catalogService;
- private final SubscriptionApiService apiService;
+ private final DefaultSubscriptionApiService apiService;
private final AddonUtils addonUtils;
private final SubscriptionFactory subscriptionFactory;
@Inject
public DefaultEntitlementUserApi(Clock clock, EntitlementDao dao, CatalogService catalogService,
- SubscriptionApiService apiService, final SubscriptionFactory subscriptionFactory, AddonUtils addonUtils) {
+ DefaultSubscriptionApiService apiService, final SubscriptionFactory subscriptionFactory, AddonUtils addonUtils) {
super();
this.clock = clock;
this.apiService = apiService;
@@ -98,7 +99,7 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
public SubscriptionBundle createBundleForAccount(UUID accountId, String bundleName, CallContext context)
throws EntitlementUserApiException {
- SubscriptionBundleData bundle = new SubscriptionBundleData(bundleName, accountId);
+ SubscriptionBundleData bundle = new SubscriptionBundleData(bundleName, accountId, clock.getUTCNow());
return dao.createSubscriptionBundle(bundle, context);
}
@@ -147,7 +148,10 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
if (baseSubscription == null) {
throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_NO_BP, bundleId);
}
- checkAddonCreationRights(baseSubscription, plan);
+ if (effectiveDate.isBefore(baseSubscription.getStartDate())) {
+ throw new EntitlementUserApiException(ErrorCode.ENT_INVALID_REQUESTED_DATE, requestedDate.toString());
+ }
+ addonUtils.checkAddonCreationRights(baseSubscription, plan);
bundleStartDate = baseSubscription.getStartDate();
break;
case STANDALONE:
@@ -177,25 +181,6 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
}
- private void checkAddonCreationRights(SubscriptionData baseSubscription, Plan targetAddOnPlan)
- throws EntitlementUserApiException, CatalogApiException {
-
- if (baseSubscription.getState() != SubscriptionState.ACTIVE) {
- throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_AO_BP_NON_ACTIVE, targetAddOnPlan.getName());
- }
-
- Product baseProduct = baseSubscription.getCurrentPlan().getProduct();
- if (addonUtils.isAddonIncluded(baseProduct, targetAddOnPlan)) {
- throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_AO_ALREADY_INCLUDED,
- targetAddOnPlan.getName(), baseSubscription.getCurrentPlan().getProduct().getName());
- }
-
- if (!addonUtils.isAddonAvailable(baseProduct, targetAddOnPlan)) {
- throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_AO_NOT_AVAILABLE,
- targetAddOnPlan.getName(), baseSubscription.getCurrentPlan().getProduct().getName());
- }
- }
-
@Override
public DateTime getNextBillingDate(UUID accountId) {
List<SubscriptionBundle> bundles = getBundlesForAccount(accountId);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBundleData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBundleData.java
index d7b600d..219becc 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBundleData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBundleData.java
@@ -28,18 +28,24 @@ public class SubscriptionBundleData implements SubscriptionBundle {
private final String key;
private final UUID accountId;
private final DateTime startDate;
+ private final DateTime lastSysTimeUpdate;
private final OverdueState<SubscriptionBundle> overdueState;
+
+ public SubscriptionBundleData(String name, UUID accountId, DateTime startDate) {
+ this(UUID.randomUUID(), name, accountId, startDate, startDate);
+ }
- public SubscriptionBundleData(String name, UUID accountId) {
- this(UUID.randomUUID(), name, accountId, null, null);
+ public SubscriptionBundleData(UUID id, String key, UUID accountId, DateTime startDate, DateTime lastSysUpdate) {
+ this(id, key, accountId, startDate, lastSysUpdate, null);
}
- public SubscriptionBundleData(UUID id, String key, UUID accountId, DateTime startDate, OverdueState<SubscriptionBundle> overdueState) {
+ public SubscriptionBundleData(UUID id, String key, UUID accountId, DateTime startDate, DateTime lastSysUpdate, OverdueState<SubscriptionBundle> overdueState) {
super();
this.id = id;
this.key = key;
this.accountId = accountId;
this.startDate = startDate;
+ this.lastSysTimeUpdate = lastSysUpdate;
this.overdueState = overdueState;
}
@@ -58,13 +64,16 @@ public class SubscriptionBundleData implements SubscriptionBundle {
return accountId;
}
-
// STEPH do we need it ? and should we return that and when is that populated/updated?
@Override
public DateTime getStartDate() {
return startDate;
}
-
+
+ public DateTime getLastSysUpdateTime() {
+ return lastSysTimeUpdate;
+ }
+
@Override
public OverdueState<SubscriptionBundle> getOverdueState() {
return overdueState;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
index feae851..d4e1685 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
@@ -37,8 +37,9 @@ import com.ning.billing.catalog.api.PlanPhase;
import com.ning.billing.catalog.api.PlanPhaseSpecifier;
import com.ning.billing.catalog.api.PriceList;
import com.ning.billing.catalog.api.ProductCategory;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.SubscriptionApiService;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.entitlement.api.user.SubscriptionTransitionDataIterator.Kind;
import com.ning.billing.entitlement.api.user.SubscriptionTransitionDataIterator.Order;
import com.ning.billing.entitlement.api.user.SubscriptionTransitionDataIterator.TimeLimit;
@@ -53,41 +54,45 @@ import com.ning.billing.util.clock.Clock;
import com.ning.billing.util.customfield.CustomField;
import com.ning.billing.util.entity.ExtendedEntityBase;
-public class SubscriptionData extends ExtendedEntityBase implements Subscription {
+public class SubscriptionData extends ExtendedEntityBase implements
+ Subscription {
private final static Logger log = LoggerFactory.getLogger(SubscriptionData.class);
- private final Clock clock;
- private final SubscriptionApiService apiService;
+
+ protected final Clock clock;
+ protected final SubscriptionApiService apiService;
//
// Final subscription fields
//
- private final UUID bundleId;
- private final DateTime startDate;
- private final DateTime bundleStartDate;
- private final ProductCategory category;
+ protected final UUID bundleId;
+ protected final DateTime startDate;
+ protected final DateTime bundleStartDate;
+ protected final ProductCategory category;
//
- // Those can be modified through non User APIs, and a new Subscription object would be created
+ // Those can be modified through non User APIs, and a new Subscription
+ // object would be created
//
- private final long activeVersion;
- private final DateTime chargedThroughDate;
- private final DateTime paidThroughDate;
+ protected final long activeVersion;
+ protected final DateTime chargedThroughDate;
+ protected final DateTime paidThroughDate;
+
//
// User APIs (create, change, cancel,...) will recompute those each time,
// so the user holding that subscription object get the correct state when
// the call completes
//
- private LinkedList<SubscriptionTransitionData> transitions;
+ protected LinkedList<SubscriptionTransitionData> transitions;
// Transient object never returned at the API
public SubscriptionData(SubscriptionBuilder builder) {
this(builder, null, null);
}
- public SubscriptionData(SubscriptionBuilder builder, @Nullable SubscriptionApiService apiService,
- @Nullable Clock clock) {
+ public SubscriptionData(SubscriptionBuilder builder,
+ @Nullable SubscriptionApiService apiService, @Nullable Clock clock) {
super(builder.getId(), null, null);
this.apiService = apiService;
this.clock = clock;
@@ -106,7 +111,8 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
}
@Override
- public void saveFieldValue(String fieldName, @Nullable String fieldValue, CallContext context) {
+ public void saveFieldValue(String fieldName, @Nullable String fieldValue,
+ CallContext context) {
super.setFieldValue(fieldName, fieldValue);
apiService.commitCustomFields(this, context);
}
@@ -135,25 +141,28 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
@Override
public SubscriptionState getState() {
- return (getPreviousTransition() == null) ? null : getPreviousTransition().getNextState();
+ return (getPreviousTransition() == null) ? null
+ : getPreviousTransition().getNextState();
}
@Override
public PlanPhase getCurrentPhase() {
- return (getPreviousTransition() == null) ? null : getPreviousTransition().getNextPhase();
+ return (getPreviousTransition() == null) ? null
+ : getPreviousTransition().getNextPhase();
}
-
@Override
public Plan getCurrentPlan() {
- return (getPreviousTransition() == null) ? null : getPreviousTransition().getNextPlan();
+ return (getPreviousTransition() == null) ? null
+ : getPreviousTransition().getNextPlan();
}
@Override
public PriceList getCurrentPriceList() {
- return (getPreviousTransition() == null) ? null : getPreviousTransition().getNextPriceList();
- }
+ return (getPreviousTransition() == null) ? null :
+ getPreviousTransition().getNextPriceList();
+ }
@Override
public DateTime getEndDate() {
@@ -164,42 +173,32 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
return null;
}
-
@Override
- public boolean cancel(DateTime requestedDate, boolean eot, CallContext context) throws EntitlementUserApiException {
+ public boolean cancel(DateTime requestedDate, boolean eot,
+ CallContext context) throws EntitlementUserApiException {
return apiService.cancel(this, requestedDate, eot, context);
}
@Override
- public boolean uncancel(CallContext context) throws EntitlementUserApiException {
+ public boolean uncancel(CallContext context)
+ throws EntitlementUserApiException {
return apiService.uncancel(this, context);
}
@Override
public boolean changePlan(String productName, BillingPeriod term,
- String priceList, DateTime requestedDate, CallContext context) throws EntitlementUserApiException {
- return apiService.changePlan(this, productName, term, priceList, requestedDate, context);
+ String priceList, DateTime requestedDate, CallContext context)
+ throws EntitlementUserApiException {
+ return apiService.changePlan(this, productName, term, priceList,
+ requestedDate, context);
}
@Override
- public boolean recreate(PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
- throws EntitlementUserApiException {
+ public boolean recreate(PlanPhaseSpecifier spec, DateTime requestedDate,
+ CallContext context) throws EntitlementUserApiException {
return apiService.recreatePlan(this, spec, requestedDate, context);
}
- public List<SubscriptionEventTransition> getBillingTransitions() {
-
- if (transitions == null) {
- return Collections.emptyList();
- }
- List<SubscriptionEventTransition> result = new ArrayList<SubscriptionEventTransition>();
- SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(clock, transitions,
- Order.ASC_FROM_PAST, Kind.BILLING, Visibility.ALL, TimeLimit.ALL);
- while (it.hasNext()) {
- result.add(it.next());
- }
- return result;
- }
@Override
public SubscriptionEventTransition getPendingTransition() {
@@ -207,8 +206,9 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
if (transitions == null) {
return null;
}
- SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(clock, transitions,
- Order.ASC_FROM_PAST, Kind.ENTITLEMENT, Visibility.ALL, TimeLimit.FUTURE_ONLY);
+ SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(
+ clock, transitions, Order.ASC_FROM_PAST, Kind.ENTITLEMENT,
+ Visibility.ALL, TimeLimit.FUTURE_ONLY);
return it.hasNext() ? it.next() : null;
}
@@ -217,27 +217,12 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
if (transitions == null) {
return null;
}
- SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(clock, transitions,
- Order.DESC_FROM_FUTURE, Kind.ENTITLEMENT, Visibility.FROM_DISK_ONLY, TimeLimit.PAST_OR_PRESENT_ONLY);
+ SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(
+ clock, transitions, Order.DESC_FROM_FUTURE, Kind.ENTITLEMENT,
+ Visibility.FROM_DISK_ONLY, TimeLimit.PAST_OR_PRESENT_ONLY);
return it.hasNext() ? it.next() : null;
}
-
- public SubscriptionEventTransition getTransitionFromEvent(final EntitlementEvent event, final int seqId) {
- if (transitions == null || event == null) {
- return null;
- }
- for (SubscriptionEventTransition cur : transitions) {
- if (cur.getId().equals(event.getId())) {
- return new SubscriptionTransitionData((SubscriptionTransitionData) cur, seqId);
- }
- }
- return null;
- }
-
- public long getActiveVersion() {
- return activeVersion;
- }
-
+
@Override
public ProductCategory getCategory() {
return category;
@@ -257,32 +242,107 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
return paidThroughDate;
}
- public SubscriptionTransitionData getInitialTransitionForCurrentPlan() {
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result
+ + ((id == null) ? 0 : id.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ SubscriptionData other = (SubscriptionData) obj;
+ if (id == null) {
+ if (other.id != null)
+ return false;
+ } else if (!id.equals(other.id))
+ return false;
+ return true;
+ }
+
+ public List<SubscriptionEventTransition> getBillingTransitions() {
+
if (transitions == null) {
- throw new EntitlementError(String.format("No transitions for subscription %s", getId()));
+ return Collections.emptyList();
+ }
+ List<SubscriptionEventTransition> result = new ArrayList<SubscriptionEventTransition>();
+ SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(
+ clock, transitions, Order.ASC_FROM_PAST, Kind.BILLING,
+ Visibility.ALL, TimeLimit.ALL);
+ while (it.hasNext()) {
+ result.add(it.next());
+ }
+ return result;
+ }
+
+
+ public SubscriptionEventTransition getTransitionFromEvent(final EntitlementEvent event, final int seqId) {
+ if (transitions == null || event == null) {
+ return null;
}
+ for (SubscriptionEventTransition cur : transitions) {
+ if (cur.getId().equals(event.getId())) {
+ return new SubscriptionTransitionData(
+ (SubscriptionTransitionData) cur, seqId);
+ }
+ }
+ return null;
+ }
+
+ public long getLastEventOrderedId() {
+ SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(
+ clock, transitions, Order.DESC_FROM_FUTURE, Kind.ENTITLEMENT,
+ Visibility.FROM_DISK_ONLY, TimeLimit.ALL);
+ return it.hasNext() ? it.next().getTotalOrdering() : -1L;
+ }
+
+ public long getActiveVersion() {
+ return activeVersion;
+ }
+
+ public List<SubscriptionTransitionData> getAllTransitions() {
+ return transitions;
+ }
+
+ public SubscriptionTransitionData getInitialTransitionForCurrentPlan() {
+ if (transitions == null) {
+ throw new EntitlementError(String.format(
+ "No transitions for subscription %s", getId()));
+ }
- SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(clock, transitions,
- Order.DESC_FROM_FUTURE, Kind.ENTITLEMENT, Visibility.ALL, TimeLimit.PAST_OR_PRESENT_ONLY);
+ SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(
+ clock, transitions, Order.DESC_FROM_FUTURE, Kind.ENTITLEMENT,
+ Visibility.ALL, TimeLimit.PAST_OR_PRESENT_ONLY);
while (it.hasNext()) {
SubscriptionTransitionData cur = it.next();
- if (cur.getTransitionType() == SubscriptionTransitionType.CREATE ||
- cur.getTransitionType() == SubscriptionTransitionType.RE_CREATE ||
- cur.getTransitionType() == SubscriptionTransitionType.CHANGE ||
- cur.getTransitionType() == SubscriptionTransitionType.MIGRATE_ENTITLEMENT) {
+ if (cur.getTransitionType() == SubscriptionTransitionType.CREATE
+ || cur.getTransitionType() == SubscriptionTransitionType.RE_CREATE
+ || cur.getTransitionType() == SubscriptionTransitionType.CHANGE
+ || cur.getTransitionType() == SubscriptionTransitionType.MIGRATE_ENTITLEMENT) {
return cur;
}
}
- throw new EntitlementError(String.format("Failed to find InitialTransitionForCurrentPlan id = %s", getId().toString()));
+ throw new EntitlementError(String.format(
+ "Failed to find InitialTransitionForCurrentPlan id = %s",
+ getId().toString()));
}
public boolean isSubscriptionFutureCancelled() {
if (transitions == null) {
return false;
}
- SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(clock, transitions,
- Order.ASC_FROM_PAST, Kind.ENTITLEMENT, Visibility.ALL, TimeLimit.FUTURE_ONLY);
+ SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(
+ clock, transitions, Order.ASC_FROM_PAST, Kind.ENTITLEMENT,
+ Visibility.ALL, TimeLimit.FUTURE_ONLY);
while (it.hasNext()) {
SubscriptionTransitionData cur = it.next();
if (cur.getTransitionType() == SubscriptionTransitionType.CANCEL) {
@@ -292,46 +352,53 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
return false;
}
- public DateTime getPlanChangeEffectiveDate(ActionPolicy policy, DateTime requestedDate) {
+ public DateTime getPlanChangeEffectiveDate(ActionPolicy policy,
+ DateTime requestedDate) {
if (policy == ActionPolicy.IMMEDIATE) {
return requestedDate;
}
if (policy != ActionPolicy.END_OF_TERM) {
- throw new EntitlementError(String.format("Unexpected policy type %s", policy.toString()));
+ throw new EntitlementError(String.format(
+ "Unexpected policy type %s", policy.toString()));
}
if (chargedThroughDate == null) {
return requestedDate;
} else {
- return chargedThroughDate.isBefore(requestedDate) ? requestedDate : chargedThroughDate;
+ return chargedThroughDate.isBefore(requestedDate) ? requestedDate
+ : chargedThroughDate;
}
}
public DateTime getCurrentPhaseStart() {
if (transitions == null) {
- throw new EntitlementError(String.format("No transitions for subscription %s", getId()));
+ throw new EntitlementError(String.format(
+ "No transitions for subscription %s", getId()));
}
- SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(clock, transitions,
- Order.DESC_FROM_FUTURE, Kind.ENTITLEMENT, Visibility.ALL, TimeLimit.PAST_OR_PRESENT_ONLY);
+ SubscriptionTransitionDataIterator it = new SubscriptionTransitionDataIterator(
+ clock, transitions, Order.DESC_FROM_FUTURE, Kind.ENTITLEMENT,
+ Visibility.ALL, TimeLimit.PAST_OR_PRESENT_ONLY);
while (it.hasNext()) {
SubscriptionTransitionData cur = it.next();
- if (cur.getTransitionType() == SubscriptionTransitionType.PHASE ||
- cur.getTransitionType() == SubscriptionTransitionType.CREATE ||
- cur.getTransitionType() == SubscriptionTransitionType.RE_CREATE ||
- cur.getTransitionType() == SubscriptionTransitionType.CHANGE ||
- cur.getTransitionType() == SubscriptionTransitionType.MIGRATE_ENTITLEMENT) {
+ if (cur.getTransitionType() == SubscriptionTransitionType.PHASE
+ || cur.getTransitionType() == SubscriptionTransitionType.CREATE
+ || cur.getTransitionType() == SubscriptionTransitionType.RE_CREATE
+ || cur.getTransitionType() == SubscriptionTransitionType.CHANGE
+ || cur.getTransitionType() == SubscriptionTransitionType.MIGRATE_ENTITLEMENT) {
return cur.getEffectiveTransitionTime();
}
}
- throw new EntitlementError(String.format("Failed to find CurrentPhaseStart id = %s", getId().toString()));
+ throw new EntitlementError(String.format(
+ "Failed to find CurrentPhaseStart id = %s", getId().toString()));
}
- public void rebuildTransitions(final List<EntitlementEvent> events, final Catalog catalog) {
+ public void rebuildTransitions(final List<EntitlementEvent> inputEvents,
+ final Catalog catalog) {
- if (events == null) {
+ if (inputEvents == null) {
return;
}
@@ -340,7 +407,7 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
String nextPhaseName = null;
String nextPriceListName = null;
UUID nextUserToken = null;
-
+
SubscriptionState previousState = null;
PriceList previousPriceList = null;
@@ -348,7 +415,7 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
Plan previousPlan = null;
PlanPhase previousPhase = null;
- for (final EntitlementEvent cur : events) {
+ for (final EntitlementEvent cur : inputEvents) {
if (!cur.isActive() || cur.getActiveVersion() < activeVersion) {
continue;
@@ -370,8 +437,8 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
apiEventType = userEV.getEventType();
isFromDisk = userEV.isFromDisk();
nextUserToken = userEV.getUserToken();
-
- switch(apiEventType) {
+
+ switch (apiEventType) {
case MIGRATE_BILLING:
case MIGRATE_ENTITLEMENT:
case CREATE:
@@ -398,16 +465,16 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
case UNCANCEL:
break;
default:
- throw new EntitlementError(String.format("Unexpected UserEvent type = %s",
- userEV.getEventType().toString()));
+ throw new EntitlementError(String.format(
+ "Unexpected UserEvent type = %s", userEV
+ .getEventType().toString()));
}
break;
default:
- throw new EntitlementError(String.format("Unexpected Event type = %s",
- cur.getType()));
+ throw new EntitlementError(String.format(
+ "Unexpected Event type = %s", cur.getType()));
}
-
Plan nextPlan = null;
PlanPhase nextPhase = null;
PriceList nextPriceList = null;
@@ -417,27 +484,17 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
nextPhase = (nextPhaseName != null) ? catalog.findPhase(nextPhaseName, cur.getRequestedDate(), getStartDate()) : null;
nextPriceList = (nextPriceListName != null) ? catalog.findPriceList(nextPriceListName, cur.getRequestedDate()) : null;
} catch (CatalogApiException e) {
- log.error(String.format("Failed to build transition for subscription %s", id), e);
+ log.error(String.format(
+ "Failed to build transition for subscription %s", id),
+ e);
}
- SubscriptionTransitionData transition =
- new SubscriptionTransitionData(cur.getId(),
- id,
- bundleId,
- cur.getType(),
- apiEventType,
- cur.getRequestedDate(),
- cur.getEffectiveDate(),
- previousState,
- previousPlan,
- previousPhase,
- previousPriceList,
- nextState,
- nextPlan,
- nextPhase,
- nextPriceList,
- cur.getTotalOrdering(),
- nextUserToken,
- isFromDisk);
+ SubscriptionTransitionData transition = new SubscriptionTransitionData(
+ cur.getId(), id, bundleId, cur.getType(), apiEventType,
+ cur.getRequestedDate(), cur.getEffectiveDate(),
+ previousState, previousPlan, previousPhase,
+ previousPriceList, nextState, nextPlan, nextPhase,
+ nextPriceList, cur.getTotalOrdering(), nextUserToken,
+ isFromDisk);
transitions.add(transition);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionData.java
index 3e52a2e..461fe3c 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionData.java
@@ -22,6 +22,7 @@ import org.joda.time.DateTime;
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
import com.ning.billing.catalog.api.PriceList;
import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
@@ -177,6 +178,10 @@ public class SubscriptionTransitionData implements SubscriptionEventTransition {
@Override
public SubscriptionTransitionType getTransitionType() {
+ return toSubscriptionTransitionType(eventType, apiEventType);
+ }
+
+ public static SubscriptionTransitionType toSubscriptionTransitionType(EventType eventType, ApiEventType apiEventType) {
switch(eventType) {
case API_USER:
return apiEventType.getSubscriptionTransitionType();
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionDataIterator.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionDataIterator.java
index 3e5e961..355628d 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionDataIterator.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionDataIterator.java
@@ -19,7 +19,7 @@ package com.ning.billing.entitlement.api.user;
import java.util.Iterator;
import java.util.LinkedList;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
import com.ning.billing.entitlement.exceptions.EntitlementError;
import com.ning.billing.util.clock.Clock;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/addon/AddonUtils.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/addon/AddonUtils.java
index e7c5139..48a3c99 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/addon/AddonUtils.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/addon/AddonUtils.java
@@ -28,6 +28,7 @@ import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.Product;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
import com.ning.billing.entitlement.api.user.SubscriptionData;
import com.ning.billing.entitlement.api.user.SubscriptionEventTransition;
import com.ning.billing.entitlement.exceptions.EntitlementError;
@@ -43,6 +44,24 @@ public class AddonUtils {
this.catalogService = catalogService;
}
+ public void checkAddonCreationRights(SubscriptionData baseSubscription, Plan targetAddOnPlan)
+ throws EntitlementUserApiException, CatalogApiException {
+
+ if (baseSubscription.getState() != SubscriptionState.ACTIVE) {
+ throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_AO_BP_NON_ACTIVE, targetAddOnPlan.getName());
+ }
+
+ Product baseProduct = baseSubscription.getCurrentPlan().getProduct();
+ if (isAddonIncluded(baseProduct, targetAddOnPlan)) {
+ throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_AO_ALREADY_INCLUDED,
+ targetAddOnPlan.getName(), baseSubscription.getCurrentPlan().getProduct().getName());
+ }
+
+ if (!isAddonAvailable(baseProduct, targetAddOnPlan)) {
+ throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_AO_NOT_AVAILABLE,
+ targetAddOnPlan.getName(), baseSubscription.getCurrentPlan().getProduct().getName());
+ }
+ }
public boolean isAddonAvailable(final String basePlanName, final DateTime requestedDate, final Plan targetAddOnPlan) {
try {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
index 00d16df..c8f82a2 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
@@ -29,6 +29,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Inject;
+
+import com.google.inject.name.Named;
+
+
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.Product;
import com.ning.billing.catalog.api.ProductCategory;
@@ -37,16 +41,10 @@ import com.ning.billing.config.NotificationConfig;
import com.ning.billing.entitlement.alignment.PlanAligner;
import com.ning.billing.entitlement.alignment.TimedPhase;
import com.ning.billing.entitlement.api.EntitlementService;
-import com.ning.billing.entitlement.api.billing.ChargeThruApi;
-import com.ning.billing.entitlement.api.billing.DefaultChargeThruApi;
-import com.ning.billing.entitlement.api.migration.DefaultEntitlementMigrationApi;
-import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
-import com.ning.billing.entitlement.api.user.DefaultEntitlementUserApi;
-import com.ning.billing.entitlement.api.user.EntitlementUserApi;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
import com.ning.billing.entitlement.api.user.SubscriptionData;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory;
import com.ning.billing.entitlement.engine.addon.AddonUtils;
import com.ning.billing.entitlement.engine.dao.EntitlementDao;
import com.ning.billing.entitlement.events.EntitlementEvent;
@@ -57,7 +55,6 @@ import com.ning.billing.entitlement.events.user.ApiEvent;
import com.ning.billing.entitlement.events.user.ApiEventBuilder;
import com.ning.billing.entitlement.events.user.ApiEventCancel;
import com.ning.billing.entitlement.exceptions.EntitlementError;
-import com.ning.billing.junction.api.BillingApi;
import com.ning.billing.lifecycle.LifecycleHandlerType;
import com.ning.billing.lifecycle.LifecycleHandlerType.LifecycleLevel;
import com.ning.billing.util.bus.Bus;
@@ -94,7 +91,8 @@ public class Engine implements EventListener, EntitlementService {
@Inject
public Engine(Clock clock, EntitlementDao dao, PlanAligner planAligner,
- EntitlementConfig config, AddonUtils addonUtils, Bus eventBus,
+ EntitlementConfig config,
+ AddonUtils addonUtils, Bus eventBus,
NotificationQueueService notificationQueueService,
SubscriptionFactory subscriptionFactory,
CallContextFactory factory) {
@@ -135,9 +133,6 @@ public class Engine implements EventListener, EntitlementService {
final CallContext context = factory.createCallContext("SubscriptionEventQueue", CallOrigin.INTERNAL, UserType.SYSTEM, userToken);
processEventReady(event, key.getSeqId(), context);
}
-
-
-
},
new NotificationConfig() {
@Override
@@ -173,6 +168,7 @@ public class Engine implements EventListener, EntitlementService {
subscriptionEventQueue.stopQueue();
}
}
+
@Override
public void processEventReady(final EntitlementEvent event, final int seqId, final CallContext context) {
if (!event.isActive()) {
@@ -183,6 +179,11 @@ public class Engine implements EventListener, EntitlementService {
log.warn("Failed to retrieve subscription for id %s", event.getSubscriptionId());
return;
}
+ if (subscription.getActiveVersion() > event.getActiveVersion()) {
+ // Skip repaired events
+ return;
+ }
+
//
// Do any internal processing on that event before we send the event to the bus
//
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
index f6f5497..5af5294 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
@@ -18,6 +18,7 @@ package com.ning.billing.entitlement.engine.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
+import java.util.Date;
import java.util.List;
import java.util.UUID;
@@ -42,6 +43,7 @@ import com.ning.billing.util.callcontext.CallContextBinder;
import com.ning.billing.util.dao.BinderBase;
import com.ning.billing.util.dao.MapperBase;
+
@ExternalizedSqlViaStringTemplate3()
public interface BundleSqlDao extends Transactional<BundleSqlDao>, CloseMe, Transmogrifier {
@@ -49,13 +51,16 @@ public interface BundleSqlDao extends Transactional<BundleSqlDao>, CloseMe, Tran
public void insertBundle(@Bind(binder = SubscriptionBundleBinder.class) SubscriptionBundleData bundle,
@CallContextBinder final CallContext context);
+ @SqlUpdate
+ public void updateBundleLastSysTime(@Bind("id") String id, @Bind("last_sys_update_dt") Date lastSysUpdate);
+
@SqlQuery
@Mapper(ISubscriptionBundleSqlMapper.class)
public SubscriptionBundle getBundleFromId(@Bind("id") String id);
@SqlQuery
@Mapper(ISubscriptionBundleSqlMapper.class)
- public SubscriptionBundle getBundleFromKey(@Bind("name") String name);
+ public SubscriptionBundle getBundleFromKey(@Bind("external_key") String externalKey);
@SqlQuery
@Mapper(ISubscriptionBundleSqlMapper.class)
@@ -66,8 +71,9 @@ public interface BundleSqlDao extends Transactional<BundleSqlDao>, CloseMe, Tran
public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, SubscriptionBundleData bundle) {
stmt.bind("id", bundle.getId().toString());
stmt.bind("start_dt", getDate(bundle.getStartDate()));
- stmt.bind("name", bundle.getKey());
+ stmt.bind("external_key", bundle.getKey());
stmt.bind("account_id", bundle.getAccountId().toString());
+ stmt.bind("last_sys_update_dt", getDate(bundle.getLastSysUpdateTime()));
}
}
@@ -76,14 +82,13 @@ public interface BundleSqlDao extends Transactional<BundleSqlDao>, CloseMe, Tran
@Override
public SubscriptionBundle map(int arg, ResultSet r,
StatementContext ctx) throws SQLException {
-
UUID id = UUID.fromString(r.getString("id"));
- String name = r.getString("name");
+ String key = r.getString("external_key");
UUID accountId = UUID.fromString(r.getString("account_id"));
DateTime startDate = getDate(r, "start_dt");
- SubscriptionBundleData bundle = new SubscriptionBundleData(id, name, accountId, startDate, null);
+ DateTime lastSysUpdateDate = getDate(r, "last_sys_update_dt");
+ SubscriptionBundleData bundle = new SubscriptionBundleData(id, key, accountId, startDate, lastSysUpdateDate);
return bundle;
}
-
}
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
index 4415c1d..ac397b6 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
@@ -17,16 +17,18 @@
package com.ning.billing.entitlement.engine.dao;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
import com.ning.billing.entitlement.api.migration.AccountMigrationData;
+import com.ning.billing.entitlement.api.repair.SubscriptionDataRepair;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
import com.ning.billing.entitlement.api.user.SubscriptionData;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory;
import com.ning.billing.entitlement.events.EntitlementEvent;
public interface EntitlementDao {
@@ -52,13 +54,15 @@ public interface EntitlementDao {
public List<Subscription> getSubscriptionsForKey(final SubscriptionFactory factory, final String bundleKey);
// Update
- public void updateSubscription(final SubscriptionData subscription, final CallContext context);
+ public void updateChargedThroughDate(final SubscriptionData subscription, final CallContext context);
// Event apis
public void createNextPhaseEvent(final UUID subscriptionId, final EntitlementEvent nextPhase, final CallContext context);
public EntitlementEvent getEventById(final UUID eventId);
+ public Map<UUID, List<EntitlementEvent>> getEventsForBundle(final UUID bundleId);
+
public List<EntitlementEvent> getEventsForSubscription(final UUID subscriptionId);
public List<EntitlementEvent> getPendingEventsForSubscription(final UUID subscriptionId);
@@ -76,6 +80,9 @@ public interface EntitlementDao {
public void migrate(final UUID accountId, final AccountMigrationData data, final CallContext context);
+ // Repair
+ public void repair(final UUID accountId, final UUID bundleId, final List<SubscriptionDataRepair> inRepair, final CallContext context);
+
// Custom Fields
public void saveCustomFields(final SubscriptionData subscription, final CallContext context);
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
index 89d2b1e..f216561 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
@@ -21,10 +21,19 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
-import javax.annotation.Nullable;
+
+import com.ning.billing.util.ChangeType;
+import com.ning.billing.util.audit.dao.AuditSqlDao;
+import com.ning.billing.util.bus.Bus;
+import com.ning.billing.util.bus.Bus.EventBusException;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.customfield.dao.CustomFieldDao;
import org.joda.time.DateTime;
import org.skife.jdbi.v2.IDBI;
@@ -41,15 +50,18 @@ import com.ning.billing.ErrorCode;
import com.ning.billing.catalog.api.CatalogService;
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
import com.ning.billing.entitlement.api.migration.AccountMigrationData;
import com.ning.billing.entitlement.api.migration.AccountMigrationData.BundleMigrationData;
import com.ning.billing.entitlement.api.migration.AccountMigrationData.SubscriptionMigrationData;
+import com.ning.billing.entitlement.api.repair.DefaultRepairEntitlementEvent;
+import com.ning.billing.entitlement.api.repair.RepairEntitlementEvent;
+import com.ning.billing.entitlement.api.repair.SubscriptionDataRepair;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
import com.ning.billing.entitlement.api.user.SubscriptionData;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.entitlement.engine.addon.AddonUtils;
import com.ning.billing.entitlement.engine.core.Engine;
import com.ning.billing.entitlement.engine.core.EntitlementNotificationKey;
@@ -61,12 +73,8 @@ import com.ning.billing.entitlement.events.user.ApiEventCancel;
import com.ning.billing.entitlement.events.user.ApiEventChange;
import com.ning.billing.entitlement.events.user.ApiEventType;
import com.ning.billing.entitlement.exceptions.EntitlementError;
-import com.ning.billing.util.ChangeType;
-import com.ning.billing.util.audit.dao.AuditSqlDao;
-import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.clock.Clock;
import com.ning.billing.util.customfield.CustomField;
-import com.ning.billing.util.customfield.dao.CustomFieldDao;
import com.ning.billing.util.customfield.dao.CustomFieldSqlDao;
import com.ning.billing.util.notificationq.NotificationKey;
import com.ning.billing.util.notificationq.NotificationQueue;
@@ -87,20 +95,15 @@ public class EntitlementSqlDao implements EntitlementDao {
private final NotificationQueueService notificationQueueService;
private final AddonUtils addonUtils;
private final CustomFieldDao customFieldDao;
- private final CatalogService catalogService;
-
-
- //
- // We are not injecting SubscriptionFactory since that creates circular dependencies--
- // Guice would still work, but this is playing with fire.
- //
- // Instead that factory passed through API top to bottom for the call where is it needed-- where we returned fully rehydrated Subscriptions
- //
+ private final Bus eventBus;
+
+
@Inject
public EntitlementSqlDao(final IDBI dbi, final Clock clock,
final AddonUtils addonUtils, final NotificationQueueService notificationQueueService,
final CustomFieldDao customFieldDao,
- final CatalogService catalogService) {
+ final Bus eventBus) {
+
this.clock = clock;
this.subscriptionsDao = dbi.onDemand(SubscriptionSqlDao.class);
this.eventsDao = dbi.onDemand(EventSqlDao.class);
@@ -108,7 +111,7 @@ public class EntitlementSqlDao implements EntitlementDao {
this.notificationQueueService = notificationQueueService;
this.addonUtils = addonUtils;
this.customFieldDao = customFieldDao;
- this.catalogService = catalogService;
+ this.eventBus = eventBus;
}
@Override
@@ -191,17 +194,18 @@ public class EntitlementSqlDao implements EntitlementDao {
}
@Override
- public void updateSubscription(final SubscriptionData subscription, final CallContext context) {
+ public void updateChargedThroughDate(final SubscriptionData subscription, final CallContext context) {
final Date ctd = (subscription.getChargedThroughDate() != null) ? subscription.getChargedThroughDate().toDate() : null;
- final Date ptd = (subscription.getPaidThroughDate() != null) ? subscription.getPaidThroughDate().toDate() : null;
subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
@Override
public Void inTransaction(SubscriptionSqlDao transactionalDao,
TransactionStatus status) throws Exception {
- transactionalDao.updateSubscription(subscription.getId().toString(), subscription.getActiveVersion(), ctd, ptd, context);
+ transactionalDao.updateChargedThroughDate(subscription.getId().toString(), ctd, context);
+ BundleSqlDao tmpDao = transactionalDao.become(BundleSqlDao.class);
+ tmpDao.updateBundleLastSysTime(subscription.getBundleId().toString(), clock.getUTCNow().toDate());
AuditSqlDao auditSqlDao = transactionalDao.become(AuditSqlDao.class);
String subscriptionId = subscription.getId().toString();
auditSqlDao.insertAuditFromTransaction(SUBSCRIPTIONS_TABLE_NAME, subscriptionId, ChangeType.UPDATE, context);
@@ -240,6 +244,29 @@ public class EntitlementSqlDao implements EntitlementDao {
}
@Override
+ public Map<UUID, List<EntitlementEvent>> getEventsForBundle(final UUID bundleId) {
+
+ Map<UUID, List<EntitlementEvent>> result = subscriptionsDao.inTransaction(new Transaction<Map<UUID, List<EntitlementEvent>>, SubscriptionSqlDao>() {
+ @Override
+ public Map<UUID, List<EntitlementEvent>> inTransaction(SubscriptionSqlDao transactional,
+ TransactionStatus status) throws Exception {
+ List<Subscription> subscriptions = transactional.getSubscriptionsFromBundleId(bundleId.toString());
+ if (subscriptions.size() == 0) {
+ return Collections.emptyMap();
+ }
+ EventSqlDao eventsDaoFromSameTransaction = transactional.become(EventSqlDao.class);
+ Map<UUID, List<EntitlementEvent>> result = new HashMap<UUID, List<EntitlementEvent>>();
+ for (Subscription cur : subscriptions) {
+ List<EntitlementEvent> events = eventsDaoFromSameTransaction.getEventsForSubscription(cur.getId().toString());
+ result.put(cur.getId(), events);
+ }
+ return result;
+ }
+ });
+ return result;
+ }
+
+ @Override
public List<EntitlementEvent> getPendingEventsForSubscription(UUID subscriptionId) {
Date now = clock.getUTCNow().toDate();
return eventsDao.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
@@ -406,7 +433,7 @@ public class EntitlementSqlDao implements EntitlementDao {
}
private void cancelFutureEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao,
- final EventType type, @Nullable final ApiEventType apiType,
+ final EventType type, final ApiEventType apiType,
final CallContext context) {
UUID futureEventId = null;
@@ -577,6 +604,39 @@ public class EntitlementSqlDao implements EntitlementDao {
});
}
+ public void repair(final UUID accountId, final UUID bundleId, final List<SubscriptionDataRepair> inRepair, final CallContext context) {
+ subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
+
+ @Override
+ public Void inTransaction(SubscriptionSqlDao transactional,
+ TransactionStatus status) throws Exception {
+
+ EventSqlDao transEventDao = transactional.become(EventSqlDao.class);
+ for (final SubscriptionDataRepair cur : inRepair) {
+ transactional.updateForRepair(cur.getId().toString(), cur.getActiveVersion(), cur.getStartDate().toDate(), cur.getBundleStartDate().toDate(), context);
+ for (EntitlementEvent event : cur.getInitialEvents()) {
+ transEventDao.updateVersion(event.getId().toString(), event.getActiveVersion(), context);
+ }
+ for (EntitlementEvent event : cur.getNewEvents()) {
+ transEventDao.insertEvent(event, context);
+ if (event.getEffectiveDate().isAfter(clock.getUTCNow())) {
+ recordFutureNotificationFromTransaction(transactional,
+ event.getEffectiveDate(),
+ new EntitlementNotificationKey(event.getId()));
+ }
+ }
+ }
+ try {
+ RepairEntitlementEvent busEvent = new DefaultRepairEntitlementEvent(context.getUserToken(), accountId, bundleId, clock.getUTCNow());
+ eventBus.postFromTransaction(busEvent, transactional);
+ } catch (EventBusException e) {
+ log.warn("Failed to post repair entitlement event for bundle " + bundleId);
+ }
+ return null;
+ }
+ });
+ }
+
private Subscription getBaseSubscription(final SubscriptionFactory factory, final UUID bundleId, boolean rebuildSubscription) {
List<Subscription> subscriptions = subscriptionsDao.getSubscriptionsFromBundleId(bundleId.toString());
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
index 89baed8..a927be3 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
@@ -66,6 +66,11 @@ public interface EventSqlDao extends Transactional<EventSqlDao>, CloseMe, Transm
public void reactiveEvent(@Bind("event_id")String eventId,
@CallContextBinder final CallContext context);
+ @SqlUpdate
+ public void updateVersion(@Bind("event_id")String eventId,
+ @Bind("current_version") Long currentVersion,
+ @CallContextBinder final CallContext context);
+
@SqlQuery
@Mapper(EventSqlMapper.class)
public List<EntitlementEvent> getFutureActiveEventForSubscription(@Bind("subscription_id") String subscriptionId, @Bind("now") Date now);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java
new file mode 100644
index 0000000..178ea2d
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java
@@ -0,0 +1,249 @@
+/*
+ * 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.
+ */
+package com.ning.billing.entitlement.engine.dao;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.UUID;
+
+import com.ning.billing.entitlement.api.SubscriptionFactory;
+import com.ning.billing.entitlement.api.migration.AccountMigrationData;
+import com.ning.billing.entitlement.api.repair.RepairEntitlementLifecycleDao;
+import com.ning.billing.entitlement.api.repair.SubscriptionDataRepair;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.events.EntitlementEvent;
+import com.ning.billing.entitlement.exceptions.EntitlementError;
+import com.ning.billing.util.callcontext.CallContext;
+
+public class RepairEntitlementDao implements EntitlementDao, RepairEntitlementLifecycleDao {
+
+ private final ThreadLocal<Map<UUID, SubscriptionRepairEvent>> preThreadsInRepairSubscriptions = new ThreadLocal<Map<UUID, SubscriptionRepairEvent>>();
+
+ private final static class SubscriptionRepairEvent {
+
+ private final Set<EntitlementEvent> events;
+
+ public SubscriptionRepairEvent(List<EntitlementEvent> initialEvents) {
+ events = new TreeSet<EntitlementEvent>(new Comparator<EntitlementEvent>() {
+ @Override
+ public int compare(EntitlementEvent o1, EntitlementEvent o2) {
+ return o1.compareTo(o2);
+ }
+ });
+ if (initialEvents != null) {
+ events.addAll(initialEvents);
+ }
+ }
+
+ public Set<EntitlementEvent> getEvents() {
+ return events;
+ }
+
+ public void addEvents(List<EntitlementEvent> newEvents) {
+ events.addAll(newEvents);
+ }
+ }
+
+ private Map<UUID, SubscriptionRepairEvent> getRepairMap() {
+ if (preThreadsInRepairSubscriptions.get() == null) {
+ preThreadsInRepairSubscriptions.set(new HashMap<UUID, SubscriptionRepairEvent>());
+ }
+ return preThreadsInRepairSubscriptions.get();
+ }
+
+ private SubscriptionRepairEvent getRepairSubscriptionEvents(UUID subscriptionId) {
+ Map<UUID, SubscriptionRepairEvent> map = getRepairMap();
+ return map.get(subscriptionId);
+ }
+
+ @Override
+ public List<EntitlementEvent> getEventsForSubscription(UUID subscriptionId) {
+ SubscriptionRepairEvent target = getRepairSubscriptionEvents(subscriptionId);
+ return new LinkedList<EntitlementEvent>(target.getEvents());
+ }
+
+ @Override
+ public void createSubscription(SubscriptionData subscription,
+ List<EntitlementEvent> createEvents, CallContext context) {
+ addEvents(subscription.getId(), createEvents);
+ }
+
+ @Override
+ public void recreateSubscription(UUID subscriptionId,
+ List<EntitlementEvent> recreateEvents, CallContext context) {
+ addEvents(subscriptionId, recreateEvents);
+ }
+
+ @Override
+ public void cancelSubscription(UUID subscriptionId,
+ EntitlementEvent cancelEvent, CallContext context, int cancelSeq) {
+ long activeVersion = cancelEvent.getActiveVersion();
+ addEvents(subscriptionId, Collections.singletonList(cancelEvent));
+ SubscriptionRepairEvent target = getRepairSubscriptionEvents(subscriptionId);
+ boolean foundCancelEvent = false;
+ for (EntitlementEvent cur : target.getEvents()) {
+ if (cur.getId().equals(cancelEvent.getId())) {
+ foundCancelEvent = true;
+ } else if (foundCancelEvent) {
+ cur.setActiveVersion(activeVersion - 1);
+ }
+ }
+ }
+
+
+ @Override
+ public void changePlan(UUID subscriptionId,
+ List<EntitlementEvent> changeEvents, CallContext context) {
+ addEvents(subscriptionId, changeEvents);
+ }
+
+ @Override
+ public void initializeRepair(UUID subscriptionId, List<EntitlementEvent> initialEvents) {
+ Map<UUID, SubscriptionRepairEvent> map = getRepairMap();
+ if (map.get(subscriptionId) == null) {
+ SubscriptionRepairEvent value = new SubscriptionRepairEvent(initialEvents);
+ map.put(subscriptionId, value);
+ } else {
+ throw new EntitlementError(String.format("Unexpected SubscriptionRepairEvent %s for thread %s", subscriptionId, Thread.currentThread().getName()));
+ }
+ }
+
+ @Override
+ public void cleanup() {
+ Map<UUID, SubscriptionRepairEvent> map = getRepairMap();
+ map.clear();
+ }
+
+
+ private void addEvents(UUID subscriptionId, List<EntitlementEvent> events) {
+ SubscriptionRepairEvent target = getRepairSubscriptionEvents(subscriptionId);
+ target.addEvents(events);
+ }
+
+
+ @Override
+ public void uncancelSubscription(UUID subscriptionId,
+ List<EntitlementEvent> uncancelEvents, CallContext context) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public List<SubscriptionBundle> getSubscriptionBundleForAccount(UUID accountId) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public SubscriptionBundle getSubscriptionBundleFromKey(String bundleKey) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public SubscriptionBundle getSubscriptionBundleFromId(UUID bundleId) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public SubscriptionBundle createSubscriptionBundle(
+ SubscriptionBundleData bundle, CallContext context) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public Subscription getSubscriptionFromId(SubscriptionFactory factory,
+ UUID subscriptionId) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public UUID getAccountIdFromSubscriptionId(UUID subscriptionId) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public Subscription getBaseSubscription(SubscriptionFactory factory,
+ UUID bundleId) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public List<Subscription> getSubscriptions(SubscriptionFactory factory,
+ UUID bundleId) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public List<Subscription> getSubscriptionsForKey(
+ SubscriptionFactory factory, String bundleKey) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public void updateChargedThroughDate(SubscriptionData subscription,
+ CallContext context) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public void createNextPhaseEvent(UUID subscriptionId,
+ EntitlementEvent nextPhase, CallContext context) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public EntitlementEvent getEventById(UUID eventId) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public Map<UUID, List<EntitlementEvent>> getEventsForBundle(UUID bundleId) {
+ throw new EntitlementError("Not implemented");
+ }
+
+
+ @Override
+ public List<EntitlementEvent> getPendingEventsForSubscription(
+ UUID subscriptionId) {
+ throw new EntitlementError("Not implemented");
+ }
+
+
+ @Override
+ public void migrate(UUID accountId, AccountMigrationData data,
+ CallContext context) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public void saveCustomFields(SubscriptionData subscription,
+ CallContext context) {
+ throw new EntitlementError("Not implemented");
+ }
+
+ @Override
+ public void repair(UUID accountId, UUID bundleId, List<SubscriptionDataRepair> inRepair,
+ CallContext context) {
+ throw new EntitlementError("Not implemented");
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
index 04f2965..92c37db 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
@@ -19,7 +19,7 @@ package com.ning.billing.entitlement.engine.dao;
import com.ning.billing.catalog.api.ProductCategory;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.SubscriptionData;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.callcontext.CallContextBinder;
import com.ning.billing.util.dao.BinderBase;
@@ -59,10 +59,18 @@ public interface SubscriptionSqlDao extends Transactional<SubscriptionSqlDao>, C
public List<Subscription> getSubscriptionsFromBundleId(@Bind("bundle_id") String bundleId);
@SqlUpdate
- public void updateSubscription(@Bind("id") String id, @Bind("active_version") long activeVersion,
- @Bind("ctd_dt") Date ctd, @Bind("ptd_dt") Date ptd,
+ public void updateChargedThroughDate(@Bind("id") String id, @Bind("ctd_dt") Date ctd,
@CallContextBinder final CallContext context);
-
+
+ @SqlUpdate void updateActiveVersion(@Bind("id") String id, @Bind("active_version") long activeVersion,
+ @CallContextBinder final CallContext context);
+
+ @SqlUpdate
+ public void updateForRepair(@Bind("id") String id, @Bind("active_version") long activeVersion,
+ @Bind("start_dt") Date startDate,
+ @Bind("bundle_start_dt") Date bundleStartDate,
+ @CallContextBinder final CallContext context);
+
public static class SubscriptionBinder extends BinderBase implements Binder<Bind, SubscriptionData> {
@Override
public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, SubscriptionData sub) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java
index 27d5b61..d1eae92 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java
@@ -16,7 +16,7 @@
package com.ning.billing.entitlement.events.user;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
public enum ApiEventType {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java b/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java
index 6f32fe3..a3810c6 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java
@@ -19,24 +19,36 @@ package com.ning.billing.entitlement.glue;
import org.skife.config.ConfigurationObjectFactory;
import com.google.inject.AbstractModule;
+import com.google.inject.name.Names;
import com.ning.billing.config.EntitlementConfig;
import com.ning.billing.entitlement.alignment.MigrationPlanAligner;
import com.ning.billing.entitlement.alignment.PlanAligner;
import com.ning.billing.entitlement.api.EntitlementService;
+import com.ning.billing.entitlement.api.SubscriptionApiService;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
import com.ning.billing.entitlement.api.billing.ChargeThruApi;
import com.ning.billing.entitlement.api.billing.DefaultChargeThruApi;
import com.ning.billing.entitlement.api.migration.DefaultEntitlementMigrationApi;
import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
+import com.ning.billing.entitlement.api.repair.DefaultEntitlementRepairApi;
+import com.ning.billing.entitlement.api.repair.EntitlementRepairApi;
+import com.ning.billing.entitlement.api.repair.RepairEntitlementLifecycleDao;
+import com.ning.billing.entitlement.api.repair.RepairSubscriptionApiService;
+import com.ning.billing.entitlement.api.repair.RepairSubscriptionFactory;
import com.ning.billing.entitlement.api.user.DefaultEntitlementUserApi;
import com.ning.billing.entitlement.api.user.EntitlementUserApi;
-import com.ning.billing.entitlement.api.user.SubscriptionApiService;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionApiService;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory;
import com.ning.billing.entitlement.engine.addon.AddonUtils;
import com.ning.billing.entitlement.engine.core.Engine;
import com.ning.billing.entitlement.engine.dao.EntitlementDao;
import com.ning.billing.entitlement.engine.dao.EntitlementSqlDao;
+import com.ning.billing.entitlement.engine.dao.RepairEntitlementDao;
public class EntitlementModule extends AbstractModule {
+
+ public static final String REPAIR_NAMED = "repair";
+
protected void installConfig() {
final EntitlementConfig config = new ConfigurationObjectFactory(System.getProperties()).build(EntitlementConfig.class);
bind(EntitlementConfig.class).toInstance(config);
@@ -44,16 +56,26 @@ public class EntitlementModule extends AbstractModule {
protected void installEntitlementDao() {
bind(EntitlementDao.class).to(EntitlementSqlDao.class).asEagerSingleton();
+ bind(EntitlementDao.class).annotatedWith(Names.named(REPAIR_NAMED)).to(RepairEntitlementDao.class);
+ bind(RepairEntitlementLifecycleDao.class).annotatedWith(Names.named(REPAIR_NAMED)).to(RepairEntitlementDao.class);
+ bind(RepairEntitlementDao.class).asEagerSingleton();
}
protected void installEntitlementCore() {
- bind(SubscriptionFactory.class).asEagerSingleton();
- bind(SubscriptionApiService.class).asEagerSingleton();
+
+ bind(SubscriptionFactory.class).annotatedWith(Names.named(REPAIR_NAMED)).to(RepairSubscriptionFactory.class).asEagerSingleton();
+ bind(SubscriptionFactory.class).to(DefaultSubscriptionFactory.class).asEagerSingleton();
+
+ bind(SubscriptionApiService.class).annotatedWith(Names.named(REPAIR_NAMED)).to(RepairSubscriptionApiService.class).asEagerSingleton();
+ bind(SubscriptionApiService.class).to(DefaultSubscriptionApiService.class).asEagerSingleton();
+
bind(EntitlementService.class).to(Engine.class).asEagerSingleton();
bind(Engine.class).asEagerSingleton();
bind(PlanAligner.class).asEagerSingleton();
bind(AddonUtils.class).asEagerSingleton();
bind(MigrationPlanAligner.class).asEagerSingleton();
+
+ bind(EntitlementRepairApi.class).to(DefaultEntitlementRepairApi.class).asEagerSingleton();
bind(EntitlementUserApi.class).to(DefaultEntitlementUserApi.class).asEagerSingleton();
bind(EntitlementMigrationApi.class).to(DefaultEntitlementMigrationApi.class).asEagerSingleton();
bind(ChargeThruApi.class).to(DefaultChargeThruApi.class).asEagerSingleton();
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql b/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
index d2bb5b7..2064310 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
@@ -44,7 +44,8 @@ DROP TABLE IF EXISTS bundles;
CREATE TABLE bundles (
id char(36) NOT NULL,
start_dt datetime, /*NOT NULL*/
- name varchar(64) NOT NULL,
+ external_key varchar(64) NOT NULL,
account_id char(36) NOT NULL,
+ last_sys_update_dt datetime,
PRIMARY KEY(id)
) ENGINE=innodb;
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg
index 4f0e4db..184604d 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg
@@ -4,37 +4,49 @@ insertBundle() ::= <<
insert into bundles (
id
, start_dt
- , name
+ , external_key
, account_id
+ , last_sys_update_dt
) values (
:id
, :start_dt
- , :name
+ , :external_key
, :account_id
+ , :last_sys_update_dt
);
>>
+updateBundleLastSysTime(id, last_sys_update_dt) ::= <<
+ update bundles
+ set
+ last_sys_update_dt = :last_sys_update_dt
+ where id = :id
+ ;
+>>
+
getBundleFromId(id) ::= <<
select
id
, start_dt
- , name
+ , external_key
, account_id
+ , last_sys_update_dt
from bundles
where
id = :id
;
>>
-getBundleFromKey(name) ::= <<
+getBundleFromKey(external_key) ::= <<
select
id
, start_dt
- , name
+ , external_key
, account_id
+ , last_sys_update_dt
from bundles
where
- name = :name
+ external_key = :external_key
;
>>
@@ -43,8 +55,9 @@ getBundleFromAccount(account_id) ::= <<
select
id
, start_dt
- , name
+ , external_key
, account_id
+ , last_sys_update_dt
from bundles
where
account_id = :account_id
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg
index cd5afbf..817afcc 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg
@@ -50,6 +50,15 @@ insertEvent() ::= <<
);
>>
+updateVersion() ::= <<
+ update entitlement_events
+ set
+ current_version = :current_version
+ where
+ event_id = :event_id
+ ;
+>>
+
unactiveEvent() ::= <<
update entitlement_events
set
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg
index 780c06a..804f6ae 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg
@@ -60,12 +60,32 @@ getSubscriptionsFromBundleId(bundle_id) ::= <<
;
>>
-updateSubscription(id, active_version, ctd_dt, ptd_dt) ::= <<
+updateChargedThroughDate() ::= <<
+ update subscriptions
+ set
+ ctd_dt = :ctd_dt
+ , updated_by = :userName
+ , updated_date = :updatedDate
+ where id = :id
+ ;
+>>
+
+updateActiveVersion() ::= <<
+ update subscriptions
+ set
+ active_version = :active_version
+ , updated_by = :userName
+ , updated_date = :updatedDate
+ where id = :id
+ ;
+>>
+
+updateForRepair() ::= <<
update subscriptions
set
active_version = :active_version
- , ctd_dt = :ctd_dt
- , ptd_dt = :ptd_dt
+ , start_dt = :start_dt
+ , bundle_start_dt = :bundle_start_dt
, updated_by = :userName
, updated_date = :updatedDate
where id = :id
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java
index 694613e..45455d2 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java
@@ -18,6 +18,7 @@ package com.ning.billing.entitlement.api;
import com.google.common.base.Joiner;
import com.google.common.eventbus.Subscribe;
+import com.ning.billing.entitlement.api.repair.RepairEntitlementEvent;
import com.ning.billing.entitlement.api.user.SubscriptionEventTransition;
import com.ning.billing.util.bus.Bus;
import org.slf4j.Logger;
@@ -32,9 +33,13 @@ public class ApiTestListener {
private static final Logger log = LoggerFactory.getLogger(ApiTestListener.class);
- private final List<NextEvent> nextExpectedEvent;
+ private final List<NextEvent> nextApiExpectedEvent;
- private volatile boolean completed;
+
+ private boolean isApiCompleted;
+
+ private boolean expectRepairCompletion;
+ private boolean isRepairCompleted;
public enum NextEvent {
MIGRATE_ENTITLEMENT,
@@ -47,8 +52,21 @@ public class ApiTestListener {
}
public ApiTestListener(Bus eventBus) {
- this.nextExpectedEvent = new Stack<NextEvent>();
- this.completed = false;
+ this.nextApiExpectedEvent = new Stack<NextEvent>();
+ reset();
+ }
+
+ @Subscribe
+ public void handleRepairEvent(RepairEntitlementEvent event) {
+ log.debug("-> Got event RepairEntitlementEvent for bundle " + event.getBundleId());
+ if (!expectRepairCompletion) {
+ log.error("Did not expect repair any event!!!");
+ } else {
+ synchronized(this) {
+ isRepairCompleted = true;
+ notify();
+ }
+ }
}
@Subscribe
@@ -83,39 +101,63 @@ public class ApiTestListener {
}
- public void pushExpectedEvent(NextEvent next) {
+ public void pushNextApiExpectedEvent(NextEvent next) {
synchronized (this) {
- nextExpectedEvent.add(next);
- completed = false;
+ nextApiExpectedEvent.add(next);
+ isApiCompleted = false;
}
}
+
+ public void expectRepairCompletion() {
+ expectRepairCompletion = true;
+ isRepairCompleted = false;
+ }
- public boolean isCompleted(long timeout) {
+ public boolean isRepairCompleted(long timeout) {
+ synchronized (this) {
+ if (isRepairCompleted) {
+ return isRepairCompleted;
+ }
+ try {
+ wait(timeout);
+ } catch (Exception ignore) {
+ }
+ }
+ if (!isRepairCompleted) {
+ log.debug("ApiTestListener did not complete in " + timeout + " ms");
+ }
+ return isRepairCompleted;
+ }
+
+ public boolean isApiCompleted(long timeout) {
synchronized (this) {
- if (completed) {
- return completed;
+ if (isApiCompleted) {
+ return isApiCompleted;
}
try {
wait(timeout);
} catch (Exception ignore) {
}
}
- if (!completed) {
+ if (!isApiCompleted) {
log.debug("ApiTestListener did not complete in " + timeout + " ms");
}
- return completed;
+ return isApiCompleted;
}
public void reset() {
- nextExpectedEvent.clear();
+ nextApiExpectedEvent.clear();
+ this.isApiCompleted = false;
+ this.expectRepairCompletion = false;
+ this.isRepairCompleted = false;
}
private void notifyIfStackEmpty() {
log.debug("notifyIfStackEmpty ENTER");
synchronized (this) {
- if (nextExpectedEvent.isEmpty()) {
+ if (nextApiExpectedEvent.isEmpty()) {
log.debug("notifyIfStackEmpty EMPTY");
- completed = true;
+ isApiCompleted = true;
notify();
}
}
@@ -125,7 +167,7 @@ public class ApiTestListener {
private void assertEqualsNicely(NextEvent expected) {
boolean foundIt = false;
- Iterator<NextEvent> it = nextExpectedEvent.iterator();
+ Iterator<NextEvent> it = nextApiExpectedEvent.iterator();
while (it.hasNext()) {
NextEvent ev = it.next();
if (ev == expected) {
@@ -137,7 +179,7 @@ public class ApiTestListener {
if (!foundIt) {
Joiner joiner = Joiner.on(" ");
- Assert.fail("Expected event " + expected + " got " + joiner.join(nextExpectedEvent));
+ Assert.fail("Expected event " + expected + " got " + joiner.join(nextApiExpectedEvent));
}
}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java
index 3e1b421..7380ec9 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java
@@ -46,7 +46,6 @@ import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import org.testng.annotations.Test;
-@Test(groups = {"slow"})
public abstract class TestMigration extends TestApiBase {
public void testSingleBasePlan() {
@@ -56,9 +55,9 @@ public abstract class TestMigration extends TestApiBase {
EntitlementAccountMigration toBeMigrated = createAccountWithRegularBasePlan(startDate);
DateTime afterMigration = clock.getUTCNow();
- testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
+ testListener.pushNextApiExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
migrationApi.migrate(toBeMigrated, context);
- assertTrue(testListener.isCompleted(5000));
+ assertTrue(testListener.isApiCompleted(5000));
List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
assertEquals(bundles.size(), 1);
@@ -87,10 +86,10 @@ public abstract class TestMigration extends TestApiBase {
EntitlementAccountMigration toBeMigrated = createAccountWithRegularBasePlanAndAddons(initalBPStart, initalAddonStart);
DateTime afterMigration = clock.getUTCNow();
- testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
- testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
+ testListener.pushNextApiExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
+ testListener.pushNextApiExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
migrationApi.migrate(toBeMigrated, context);
- assertTrue(testListener.isCompleted(5000));
+ assertTrue(testListener.isApiCompleted(5000));
List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
assertEquals(bundles.size(), 1);
@@ -111,7 +110,9 @@ public abstract class TestMigration extends TestApiBase {
Subscription aoSubscription = (subscriptions.get(0).getCurrentPlan().getProduct().getCategory() == ProductCategory.ADD_ON) ?
subscriptions.get(0) : subscriptions.get(1);
- assertEquals(aoSubscription.getStartDate(), initalAddonStart);
+ // initalAddonStart.plusMonths(1).minusMonths(1) may be different from initalAddonStart, depending on exact date
+ // e.g : March 31 + 1 month => April 30 and April 30 - 1 month = March 30 which is != March 31 !!!!
+ assertEquals(aoSubscription.getStartDate(), initalAddonStart.plusMonths(1).minusMonths(1));
assertEquals(aoSubscription.getEndDate(), null);
assertEquals(aoSubscription.getCurrentPriceList().getName(), PriceListSet.DEFAULT_PRICELIST_NAME);
assertEquals(aoSubscription.getCurrentPhase().getPhaseType(), PhaseType.DISCOUNT);
@@ -133,9 +134,9 @@ public abstract class TestMigration extends TestApiBase {
EntitlementAccountMigration toBeMigrated = createAccountWithRegularBasePlanFutreCancelled(startDate);
DateTime afterMigration = clock.getUTCNow();
- testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
+ testListener.pushNextApiExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
migrationApi.migrate(toBeMigrated, context);
- assertTrue(testListener.isCompleted(5000));
+ assertTrue(testListener.isApiCompleted(5000));
List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
assertEquals(bundles.size(), 1);
@@ -152,11 +153,11 @@ public abstract class TestMigration extends TestApiBase {
assertEquals(subscription.getCurrentPlan().getName(), "assault-rifle-annual");
assertEquals(subscription.getChargedThroughDate(), startDate.plusYears(1));
- testListener.pushExpectedEvent(NextEvent.MIGRATE_BILLING);
- testListener.pushExpectedEvent(NextEvent.CANCEL);
+ testListener.pushNextApiExpectedEvent(NextEvent.MIGRATE_BILLING);
+ testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
Duration oneYear = getDurationYear(1);
clock.setDeltaFromReality(oneYear, 0);
- assertTrue(testListener.isCompleted(5000));
+ assertTrue(testListener.isApiCompleted(5000));
assertDateWithin(subscription.getStartDate(), beforeMigration, afterMigration);
assertNotNull(subscription.getEndDate());
@@ -177,9 +178,9 @@ public abstract class TestMigration extends TestApiBase {
final DateTime trialDate = clock.getUTCNow().minusDays(10);
EntitlementAccountMigration toBeMigrated = createAccountFuturePendingPhase(trialDate);
- testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
+ testListener.pushNextApiExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
migrationApi.migrate(toBeMigrated, context);
- assertTrue(testListener.isCompleted(5000));
+ assertTrue(testListener.isApiCompleted(5000));
List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
assertEquals(bundles.size(), 1);
@@ -197,11 +198,11 @@ public abstract class TestMigration extends TestApiBase {
assertEquals(subscription.getCurrentPlan().getName(), "assault-rifle-monthly");
assertEquals(subscription.getChargedThroughDate(), trialDate.plusDays(30));
- testListener.pushExpectedEvent(NextEvent.MIGRATE_BILLING);
- testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.MIGRATE_BILLING);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
Duration thirtyDays = getDurationDay(30);
clock.setDeltaFromReality(thirtyDays, 0);
- assertTrue(testListener.isCompleted(5000));
+ assertTrue(testListener.isApiCompleted(5000));
assertEquals(subscription.getStartDate(), trialDate);
assertEquals(subscription.getEndDate(), null);
@@ -223,9 +224,9 @@ public abstract class TestMigration extends TestApiBase {
EntitlementAccountMigration toBeMigrated = createAccountFuturePendingChange();
DateTime afterMigration = clock.getUTCNow();
- testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
+ testListener.pushNextApiExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
migrationApi.migrate(toBeMigrated, context);
- assertTrue(testListener.isCompleted(5000));
+ assertTrue(testListener.isApiCompleted(5000));
List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(toBeMigrated.getAccountKey());
assertEquals(bundles.size(), 1);
@@ -241,10 +242,10 @@ public abstract class TestMigration extends TestApiBase {
assertEquals(subscription.getState(), SubscriptionState.ACTIVE);
assertEquals(subscription.getCurrentPlan().getName(), "assault-rifle-monthly");
- testListener.pushExpectedEvent(NextEvent.CHANGE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
Duration oneMonth = getDurationMonth(1);
clock.setDeltaFromReality(oneMonth, 0);
- assertTrue(testListener.isCompleted(5000));
+ assertTrue(testListener.isApiCompleted(5000));
assertDateWithin(subscription.getStartDate(), beforeMigration, afterMigration);
assertEquals(subscription.getEndDate(), null);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigrationSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigrationSql.java
index a17cbcd..34b2266 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigrationSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigrationSql.java
@@ -23,34 +23,32 @@ import com.google.inject.Injector;
import com.google.inject.Stage;
import com.ning.billing.entitlement.glue.MockEngineModuleSql;
-@Test(groups = "slow")
public class TestMigrationSql extends TestMigration {
-//TODO MDW These tests pass but not when you run them all together - some clean up needed
@Override
protected Injector getInjector() {
return Guice.createInjector(Stage.DEVELOPMENT, new MockEngineModuleSql());
}
@Override
- @Test(enabled=false, groups="slow")
+ @Test(enabled=true, groups="slow")
public void testSingleBasePlan() {
super.testSingleBasePlan();
}
@Override
- @Test(enabled=false, groups="slow")
+ @Test(enabled=true, groups="slow")
public void testPlanWithAddOn() {
super.testPlanWithAddOn();
}
@Override
- @Test(enabled=false, groups="slow")
+ @Test(enabled=true, groups="slow")
public void testSingleBasePlanFutureCancelled() {
super.testSingleBasePlanFutureCancelled();
}
@Override
- @Test(enabled=false, groups="slow")
+ @Test(enabled=true, groups="slow")
public void testSingleBasePlanWithPendingPhase() {
super.testSingleBasePlanWithPendingPhase();
}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestApiBaseRepair.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestApiBaseRepair.java
new file mode 100644
index 0000000..b908bb6
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestApiBaseRepair.java
@@ -0,0 +1,215 @@
+/*
+ * 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.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+
+
+import com.ning.billing.ErrorCode;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.TestApiBase;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.DeletedEvent;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.ExistingEvent;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.NewEvent;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
+
+
+public abstract class TestApiBaseRepair extends TestApiBase {
+
+ protected final static Logger log = LoggerFactory.getLogger(TestApiBaseRepair.class);
+
+ public interface TestWithExceptionCallback {
+ public void doTest() throws EntitlementRepairException, EntitlementUserApiException;
+ }
+
+ public static class TestWithException {
+ public void withException(TestWithExceptionCallback callback, ErrorCode code) throws Exception {
+ try {
+ callback.doTest();
+ Assert.fail("Failed to catch exception " + code);
+ } catch (EntitlementRepairException e) {
+ assertEquals(e.getCode(), code.getCode());
+ }
+ }
+ }
+
+
+ protected SubscriptionRepair createSubscriptionReapir(final UUID id, final List<DeletedEvent> deletedEvents, final List<NewEvent> newEvents) {
+ return new SubscriptionRepair() {
+ @Override
+ public UUID getId() {
+ return id;
+ }
+ @Override
+ public List<NewEvent> getNewEvents() {
+ return newEvents;
+ }
+ @Override
+ public List<ExistingEvent> getExistingEvents() {
+ return null;
+ }
+ @Override
+ public List<DeletedEvent> getDeletedEvents() {
+ return deletedEvents;
+ }
+ };
+ }
+
+ protected BundleRepair createBundleRepair(final UUID bundleId, final String viewId, final List<SubscriptionRepair> subscriptionRepair) {
+ return new BundleRepair() {
+ @Override
+ public String getViewId() {
+ return viewId;
+ }
+ @Override
+ public List<SubscriptionRepair> getSubscriptions() {
+ return subscriptionRepair;
+ }
+ @Override
+ public UUID getBundleId() {
+ return bundleId;
+ }
+ };
+ }
+
+ protected ExistingEvent createExistingEventForAssertion(final SubscriptionTransitionType type,
+ final String productName, final PhaseType phaseType, final ProductCategory category, final String priceListName, final BillingPeriod billingPeriod,
+ final DateTime effectiveDateTime) {
+
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, category, billingPeriod, priceListName, phaseType);
+ ExistingEvent ev = new ExistingEvent() {
+ @Override
+ public SubscriptionTransitionType getSubscriptionTransitionType() {
+ return type;
+ }
+ @Override
+ public DateTime getRequestedDate() {
+ return null;
+ }
+ @Override
+ public PlanPhaseSpecifier getPlanPhaseSpecifier() {
+ return spec;
+ }
+ @Override
+ public UUID getEventId() {
+ return null;
+ }
+ @Override
+ public DateTime getEffectiveDate() {
+ return effectiveDateTime;
+ }
+ };
+ return ev;
+ }
+
+ protected SubscriptionRepair getSubscriptionRepair(final UUID id, final BundleRepair bundleRepair) {
+ for (SubscriptionRepair cur : bundleRepair.getSubscriptions()) {
+ if (cur.getId().equals(id)) {
+ return cur;
+ }
+ }
+ Assert.fail("Failed to find SubscriptionReapir " + id);
+ return null;
+ }
+ protected void validateExistingEventForAssertion(final ExistingEvent expected, final ExistingEvent input) {
+
+ log.info(String.format("Got %s -> Expected %s", input.getPlanPhaseSpecifier().getProductName(), expected.getPlanPhaseSpecifier().getProductName()));
+ assertEquals(input.getPlanPhaseSpecifier().getProductName(), expected.getPlanPhaseSpecifier().getProductName());
+ log.info(String.format("Got %s -> Expected %s", input.getPlanPhaseSpecifier().getPhaseType(), expected.getPlanPhaseSpecifier().getPhaseType()));
+ assertEquals(input.getPlanPhaseSpecifier().getPhaseType(), expected.getPlanPhaseSpecifier().getPhaseType());
+ log.info(String.format("Got %s -> Expected %s", input.getPlanPhaseSpecifier().getProductCategory(), expected.getPlanPhaseSpecifier().getProductCategory()));
+ assertEquals(input.getPlanPhaseSpecifier().getProductCategory(), expected.getPlanPhaseSpecifier().getProductCategory());
+ log.info(String.format("Got %s -> Expected %s", input.getPlanPhaseSpecifier().getPriceListName(), expected.getPlanPhaseSpecifier().getPriceListName()));
+ assertEquals(input.getPlanPhaseSpecifier().getPriceListName(), expected.getPlanPhaseSpecifier().getPriceListName());
+ log.info(String.format("Got %s -> Expected %s", input.getPlanPhaseSpecifier().getBillingPeriod(), expected.getPlanPhaseSpecifier().getBillingPeriod()));
+ assertEquals(input.getPlanPhaseSpecifier().getBillingPeriod(), expected.getPlanPhaseSpecifier().getBillingPeriod());
+ log.info(String.format("Got %s -> Expected %s", input.getEffectiveDate(), expected.getEffectiveDate()));
+ assertEquals(input.getEffectiveDate(), expected.getEffectiveDate());
+ }
+
+ protected DeletedEvent createDeletedEvent(final UUID eventId) {
+ return new DeletedEvent() {
+ @Override
+ public UUID getEventId() {
+ return eventId;
+ }
+ };
+ }
+
+ protected NewEvent createNewEvent(final SubscriptionTransitionType type, final DateTime requestedDate, final PlanPhaseSpecifier spec) {
+
+ return new NewEvent() {
+ @Override
+ public SubscriptionTransitionType getSubscriptionTransitionType() {
+ return type;
+ }
+ @Override
+ public DateTime getRequestedDate() {
+ return requestedDate;
+ }
+ @Override
+ public PlanPhaseSpecifier getPlanPhaseSpecifier() {
+ return spec;
+ }
+ };
+ }
+
+ protected void sortEventsOnBundle(final BundleRepair bundle) {
+ if (bundle.getSubscriptions() == null) {
+ return;
+ }
+ for (SubscriptionRepair cur : bundle.getSubscriptions()) {
+ if (cur.getExistingEvents() != null) {
+ sortExistingEvent(cur.getExistingEvents());
+ }
+ if (cur.getNewEvents() != null) {
+ sortNewEvent(cur.getNewEvents());
+ }
+ }
+ }
+
+ protected void sortExistingEvent(final List<ExistingEvent> events) {
+ Collections.sort(events, new Comparator<ExistingEvent>() {
+ @Override
+ public int compare(ExistingEvent arg0, ExistingEvent arg1) {
+ return arg0.getEffectiveDate().compareTo(arg1.getEffectiveDate());
+ }
+ });
+ }
+ protected void sortNewEvent(final List<NewEvent> events) {
+ Collections.sort(events, new Comparator<NewEvent>() {
+ @Override
+ public int compare(NewEvent arg0, NewEvent arg1) {
+ return arg0.getRequestedDate().compareTo(arg1.getRequestedDate());
+ }
+ });
+ }
+
+}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairBP.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairBP.java
new file mode 100644
index 0000000..8f6725f
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairBP.java
@@ -0,0 +1,708 @@
+/*
+ * 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.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Stage;
+import com.ning.billing.ErrorCode;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.Duration;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+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.api.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.DeletedEvent;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.ExistingEvent;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.NewEvent;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.SubscriptionEvents;
+import com.ning.billing.entitlement.glue.MockEngineModuleSql;
+
+public class TestRepairBP extends TestApiBaseRepair {
+
+ @Override
+ public Injector getInjector() {
+ return Guice.createInjector(Stage.DEVELOPMENT, new MockEngineModuleSql());
+ }
+
+ @Test(groups={"slow"})
+ public void testFetchBundleRepair() throws Exception {
+
+ String baseProduct = "Shotgun";
+ BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+ String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+ // CREATE BP
+ Subscription baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+ String aoProduct = "Telescopic-Scope";
+ BillingPeriod aoTerm = BillingPeriod.MONTHLY;
+ String aoPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+ SubscriptionData aoSubscription = createSubscription(aoProduct, aoTerm, aoPriceList);
+
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ List<SubscriptionRepair> subscriptionRepair = bundleRepair.getSubscriptions();
+ assertEquals(subscriptionRepair.size(), 2);
+
+ for (SubscriptionRepair cur : subscriptionRepair) {
+ assertNull(cur.getDeletedEvents());
+ assertNull(cur.getNewEvents());
+
+ List<ExistingEvent> events = cur.getExistingEvents();
+ assertEquals(events.size(), 2);
+ sortExistingEvent(events);
+
+ assertEquals(events.get(0).getSubscriptionTransitionType(), SubscriptionTransitionType.CREATE);
+ assertEquals(events.get(1).getSubscriptionTransitionType(), SubscriptionTransitionType.PHASE);
+ final boolean isBP = cur.getId().equals(baseSubscription.getId());
+ if (isBP) {
+ assertEquals(cur.getId(), baseSubscription.getId());
+
+ assertEquals(events.get(0).getPlanPhaseSpecifier().getProductName(), baseProduct);
+ assertEquals(events.get(0).getPlanPhaseSpecifier().getPhaseType(), PhaseType.TRIAL);
+ assertEquals(events.get(0).getPlanPhaseSpecifier().getProductCategory(),ProductCategory.BASE);
+ assertEquals(events.get(0).getPlanPhaseSpecifier().getPriceListName(), basePriceList);
+ assertEquals(events.get(0).getPlanPhaseSpecifier().getBillingPeriod(), BillingPeriod.NO_BILLING_PERIOD);
+
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getProductName(), baseProduct);
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getPhaseType(), PhaseType.EVERGREEN);
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getProductCategory(),ProductCategory.BASE);
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getPriceListName(), basePriceList);
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getBillingPeriod(), baseTerm);
+ } else {
+ assertEquals(cur.getId(), aoSubscription.getId());
+
+ assertEquals(events.get(0).getPlanPhaseSpecifier().getProductName(), aoProduct);
+ assertEquals(events.get(0).getPlanPhaseSpecifier().getPhaseType(), PhaseType.DISCOUNT);
+ assertEquals(events.get(0).getPlanPhaseSpecifier().getProductCategory(),ProductCategory.ADD_ON);
+ assertEquals(events.get(0).getPlanPhaseSpecifier().getPriceListName(), aoPriceList);
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getBillingPeriod(), aoTerm);
+
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getProductName(), aoProduct);
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getPhaseType(), PhaseType.EVERGREEN);
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getProductCategory(),ProductCategory.ADD_ON);
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getPriceListName(), aoPriceList);
+ assertEquals(events.get(1).getPlanPhaseSpecifier().getBillingPeriod(), aoTerm);
+ }
+ }
+ }
+
+ @Test(groups={"slow"})
+ public void testBPRepairWithCancellationOnstart() throws Exception {
+
+ String baseProduct = "Shotgun";
+ DateTime startDate = clock.getUTCNow();
+
+ // CREATE BP
+ Subscription baseSubscription = createSubscription(baseProduct, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, startDate);
+
+ // Stays in trial-- for instance
+ Duration durationShift = getDurationDay(10);
+ clock.setDeltaFromReality(durationShift, 0);
+
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+
+ List<DeletedEvent> des = new LinkedList<SubscriptionRepair.DeletedEvent>();
+ des.add(createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(1).getEventId()));
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CANCEL, baseSubscription.getStartDate(), null);
+
+
+ SubscriptionRepair sRepair = createSubscriptionReapir(baseSubscription.getId(), des, Collections.singletonList(ne));
+
+ // FIRST ISSUE DRY RUN
+ BundleRepair bRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
+
+ boolean dryRun = true;
+ BundleRepair dryRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
+ sortEventsOnBundle(dryRunBundleRepair);
+ List<SubscriptionRepair> subscriptionRepair = dryRunBundleRepair.getSubscriptions();
+ assertEquals(subscriptionRepair.size(), 1);
+ SubscriptionRepair cur = subscriptionRepair.get(0);
+ int index = 0;
+ List<ExistingEvent> events = subscriptionRepair.get(0).getExistingEvents();
+ assertEquals(events.size(), 2);
+ List<ExistingEvent> expected = new LinkedList<SubscriptionRepair.ExistingEvent>();
+ expected.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, baseProduct, PhaseType.TRIAL,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.NO_BILLING_PERIOD, baseSubscription.getStartDate()));
+ expected.add(createExistingEventForAssertion(SubscriptionTransitionType.CANCEL, baseProduct, PhaseType.TRIAL,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.NO_BILLING_PERIOD,baseSubscription.getStartDate()));
+
+ for (ExistingEvent e : expected) {
+ validateExistingEventForAssertion(e, events.get(index++));
+ }
+
+ SubscriptionData dryRunBaseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+
+ assertEquals(dryRunBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
+ assertEquals(dryRunBaseSubscription.getBundleId(), bundle.getId());
+ assertEquals(dryRunBaseSubscription.getStartDate(), baseSubscription.getStartDate());
+
+ Plan currentPlan = dryRunBaseSubscription.getCurrentPlan();
+ assertNotNull(currentPlan);
+ assertEquals(currentPlan.getProduct().getName(), baseProduct);
+ assertEquals(currentPlan.getProduct().getCategory(), ProductCategory.BASE);
+ assertEquals(currentPlan.getBillingPeriod(), BillingPeriod.MONTHLY);
+
+ PlanPhase currentPhase = dryRunBaseSubscription.getCurrentPhase();
+ assertNotNull(currentPhase);
+ assertEquals(currentPhase.getPhaseType(), PhaseType.TRIAL);
+
+ // SECOND RE-ISSUE CALL-- NON DRY RUN
+ dryRun = false;
+ testListener.expectRepairCompletion();
+ BundleRepair realRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
+ assertTrue(testListener.isRepairCompleted(5000));
+
+ subscriptionRepair = realRunBundleRepair.getSubscriptions();
+ assertEquals(subscriptionRepair.size(), 1);
+ cur = subscriptionRepair.get(0);
+ assertEquals(cur.getId(), baseSubscription.getId());
+ index = 0;
+ for (ExistingEvent e : expected) {
+ validateExistingEventForAssertion(e, events.get(index++));
+ }
+ SubscriptionData realRunBaseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+ assertEquals(realRunBaseSubscription.getAllTransitions().size(), 2);
+
+
+ assertEquals(realRunBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+ assertEquals(realRunBaseSubscription.getBundleId(), bundle.getId());
+ assertEquals(realRunBaseSubscription.getStartDate(), startDate);
+
+ assertEquals(realRunBaseSubscription.getState(), SubscriptionState.CANCELLED);
+ }
+
+ @Test(groups={"slow"})
+ public void testBPRepairReplaceCreateBeforeTrial() throws Exception {
+ String baseProduct = "Shotgun";
+ String newBaseProduct = "Assault-Rifle";
+
+ DateTime startDate = clock.getUTCNow();
+ int clockShift = -1;
+ DateTime restartDate = startDate.plusDays(clockShift).minusDays(1);
+ LinkedList<ExistingEvent> expected = new LinkedList<SubscriptionRepair.ExistingEvent>();
+
+ expected.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, newBaseProduct, PhaseType.TRIAL,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.NO_BILLING_PERIOD, restartDate));
+ expected.add(createExistingEventForAssertion(SubscriptionTransitionType.PHASE, newBaseProduct, PhaseType.EVERGREEN,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, restartDate.plusDays(30)));
+
+ testBPRepairCreate(true, startDate, clockShift, baseProduct, newBaseProduct, expected);
+ }
+
+ @Test(groups={"slow"}, enabled=true)
+ public void testBPRepairReplaceCreateInTrial() throws Exception {
+ String baseProduct = "Shotgun";
+ String newBaseProduct = "Assault-Rifle";
+
+ DateTime startDate = clock.getUTCNow();
+ int clockShift = 10;
+ DateTime restartDate = startDate.plusDays(clockShift).minusDays(1);
+ LinkedList<ExistingEvent> expected = new LinkedList<SubscriptionRepair.ExistingEvent>();
+
+ expected.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, newBaseProduct, PhaseType.TRIAL,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.NO_BILLING_PERIOD, restartDate));
+ expected.add(createExistingEventForAssertion(SubscriptionTransitionType.PHASE, newBaseProduct, PhaseType.EVERGREEN,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, restartDate.plusDays(30)));
+
+ UUID baseSubscriptionId = testBPRepairCreate(true, startDate, clockShift, baseProduct, newBaseProduct, expected);
+
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+ clock.addDeltaFromReality(getDurationDay(32));
+ assertTrue(testListener.isApiCompleted(5000));
+
+ // CHECK WHAT"S GOING ON AFTER WE MOVE CLOCK-- FUTURE MOTIFICATION SHOULD KICK IN
+ SubscriptionData subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscriptionId);
+
+ assertEquals(subscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+ assertEquals(subscription.getBundleId(), bundle.getId());
+ assertEquals(subscription.getStartDate(), restartDate);
+ assertEquals(subscription.getBundleStartDate(), restartDate);
+
+ Plan currentPlan = subscription.getCurrentPlan();
+ assertNotNull(currentPlan);
+ assertEquals(currentPlan.getProduct().getName(), newBaseProduct);
+ assertEquals(currentPlan.getProduct().getCategory(), ProductCategory.BASE);
+ assertEquals(currentPlan.getBillingPeriod(), BillingPeriod.MONTHLY);
+
+ PlanPhase currentPhase = subscription.getCurrentPhase();
+ assertNotNull(currentPhase);
+ assertEquals(currentPhase.getPhaseType(), PhaseType.EVERGREEN);
+ }
+
+
+ @Test(groups={"slow"})
+ public void testBPRepairReplaceCreateAfterTrial() throws Exception {
+ String baseProduct = "Shotgun";
+ String newBaseProduct = "Assault-Rifle";
+
+ DateTime startDate = clock.getUTCNow();
+ int clockShift = 40;
+ DateTime restartDate = startDate.plusDays(clockShift).minusDays(1);
+ LinkedList<ExistingEvent> expected = new LinkedList<SubscriptionRepair.ExistingEvent>();
+
+ expected.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, newBaseProduct, PhaseType.TRIAL,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.NO_BILLING_PERIOD, restartDate));
+ expected.add(createExistingEventForAssertion(SubscriptionTransitionType.PHASE, newBaseProduct, PhaseType.EVERGREEN,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, restartDate.plusDays(30)));
+
+ testBPRepairCreate(false, startDate, clockShift, baseProduct, newBaseProduct, expected);
+
+ }
+
+
+ private UUID testBPRepairCreate(boolean inTrial, DateTime startDate, int clockShift,
+ String baseProduct, String newBaseProduct, List<ExistingEvent> expectedEvents) throws Exception {
+
+ // CREATE BP
+ Subscription baseSubscription = createSubscription(baseProduct, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, startDate);
+
+ // MOVE CLOCK
+ if (clockShift > 0) {
+ if (!inTrial) {
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+ }
+ Duration durationShift = getDurationDay(clockShift);
+ clock.setDeltaFromReality(durationShift, 0);
+ if (!inTrial) {
+ assertTrue(testListener.isApiCompleted(5000));
+ }
+ }
+
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+
+ DateTime newCreateTime = baseSubscription.getStartDate().plusDays(clockShift - 1);
+
+ PlanPhaseSpecifier spec = new PlanPhaseSpecifier(newBaseProduct, ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.TRIAL);
+
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CREATE, newCreateTime, spec);
+ List<DeletedEvent> des = new LinkedList<SubscriptionRepair.DeletedEvent>();
+ des.add(createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(0).getEventId()));
+ des.add(createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(1).getEventId()));
+
+ SubscriptionRepair sRepair = createSubscriptionReapir(baseSubscription.getId(), des, Collections.singletonList(ne));
+
+ // FIRST ISSUE DRY RUN
+ BundleRepair bRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
+
+ boolean dryRun = true;
+ BundleRepair dryRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
+ List<SubscriptionRepair> subscriptionRepair = dryRunBundleRepair.getSubscriptions();
+ assertEquals(subscriptionRepair.size(), 1);
+ SubscriptionRepair cur = subscriptionRepair.get(0);
+ assertEquals(cur.getId(), baseSubscription.getId());
+
+ List<ExistingEvent> events = cur.getExistingEvents();
+ assertEquals(expectedEvents.size(), events.size());
+ int index = 0;
+ for (ExistingEvent e : expectedEvents) {
+ validateExistingEventForAssertion(e, events.get(index++));
+ }
+ SubscriptionData dryRunBaseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+
+ assertEquals(dryRunBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
+ assertEquals(dryRunBaseSubscription.getBundleId(), bundle.getId());
+ assertEquals(dryRunBaseSubscription.getStartDate(), baseSubscription.getStartDate());
+
+ Plan currentPlan = dryRunBaseSubscription.getCurrentPlan();
+ assertNotNull(currentPlan);
+ assertEquals(currentPlan.getProduct().getName(), baseProduct);
+ assertEquals(currentPlan.getProduct().getCategory(), ProductCategory.BASE);
+ assertEquals(currentPlan.getBillingPeriod(), BillingPeriod.MONTHLY);
+
+ PlanPhase currentPhase = dryRunBaseSubscription.getCurrentPhase();
+ assertNotNull(currentPhase);
+ if (inTrial) {
+ assertEquals(currentPhase.getPhaseType(), PhaseType.TRIAL);
+ } else {
+ assertEquals(currentPhase.getPhaseType(), PhaseType.EVERGREEN);
+ }
+
+ // SECOND RE-ISSUE CALL-- NON DRY RUN
+ dryRun = false;
+ BundleRepair realRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
+
+ subscriptionRepair = realRunBundleRepair.getSubscriptions();
+ assertEquals(subscriptionRepair.size(), 1);
+ cur = subscriptionRepair.get(0);
+ assertEquals(cur.getId(), baseSubscription.getId());
+
+ events = cur.getExistingEvents();
+ for (ExistingEvent e : events) {
+ log.info(String.format("%s, %s, %s, %s", e.getSubscriptionTransitionType(), e.getEffectiveDate(), e.getPlanPhaseSpecifier().getProductName(), e.getPlanPhaseSpecifier().getPhaseType()));
+ }
+ assertEquals(events.size(), expectedEvents.size());
+ index = 0;
+ for (ExistingEvent e : expectedEvents) {
+ validateExistingEventForAssertion(e, events.get(index++));
+ }
+ SubscriptionData realRunBaseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+ assertEquals(realRunBaseSubscription.getAllTransitions().size(), 2);
+
+
+ assertEquals(realRunBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+ assertEquals(realRunBaseSubscription.getBundleId(), bundle.getId());
+ assertEquals(realRunBaseSubscription.getStartDate(), newCreateTime);
+
+ currentPlan = realRunBaseSubscription.getCurrentPlan();
+ assertNotNull(currentPlan);
+ assertEquals(currentPlan.getProduct().getName(), newBaseProduct);
+ assertEquals(currentPlan.getProduct().getCategory(), ProductCategory.BASE);
+ assertEquals(currentPlan.getBillingPeriod(), BillingPeriod.MONTHLY);
+
+ currentPhase = realRunBaseSubscription.getCurrentPhase();
+ assertNotNull(currentPhase);
+ assertEquals(currentPhase.getPhaseType(), PhaseType.TRIAL);
+
+ return baseSubscription.getId();
+ }
+
+ @Test(groups={"slow"})
+ public void testBPRepairAddChangeInTrial() throws Exception {
+
+ String baseProduct = "Shotgun";
+ String newBaseProduct = "Assault-Rifle";
+
+ DateTime startDate = clock.getUTCNow();
+ int clockShift = 10;
+ DateTime changeDate = startDate.plusDays(clockShift).minusDays(1);
+ LinkedList<ExistingEvent> expected = new LinkedList<SubscriptionRepair.ExistingEvent>();
+
+ expected.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, baseProduct, PhaseType.TRIAL,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.NO_BILLING_PERIOD, startDate));
+ expected.add(createExistingEventForAssertion(SubscriptionTransitionType.CHANGE, newBaseProduct, PhaseType.TRIAL,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.NO_BILLING_PERIOD, changeDate));
+ expected.add(createExistingEventForAssertion(SubscriptionTransitionType.PHASE, newBaseProduct, PhaseType.EVERGREEN,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, startDate.plusDays(30)));
+
+ UUID baseSubscriptionId = testBPRepairAddChange(true, startDate, clockShift, baseProduct, newBaseProduct, expected, 3);
+
+ // CHECK WHAT"S GOING ON AFTER WE MOVE CLOCK-- FUTURE MOTIFICATION SHOULD KICK IN
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+ clock.addDeltaFromReality(getDurationDay(32));
+ assertTrue(testListener.isApiCompleted(5000));
+ SubscriptionData subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscriptionId);
+
+ assertEquals(subscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+ assertEquals(subscription.getBundleId(), bundle.getId());
+ assertEquals(subscription.getStartDate(), startDate);
+ assertEquals(subscription.getBundleStartDate(), startDate);
+
+ Plan currentPlan = subscription.getCurrentPlan();
+ assertNotNull(currentPlan);
+ assertEquals(currentPlan.getProduct().getName(), newBaseProduct);
+ assertEquals(currentPlan.getProduct().getCategory(), ProductCategory.BASE);
+ assertEquals(currentPlan.getBillingPeriod(), BillingPeriod.MONTHLY);
+
+ PlanPhase currentPhase = subscription.getCurrentPhase();
+ assertNotNull(currentPhase);
+ assertEquals(currentPhase.getPhaseType(), PhaseType.EVERGREEN);
+ }
+
+ @Test(groups={"slow"})
+ public void testBPRepairAddChangeAfterTrial() throws Exception {
+
+ String baseProduct = "Shotgun";
+ String newBaseProduct = "Assault-Rifle";
+
+ DateTime startDate = clock.getUTCNow();
+ int clockShift = 40;
+ DateTime changeDate = startDate.plusDays(clockShift).minusDays(1);
+
+ LinkedList<ExistingEvent> expected = new LinkedList<SubscriptionRepair.ExistingEvent>();
+ expected.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, baseProduct, PhaseType.TRIAL,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.NO_BILLING_PERIOD, startDate));
+ expected.add(createExistingEventForAssertion(SubscriptionTransitionType.PHASE, baseProduct, PhaseType.EVERGREEN,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, startDate.plusDays(30)));
+ expected.add(createExistingEventForAssertion(SubscriptionTransitionType.CHANGE, newBaseProduct, PhaseType.EVERGREEN,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, changeDate));
+ testBPRepairAddChange(false, startDate, clockShift, baseProduct, newBaseProduct, expected, 3);
+ }
+
+
+ private UUID testBPRepairAddChange(boolean inTrial, DateTime startDate, int clockShift,
+ String baseProduct, String newBaseProduct, List<ExistingEvent> expectedEvents, int expectedTransitions) throws Exception {
+
+ // CREATE BP
+ Subscription baseSubscription = createSubscription(baseProduct, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, startDate);
+
+ // MOVE CLOCK
+ if (!inTrial) {
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+ }
+
+ Duration durationShift = getDurationDay(clockShift);
+ clock.setDeltaFromReality(durationShift, 0);
+ if (!inTrial) {
+ assertTrue(testListener.isApiCompleted(5000));
+ }
+
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+
+ DateTime changeTime = baseSubscription.getStartDate().plusDays(clockShift - 1);
+
+ PlanPhaseSpecifier spec = new PlanPhaseSpecifier(newBaseProduct, ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.TRIAL);
+
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, changeTime, spec);
+ List<DeletedEvent> des = new LinkedList<SubscriptionRepair.DeletedEvent>();
+ if (inTrial) {
+ des.add(createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(1).getEventId()));
+ }
+ SubscriptionRepair sRepair = createSubscriptionReapir(baseSubscription.getId(), des, Collections.singletonList(ne));
+
+ // FIRST ISSUE DRY RUN
+ BundleRepair bRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
+
+ boolean dryRun = true;
+ BundleRepair dryRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
+ List<SubscriptionRepair> subscriptionRepair = dryRunBundleRepair.getSubscriptions();
+ assertEquals(subscriptionRepair.size(), 1);
+ SubscriptionRepair cur = subscriptionRepair.get(0);
+ assertEquals(cur.getId(), baseSubscription.getId());
+
+ List<ExistingEvent> events = cur.getExistingEvents();
+ assertEquals(expectedEvents.size(), events.size());
+ int index = 0;
+ for (ExistingEvent e : expectedEvents) {
+ validateExistingEventForAssertion(e, events.get(index++));
+ }
+ SubscriptionData dryRunBaseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+
+ assertEquals(dryRunBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
+ assertEquals(dryRunBaseSubscription.getBundleId(), bundle.getId());
+ assertEquals(dryRunBaseSubscription.getStartDate(), baseSubscription.getStartDate());
+
+ Plan currentPlan = dryRunBaseSubscription.getCurrentPlan();
+ assertNotNull(currentPlan);
+ assertEquals(currentPlan.getProduct().getName(), baseProduct);
+ assertEquals(currentPlan.getProduct().getCategory(), ProductCategory.BASE);
+ assertEquals(currentPlan.getBillingPeriod(), BillingPeriod.MONTHLY);
+
+ PlanPhase currentPhase = dryRunBaseSubscription.getCurrentPhase();
+ assertNotNull(currentPhase);
+ if (inTrial) {
+ assertEquals(currentPhase.getPhaseType(), PhaseType.TRIAL);
+ } else {
+ assertEquals(currentPhase.getPhaseType(), PhaseType.EVERGREEN);
+ }
+
+
+ // SECOND RE-ISSUE CALL-- NON DRY RUN
+ dryRun = false;
+ BundleRepair realRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
+
+ subscriptionRepair = realRunBundleRepair.getSubscriptions();
+ assertEquals(subscriptionRepair.size(), 1);
+ cur = subscriptionRepair.get(0);
+ assertEquals(cur.getId(), baseSubscription.getId());
+
+ events = cur.getExistingEvents();
+ assertEquals(expectedEvents.size(), events.size());
+ index = 0;
+ for (ExistingEvent e : expectedEvents) {
+ validateExistingEventForAssertion(e, events.get(index++));
+ }
+ SubscriptionData realRunBaseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+ assertEquals(realRunBaseSubscription.getAllTransitions().size(), expectedTransitions);
+
+
+ assertEquals(realRunBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+ assertEquals(realRunBaseSubscription.getBundleId(), bundle.getId());
+ assertEquals(realRunBaseSubscription.getStartDate(), baseSubscription.getStartDate());
+
+ currentPlan = realRunBaseSubscription.getCurrentPlan();
+ assertNotNull(currentPlan);
+ assertEquals(currentPlan.getProduct().getName(), newBaseProduct);
+ assertEquals(currentPlan.getProduct().getCategory(), ProductCategory.BASE);
+ assertEquals(currentPlan.getBillingPeriod(), BillingPeriod.MONTHLY);
+
+ currentPhase = realRunBaseSubscription.getCurrentPhase();
+ assertNotNull(currentPhase);
+ if (inTrial) {
+ assertEquals(currentPhase.getPhaseType(), PhaseType.TRIAL);
+ } else {
+ assertEquals(currentPhase.getPhaseType(), PhaseType.EVERGREEN);
+ }
+ return baseSubscription.getId();
+ }
+
+ @Test(groups={"slow"})
+ public void testRepairWithFurureCancelEvent() throws Exception {
+
+ DateTime startDate = clock.getUTCNow();
+
+ // CREATE BP
+ Subscription baseSubscription = createSubscription("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, startDate);
+
+ // MOVE CLOCK -- OUT OF TRIAL
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+ clock.setDeltaFromReality(getDurationDay(35), 0);
+ assertTrue(testListener.isApiCompleted(5000));
+
+ // SET CTD to BASE SUBSCRIPTION SP CANCEL OCCURS EOT
+ DateTime newChargedThroughDate = baseSubscription.getStartDate().plusDays(30).plusMonths(1);
+ billingApi.setChargedThroughDate(baseSubscription.getId(), newChargedThroughDate, context);
+ baseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+
+
+ DateTime requestedChange = clock.getUTCNow();
+ baseSubscription.changePlan("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, requestedChange, context);
+
+
+ // CHECK CHANGE DID NOT OCCUR YET
+ Plan currentPlan = baseSubscription.getCurrentPlan();
+ assertNotNull(currentPlan);
+ assertEquals(currentPlan.getProduct().getName(), "Shotgun");
+ assertEquals(currentPlan.getProduct().getCategory(), ProductCategory.BASE);
+ assertEquals(currentPlan.getBillingPeriod(), BillingPeriod.MONTHLY);
+
+
+ DateTime repairTime = clock.getUTCNow().minusDays(1);
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+
+ PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, repairTime, spec);
+ List<DeletedEvent> des = new LinkedList<SubscriptionRepair.DeletedEvent>();
+ des.add(createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(2).getEventId()));
+
+ SubscriptionRepair sRepair = createSubscriptionReapir(baseSubscription.getId(), des, Collections.singletonList(ne));
+
+ // SKIP DRY RUN AND DO REPAIR...
+ BundleRepair bRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
+
+ boolean dryRun = false;
+ repairApi.repairBundle(bRepair, dryRun, context);
+
+ baseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+
+ assertEquals(((SubscriptionData) baseSubscription).getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+ assertEquals(baseSubscription.getBundleId(), bundle.getId());
+ assertEquals(baseSubscription.getStartDate(), baseSubscription.getStartDate());
+
+ currentPlan = baseSubscription.getCurrentPlan();
+ assertNotNull(currentPlan);
+ assertEquals(currentPlan.getProduct().getName(), "Assault-Rifle");
+ assertEquals(currentPlan.getProduct().getCategory(), ProductCategory.BASE);
+ assertEquals(currentPlan.getBillingPeriod(), BillingPeriod.MONTHLY);
+
+ PlanPhase currentPhase = baseSubscription.getCurrentPhase();
+ assertNotNull(currentPhase);
+ assertEquals(currentPhase.getPhaseType(), PhaseType.EVERGREEN);
+ }
+
+
+ // Needs real SQL backend to be tested properly
+ @Test(groups={"slow"})
+ public void testENT_REPAIR_VIEW_CHANGED_newEvent() throws Exception {
+
+ TestWithException test = new TestWithException();
+ DateTime startDate = clock.getUTCNow();
+
+ testListener.reset();
+ clock.resetDeltaFromReality();
+
+ final Subscription baseSubscription = createSubscription("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, startDate);
+
+ test.withException(new TestWithExceptionCallback() {
+ @Override
+ public void doTest() throws EntitlementRepairException, EntitlementUserApiException {
+
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+ PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, baseSubscription.getStartDate().plusDays(10), spec);
+ List<DeletedEvent> des = new LinkedList<SubscriptionRepair.DeletedEvent>();
+ des.add(createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(0).getEventId()));
+ des.add(createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(1).getEventId()));
+ SubscriptionRepair sRepair = createSubscriptionReapir(baseSubscription.getId(), des, Collections.singletonList(ne));
+
+ BundleRepair bRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
+
+ testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
+ DateTime changeTime = clock.getUTCNow();
+ baseSubscription.changePlan("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, changeTime, context);
+ assertTrue(testListener.isApiCompleted(5000));
+
+ repairApi.repairBundle(bRepair, true, context);
+ }
+ }, ErrorCode.ENT_REPAIR_VIEW_CHANGED);
+ }
+
+ @Test(groups={"slow"}, enabled=false)
+ public void testENT_REPAIR_VIEW_CHANGED_ctd() throws Exception {
+
+ TestWithException test = new TestWithException();
+ DateTime startDate = clock.getUTCNow();
+
+ testListener.reset();
+ clock.resetDeltaFromReality();
+
+ final Subscription baseSubscription = createSubscription("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, startDate);
+
+ test.withException(new TestWithExceptionCallback() {
+ @Override
+ public void doTest() throws EntitlementRepairException, EntitlementUserApiException {
+
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+ PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, baseSubscription.getStartDate().plusDays(10), spec);
+ List<DeletedEvent> des = new LinkedList<SubscriptionRepair.DeletedEvent>();
+ des.add(createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(0).getEventId()));
+ des.add(createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(1).getEventId()));
+ SubscriptionRepair sRepair = createSubscriptionReapir(baseSubscription.getId(), des, Collections.singletonList(ne));
+
+ BundleRepair bRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
+
+ DateTime newChargedThroughDate = baseSubscription.getStartDate().plusDays(30).plusMonths(1);
+ billingApi.setChargedThroughDate(baseSubscription.getId(), newChargedThroughDate, context);
+ entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+
+ repairApi.repairBundle(bRepair, true, context);
+ }
+ }, ErrorCode.ENT_REPAIR_VIEW_CHANGED);
+ }
+
+}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairWithAO.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairWithAO.java
new file mode 100644
index 0000000..56a6d2e
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairWithAO.java
@@ -0,0 +1,745 @@
+/*
+ * 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.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import org.joda.time.DateTime;
+import org.testng.annotations.Test;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Stage;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.Duration;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+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.api.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.DeletedEvent;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.ExistingEvent;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.NewEvent;
+import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.SubscriptionEvents;
+import com.ning.billing.entitlement.glue.MockEngineModuleSql;
+
+public class TestRepairWithAO extends TestApiBaseRepair {
+
+ @Override
+ public Injector getInjector() {
+ return Guice.createInjector(Stage.DEVELOPMENT, new MockEngineModuleSql());
+ }
+
+ @Test(groups={"slow"})
+ public void testRepairChangeBPWithAddonIncluded() throws Exception {
+
+ String baseProduct = "Shotgun";
+ BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+ String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+ // CREATE BP
+ SubscriptionData baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+ // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
+ Duration someTimeLater = getDurationDay(3);
+ clock.setDeltaFromReality(someTimeLater, DAY_IN_MS);
+
+ SubscriptionData aoSubscription = createSubscription("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+
+ SubscriptionData aoSubscription2 = createSubscription("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+
+ // MOVE CLOCK A LITTLE BIT MORE -- STILL IN TRIAL
+ clock.addDeltaFromReality(someTimeLater);
+
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+
+ // Quick check
+ SubscriptionRepair bpRepair = getSubscriptionRepair(baseSubscription.getId(), bundleRepair);
+ assertEquals(bpRepair.getExistingEvents().size(), 2);
+
+ SubscriptionRepair aoRepair = getSubscriptionRepair(aoSubscription.getId(), bundleRepair);
+ assertEquals(aoRepair.getExistingEvents().size(), 2);
+
+ SubscriptionRepair aoRepair2 = getSubscriptionRepair(aoSubscription2.getId(), bundleRepair);
+ assertEquals(aoRepair2.getExistingEvents().size(), 2);
+
+ DateTime bpChangeDate = clock.getUTCNow().minusDays(1);
+
+ List<DeletedEvent> des = new LinkedList<SubscriptionRepair.DeletedEvent>();
+ des.add(createDeletedEvent(bpRepair.getExistingEvents().get(1).getEventId()));
+
+ PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.TRIAL);
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, bpChangeDate, spec);
+
+ bpRepair = createSubscriptionReapir(baseSubscription.getId(), des, Collections.singletonList(ne));
+
+ bundleRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(bpRepair));
+
+ boolean dryRun = true;
+ BundleRepair dryRunBundleRepair = repairApi.repairBundle(bundleRepair, dryRun, context);
+
+ aoRepair = getSubscriptionRepair(aoSubscription.getId(), dryRunBundleRepair);
+ assertEquals(aoRepair.getExistingEvents().size(), 2);
+
+ aoRepair2 = getSubscriptionRepair(aoSubscription2.getId(), dryRunBundleRepair);
+ assertEquals(aoRepair.getExistingEvents().size(), 2);
+
+ bpRepair = getSubscriptionRepair(baseSubscription.getId(), dryRunBundleRepair);
+ assertEquals(bpRepair.getExistingEvents().size(), 3);
+
+ // Check expected for AO
+ List<ExistingEvent> expectedAO = new LinkedList<SubscriptionRepair.ExistingEvent>();
+ expectedAO.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Telescopic-Scope", PhaseType.DISCOUNT,
+ ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, aoSubscription.getStartDate()));
+ expectedAO.add(createExistingEventForAssertion(SubscriptionTransitionType.CANCEL, "Telescopic-Scope", PhaseType.DISCOUNT,
+ ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, bpChangeDate));
+ int index = 0;
+ for (ExistingEvent e : expectedAO) {
+ validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));
+ }
+
+ List<ExistingEvent> expectedAO2 = new LinkedList<SubscriptionRepair.ExistingEvent>();
+ expectedAO2.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Laser-Scope", PhaseType.DISCOUNT,
+ ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, aoSubscription2.getStartDate()));
+ expectedAO2.add(createExistingEventForAssertion(SubscriptionTransitionType.PHASE, "Laser-Scope", PhaseType.EVERGREEN,
+ ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, aoSubscription2.getStartDate().plusMonths(1)));
+ index = 0;
+ for (ExistingEvent e : expectedAO2) {
+ validateExistingEventForAssertion(e, aoRepair2.getExistingEvents().get(index++));
+ }
+
+ // Check expected for BP
+ List<ExistingEvent> expectedBP = new LinkedList<SubscriptionRepair.ExistingEvent>();
+ expectedBP.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Shotgun", PhaseType.TRIAL,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.NO_BILLING_PERIOD, baseSubscription.getStartDate()));
+ expectedBP.add(createExistingEventForAssertion(SubscriptionTransitionType.CHANGE, "Assault-Rifle", PhaseType.TRIAL,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.NO_BILLING_PERIOD, bpChangeDate));
+ expectedBP.add(createExistingEventForAssertion(SubscriptionTransitionType.PHASE, "Assault-Rifle", PhaseType.EVERGREEN,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, baseSubscription.getStartDate().plusDays(30)));
+ index = 0;
+ for (ExistingEvent e : expectedBP) {
+ validateExistingEventForAssertion(e, bpRepair.getExistingEvents().get(index++));
+ }
+
+
+ SubscriptionData newAoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+ assertEquals(newAoSubscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(newAoSubscription.getAllTransitions().size(), 2);
+ assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
+
+ SubscriptionData newAoSubscription2 = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription2.getId());
+ assertEquals(newAoSubscription2.getState(), SubscriptionState.ACTIVE);
+ assertEquals(newAoSubscription2.getAllTransitions().size(), 2);
+ assertEquals(newAoSubscription2.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
+
+
+ SubscriptionData newBaseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+ assertEquals(newBaseSubscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(newBaseSubscription.getAllTransitions().size(), 2);
+ assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
+
+ dryRun = false;
+ BundleRepair realRunBundleRepair = repairApi.repairBundle(bundleRepair, dryRun, context);
+
+
+ aoRepair = getSubscriptionRepair(aoSubscription.getId(), realRunBundleRepair);
+ assertEquals(aoRepair.getExistingEvents().size(), 2);
+
+ bpRepair = getSubscriptionRepair(baseSubscription.getId(), realRunBundleRepair);
+ assertEquals(bpRepair.getExistingEvents().size(), 3);
+
+ index = 0;
+ for (ExistingEvent e : expectedAO) {
+ validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));
+ }
+
+ index = 0;
+ for (ExistingEvent e : expectedAO2) {
+ validateExistingEventForAssertion(e, aoRepair2.getExistingEvents().get(index++));
+ }
+
+ index = 0;
+ for (ExistingEvent e : expectedBP) {
+ validateExistingEventForAssertion(e, bpRepair.getExistingEvents().get(index++));
+ }
+
+ newAoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+ assertEquals(newAoSubscription.getState(), SubscriptionState.CANCELLED);
+ assertEquals(newAoSubscription.getAllTransitions().size(), 2);
+ assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+
+ newAoSubscription2 = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription2.getId());
+ assertEquals(newAoSubscription2.getState(), SubscriptionState.ACTIVE);
+ assertEquals(newAoSubscription2.getAllTransitions().size(), 2);
+ assertEquals(newAoSubscription2.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+
+
+ newBaseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+ assertEquals(newBaseSubscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(newBaseSubscription.getAllTransitions().size(), 3);
+ assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+ }
+
+ @Test(groups={"slow"})
+ public void testRepairChangeBPWithAddonNonAvailable() throws Exception {
+
+ String baseProduct = "Shotgun";
+ BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+ String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+ // CREATE BP
+ SubscriptionData baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+ // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
+ Duration someTimeLater = getDurationDay(3);
+ clock.setDeltaFromReality(someTimeLater, DAY_IN_MS);
+
+ SubscriptionData aoSubscription = createSubscription("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+
+ // MOVE CLOCK A LITTLE BIT MORE -- AFTER TRIAL
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+ someTimeLater = getDurationDay(32);
+ clock.addDeltaFromReality(someTimeLater);
+ assertTrue(testListener.isApiCompleted(7000));
+
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+
+ // Quick check
+ SubscriptionRepair bpRepair = getSubscriptionRepair(baseSubscription.getId(), bundleRepair);
+ assertEquals(bpRepair.getExistingEvents().size(), 2);
+
+ SubscriptionRepair aoRepair = getSubscriptionRepair(aoSubscription.getId(), bundleRepair);
+ assertEquals(aoRepair.getExistingEvents().size(), 2);
+
+ DateTime bpChangeDate = clock.getUTCNow().minusDays(1);
+
+ PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, bpChangeDate, spec);
+
+ bpRepair = createSubscriptionReapir(baseSubscription.getId(), Collections.<SubscriptionRepair.DeletedEvent>emptyList(), Collections.singletonList(ne));
+
+ bundleRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(bpRepair));
+
+ boolean dryRun = true;
+ BundleRepair dryRunBundleRepair = repairApi.repairBundle(bundleRepair, dryRun, context);
+
+ aoRepair = getSubscriptionRepair(aoSubscription.getId(), dryRunBundleRepair);
+ assertEquals(aoRepair.getExistingEvents().size(), 3);
+
+ bpRepair = getSubscriptionRepair(baseSubscription.getId(), dryRunBundleRepair);
+ assertEquals(bpRepair.getExistingEvents().size(), 3);
+
+ // Check expected for AO
+ List<ExistingEvent> expectedAO = new LinkedList<SubscriptionRepair.ExistingEvent>();
+ expectedAO.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Telescopic-Scope", PhaseType.DISCOUNT,
+ ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, aoSubscription.getStartDate()));
+ expectedAO.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Telescopic-Scope", PhaseType.EVERGREEN,
+ ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, baseSubscription.getStartDate().plusMonths(1)));
+ expectedAO.add(createExistingEventForAssertion(SubscriptionTransitionType.CANCEL, "Telescopic-Scope", PhaseType.EVERGREEN,
+ ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, bpChangeDate));
+ int index = 0;
+ for (ExistingEvent e : expectedAO) {
+ validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));
+ }
+
+ // Check expected for BP
+ List<ExistingEvent> expectedBP = new LinkedList<SubscriptionRepair.ExistingEvent>();
+ expectedBP.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Shotgun", PhaseType.TRIAL,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.NO_BILLING_PERIOD, baseSubscription.getStartDate()));
+ expectedBP.add(createExistingEventForAssertion(SubscriptionTransitionType.PHASE, "Shotgun", PhaseType.EVERGREEN,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, baseSubscription.getStartDate().plusDays(30)));
+ expectedBP.add(createExistingEventForAssertion(SubscriptionTransitionType.CHANGE, "Pistol", PhaseType.EVERGREEN,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, bpChangeDate));
+ index = 0;
+ for (ExistingEvent e : expectedBP) {
+ validateExistingEventForAssertion(e, bpRepair.getExistingEvents().get(index++));
+ }
+
+ SubscriptionData newAoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+ assertEquals(newAoSubscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(newAoSubscription.getAllTransitions().size(), 2);
+ assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
+
+ SubscriptionData newBaseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+ assertEquals(newBaseSubscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(newBaseSubscription.getAllTransitions().size(), 2);
+ assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
+
+ dryRun = false;
+ BundleRepair realRunBundleRepair = repairApi.repairBundle(bundleRepair, dryRun, context);
+
+ aoRepair = getSubscriptionRepair(aoSubscription.getId(), realRunBundleRepair);
+ assertEquals(aoRepair.getExistingEvents().size(), 3);
+
+ bpRepair = getSubscriptionRepair(baseSubscription.getId(), realRunBundleRepair);
+ assertEquals(bpRepair.getExistingEvents().size(), 3);
+
+ index = 0;
+ for (ExistingEvent e : expectedAO) {
+ validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));
+ }
+
+ index = 0;
+ for (ExistingEvent e : expectedBP) {
+ validateExistingEventForAssertion(e, bpRepair.getExistingEvents().get(index++));
+ }
+
+ newAoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+ assertEquals(newAoSubscription.getState(), SubscriptionState.CANCELLED);
+ assertEquals(newAoSubscription.getAllTransitions().size(), 3);
+ assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+
+ newBaseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+ assertEquals(newBaseSubscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(newBaseSubscription.getAllTransitions().size(), 3);
+ assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+ }
+
+ @Test(groups={"slow"})
+ public void testRepairCancelBP_EOT_WithAddons() throws Exception {
+
+ String baseProduct = "Shotgun";
+ BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+ String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+ // CREATE BP
+ SubscriptionData baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+ // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
+ Duration someTimeLater = getDurationDay(3);
+ clock.setDeltaFromReality(someTimeLater, DAY_IN_MS);
+
+ SubscriptionData aoSubscription = createSubscription("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+
+ // MOVE CLOCK A LITTLE BIT MORE -- AFTER TRIAL
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+ someTimeLater = getDurationDay(40);
+ clock.addDeltaFromReality(someTimeLater);
+ assertTrue(testListener.isApiCompleted(7000));
+
+ // SET CTD to BASE SUBSCRIPTION SP CANCEL OCCURS EOT
+ DateTime newChargedThroughDate = baseSubscription.getStartDate().plusDays(30).plusMonths(1);
+ billingApi.setChargedThroughDate(baseSubscription.getId(), newChargedThroughDate, context);
+ baseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+
+ // Quick check
+ SubscriptionRepair bpRepair = getSubscriptionRepair(baseSubscription.getId(), bundleRepair);
+ assertEquals(bpRepair.getExistingEvents().size(), 2);
+
+ SubscriptionRepair aoRepair = getSubscriptionRepair(aoSubscription.getId(), bundleRepair);
+ assertEquals(aoRepair.getExistingEvents().size(), 2);
+
+ DateTime bpCancelDate = clock.getUTCNow().minusDays(1);
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CANCEL, bpCancelDate, null);
+ bpRepair = createSubscriptionReapir(baseSubscription.getId(), Collections.<SubscriptionRepair.DeletedEvent>emptyList(), Collections.singletonList(ne));
+ bundleRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(bpRepair));
+
+ boolean dryRun = true;
+ BundleRepair dryRunBundleRepair = repairApi.repairBundle(bundleRepair, dryRun, context);
+
+ aoRepair = getSubscriptionRepair(aoSubscription.getId(), dryRunBundleRepair);
+ assertEquals(aoRepair.getExistingEvents().size(), 3);
+
+ bpRepair = getSubscriptionRepair(baseSubscription.getId(), dryRunBundleRepair);
+ assertEquals(bpRepair.getExistingEvents().size(), 3);
+
+ // Check expected for AO
+ List<ExistingEvent> expectedAO = new LinkedList<SubscriptionRepair.ExistingEvent>();
+ expectedAO.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Telescopic-Scope", PhaseType.DISCOUNT,
+ ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, aoSubscription.getStartDate()));
+ expectedAO.add(createExistingEventForAssertion(SubscriptionTransitionType.PHASE, "Telescopic-Scope", PhaseType.EVERGREEN,
+ ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, baseSubscription.getStartDate().plusMonths(1)));
+ expectedAO.add(createExistingEventForAssertion(SubscriptionTransitionType.CANCEL, "Telescopic-Scope", PhaseType.EVERGREEN,
+ ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, newChargedThroughDate));
+
+ int index = 0;
+ for (ExistingEvent e : expectedAO) {
+ validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));
+ }
+
+ // Check expected for BP
+ List<ExistingEvent> expectedBP = new LinkedList<SubscriptionRepair.ExistingEvent>();
+ expectedBP.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Shotgun", PhaseType.TRIAL,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.NO_BILLING_PERIOD, baseSubscription.getStartDate()));
+ expectedBP.add(createExistingEventForAssertion(SubscriptionTransitionType.PHASE, "Shotgun", PhaseType.EVERGREEN,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, baseSubscription.getStartDate().plusDays(30)));
+ expectedBP.add(createExistingEventForAssertion(SubscriptionTransitionType.CANCEL, "Shotgun", PhaseType.EVERGREEN,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, newChargedThroughDate));
+ index = 0;
+ for (ExistingEvent e : expectedBP) {
+ validateExistingEventForAssertion(e, bpRepair.getExistingEvents().get(index++));
+ }
+
+ SubscriptionData newAoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+ assertEquals(newAoSubscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(newAoSubscription.getAllTransitions().size(), 2);
+ assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
+
+ SubscriptionData newBaseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+ assertEquals(newBaseSubscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(newBaseSubscription.getAllTransitions().size(), 2);
+ assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
+
+ dryRun = false;
+ BundleRepair realRunBundleRepair = repairApi.repairBundle(bundleRepair, dryRun, context);
+
+ aoRepair = getSubscriptionRepair(aoSubscription.getId(), realRunBundleRepair);
+ assertEquals(aoRepair.getExistingEvents().size(), 3);
+
+ bpRepair = getSubscriptionRepair(baseSubscription.getId(), realRunBundleRepair);
+ assertEquals(bpRepair.getExistingEvents().size(), 3);
+
+ index = 0;
+ for (ExistingEvent e : expectedAO) {
+ validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));
+ }
+
+ index = 0;
+ for (ExistingEvent e : expectedBP) {
+ validateExistingEventForAssertion(e, bpRepair.getExistingEvents().get(index++));
+ }
+
+ newAoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+ assertEquals(newAoSubscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(newAoSubscription.getAllTransitions().size(), 3);
+ assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+
+ newBaseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+ assertEquals(newBaseSubscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(newBaseSubscription.getAllTransitions().size(), 3);
+ assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+
+ // MOVE CLOCK AFTER CANCEL DATE
+ testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
+ testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
+ someTimeLater = getDurationDay(32);
+ clock.addDeltaFromReality(someTimeLater);
+ assertTrue(testListener.isApiCompleted(7000));
+
+ newAoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+ assertEquals(newAoSubscription.getState(), SubscriptionState.CANCELLED);
+ assertEquals(newAoSubscription.getAllTransitions().size(), 3);
+ assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+
+ newBaseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+ assertEquals(newBaseSubscription.getState(), SubscriptionState.CANCELLED);
+ assertEquals(newBaseSubscription.getAllTransitions().size(), 3);
+ assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+ }
+
+
+
+ @Test(groups={"slow"})
+ public void testRepairCancelAO() throws Exception {
+ String baseProduct = "Shotgun";
+ BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+ String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+ // CREATE BP
+ SubscriptionData baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+ // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
+ Duration someTimeLater = getDurationDay(3);
+ clock.setDeltaFromReality(someTimeLater, DAY_IN_MS);
+
+ SubscriptionData aoSubscription = createSubscription("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+
+ // MOVE CLOCK A LITTLE BIT MORE -- STILL IN TRIAL
+ clock.addDeltaFromReality(someTimeLater);
+
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+
+ // Quick check
+ SubscriptionRepair bpRepair = getSubscriptionRepair(baseSubscription.getId(), bundleRepair);
+ assertEquals(bpRepair.getExistingEvents().size(), 2);
+
+ SubscriptionRepair aoRepair = getSubscriptionRepair(aoSubscription.getId(), bundleRepair);
+ assertEquals(aoRepair.getExistingEvents().size(), 2);
+
+
+ List<DeletedEvent> des = new LinkedList<SubscriptionRepair.DeletedEvent>();
+ des.add(createDeletedEvent(aoRepair.getExistingEvents().get(1).getEventId()));
+ DateTime aoCancelDate = aoSubscription.getStartDate().plusDays(1);
+
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CANCEL, aoCancelDate, null);
+
+ SubscriptionRepair saoRepair = createSubscriptionReapir(aoSubscription.getId(), des, Collections.singletonList(ne));
+
+ BundleRepair bRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(saoRepair));
+
+ boolean dryRun = true;
+ BundleRepair dryRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
+
+ aoRepair = getSubscriptionRepair(aoSubscription.getId(), dryRunBundleRepair);
+ assertEquals(aoRepair.getExistingEvents().size(), 2);
+
+ bpRepair = getSubscriptionRepair(baseSubscription.getId(), bundleRepair);
+ assertEquals(bpRepair.getExistingEvents().size(), 2);
+
+ List<ExistingEvent> expected = new LinkedList<SubscriptionRepair.ExistingEvent>();
+ expected.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Telescopic-Scope", PhaseType.DISCOUNT,
+ ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, aoSubscription.getStartDate()));
+ expected.add(createExistingEventForAssertion(SubscriptionTransitionType.CANCEL, "Telescopic-Scope", PhaseType.DISCOUNT,
+ ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, aoCancelDate));
+ int index = 0;
+ for (ExistingEvent e : expected) {
+ validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));
+ }
+ SubscriptionData newAoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+ assertEquals(newAoSubscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(newAoSubscription.getAllTransitions().size(), 2);
+ assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
+
+ SubscriptionData newBaseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+ assertEquals(newBaseSubscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(newBaseSubscription.getAllTransitions().size(), 2);
+ assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
+
+ dryRun = false;
+ BundleRepair realRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
+
+ aoRepair = getSubscriptionRepair(aoSubscription.getId(), realRunBundleRepair);
+ assertEquals(aoRepair.getExistingEvents().size(), 2);
+ index = 0;
+ for (ExistingEvent e : expected) {
+ validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));
+ }
+
+ newAoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+ assertEquals(newAoSubscription.getState(), SubscriptionState.CANCELLED);
+ assertEquals(newAoSubscription.getAllTransitions().size(), 2);
+ assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+
+ newBaseSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(baseSubscription.getId());
+ assertEquals(newBaseSubscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(newBaseSubscription.getAllTransitions().size(), 2);
+ assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
+ }
+
+
+ @Test(groups={"slow"})
+ public void testRepairRecreateAO() throws Exception {
+ String baseProduct = "Shotgun";
+ BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+ String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+ // CREATE BP
+ SubscriptionData baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+ // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
+ Duration someTimeLater = getDurationDay(3);
+ clock.setDeltaFromReality(someTimeLater, DAY_IN_MS);
+
+ SubscriptionData aoSubscription = createSubscription("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+
+ // MOVE CLOCK A LITTLE BIT MORE -- STILL IN TRIAL
+ clock.addDeltaFromReality(someTimeLater);
+
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+
+ // Quick check
+ SubscriptionRepair bpRepair = getSubscriptionRepair(baseSubscription.getId(), bundleRepair);
+ assertEquals(bpRepair.getExistingEvents().size(), 2);
+
+ SubscriptionRepair aoRepair = getSubscriptionRepair(aoSubscription.getId(), bundleRepair);
+ assertEquals(aoRepair.getExistingEvents().size(), 2);
+
+
+ List<DeletedEvent> des = new LinkedList<SubscriptionRepair.DeletedEvent>();
+ des.add(createDeletedEvent(aoRepair.getExistingEvents().get(0).getEventId()));
+ des.add(createDeletedEvent(aoRepair.getExistingEvents().get(1).getEventId()));
+
+ DateTime aoRecreateDate = aoSubscription.getStartDate().plusDays(1);
+ PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.DISCOUNT);
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CREATE, aoRecreateDate, spec);
+
+ SubscriptionRepair saoRepair = createSubscriptionReapir(aoSubscription.getId(), des, Collections.singletonList(ne));
+
+ BundleRepair bRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(saoRepair));
+
+ boolean dryRun = true;
+ BundleRepair dryRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
+
+ aoRepair = getSubscriptionRepair(aoSubscription.getId(), dryRunBundleRepair);
+ assertEquals(aoRepair.getExistingEvents().size(), 2);
+
+
+ List<ExistingEvent> expected = new LinkedList<SubscriptionRepair.ExistingEvent>();
+ expected.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Telescopic-Scope", PhaseType.DISCOUNT,
+ ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, aoRecreateDate));
+ expected.add(createExistingEventForAssertion(SubscriptionTransitionType.PHASE, "Telescopic-Scope", PhaseType.EVERGREEN,
+ ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, baseSubscription.getStartDate().plusMonths(1) /* Bundle align */));
+ int index = 0;
+ for (ExistingEvent e : expected) {
+ validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));
+ }
+ SubscriptionData newAoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+ assertEquals(newAoSubscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(newAoSubscription.getAllTransitions().size(), 2);
+ assertEquals(newAoSubscription.getStartDate(), aoSubscription.getStartDate());
+ assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
+
+ // NOW COMMIT
+ dryRun = false;
+ BundleRepair realRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
+
+ aoRepair = getSubscriptionRepair(aoSubscription.getId(), realRunBundleRepair);
+ assertEquals(aoRepair.getExistingEvents().size(), 2);
+ index = 0;
+ for (ExistingEvent e : expected) {
+ validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));
+ }
+
+ newAoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+ assertEquals(newAoSubscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(newAoSubscription.getAllTransitions().size(), 2);
+ assertEquals(newAoSubscription.getStartDate(), aoRecreateDate);
+ assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+
+ }
+
+ // Fasten your seatbelt here:
+ //
+ // We are doing repair for multi-phase tiered-addon with different alignment:
+ // Telescopic-Scope -> Laser-Scope
+ // Tiered ADON logic
+ // . Both multi phase
+ // . Telescopic-Scope (bundle align) and Laser-Scope is Subscription align
+ //
+ @Test(groups={"slow"})
+ public void testRepairChangeAOOK() throws Exception {
+
+ String baseProduct = "Shotgun";
+ BillingPeriod baseTerm = BillingPeriod.MONTHLY;
+ String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+ // CREATE BP
+ SubscriptionData baseSubscription = createSubscription(baseProduct, baseTerm, basePriceList);
+
+ // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
+ Duration someTimeLater = getDurationDay(3);
+ clock.setDeltaFromReality(someTimeLater, DAY_IN_MS);
+
+ SubscriptionData aoSubscription = createSubscription("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+
+ // MOVE CLOCK A LITTLE BIT MORE -- STILL IN TRIAL
+ clock.addDeltaFromReality(someTimeLater);
+
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+
+ // Quick check
+ SubscriptionRepair bpRepair = getSubscriptionRepair(baseSubscription.getId(), bundleRepair);
+ assertEquals(bpRepair.getExistingEvents().size(), 2);
+
+ SubscriptionRepair aoRepair = getSubscriptionRepair(aoSubscription.getId(), bundleRepair);
+ assertEquals(aoRepair.getExistingEvents().size(), 2);
+
+ List<DeletedEvent> des = new LinkedList<SubscriptionRepair.DeletedEvent>();
+ des.add(createDeletedEvent(aoRepair.getExistingEvents().get(1).getEventId()));
+ DateTime aoChangeDate = aoSubscription.getStartDate().plusDays(1);
+ PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Laser-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.TRIAL);
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, aoChangeDate, spec);
+
+ SubscriptionRepair saoRepair = createSubscriptionReapir(aoSubscription.getId(), des, Collections.singletonList(ne));
+
+ BundleRepair bRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(saoRepair));
+
+ boolean dryRun = true;
+ BundleRepair dryRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
+
+ aoRepair = getSubscriptionRepair(aoSubscription.getId(), dryRunBundleRepair);
+ assertEquals(aoRepair.getExistingEvents().size(), 3);
+
+
+ List<ExistingEvent> expected = new LinkedList<SubscriptionRepair.ExistingEvent>();
+ expected.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Telescopic-Scope", PhaseType.DISCOUNT,
+ ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, aoSubscription.getStartDate()));
+ expected.add(createExistingEventForAssertion(SubscriptionTransitionType.CHANGE, "Laser-Scope", PhaseType.DISCOUNT,
+ ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, aoChangeDate));
+ expected.add(createExistingEventForAssertion(SubscriptionTransitionType.PHASE, "Laser-Scope", PhaseType.EVERGREEN,
+ ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY,
+ aoSubscription.getStartDate().plusMonths(1) /* Subscription alignment */));
+
+ int index = 0;
+ for (ExistingEvent e : expected) {
+ validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));
+ }
+ SubscriptionData newAoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+ assertEquals(newAoSubscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(newAoSubscription.getAllTransitions().size(), 2);
+
+ // AND NOW COMMIT
+ dryRun = false;
+ BundleRepair realRunBundleRepair = repairApi.repairBundle(bRepair, dryRun, context);
+
+ aoRepair = getSubscriptionRepair(aoSubscription.getId(), realRunBundleRepair);
+ assertEquals(aoRepair.getExistingEvents().size(), 3);
+ index = 0;
+ for (ExistingEvent e : expected) {
+ validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));
+ }
+
+ newAoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+ assertEquals(newAoSubscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(newAoSubscription.getAllTransitions().size(), 3);
+
+
+ assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+ assertEquals(newAoSubscription.getBundleId(), bundle.getId());
+ assertEquals(newAoSubscription.getStartDate(), aoSubscription.getStartDate());
+
+ Plan currentPlan = newAoSubscription.getCurrentPlan();
+ assertNotNull(currentPlan);
+ assertEquals(currentPlan.getProduct().getName(), "Laser-Scope");
+ assertEquals(currentPlan.getProduct().getCategory(), ProductCategory.ADD_ON);
+ assertEquals(currentPlan.getBillingPeriod(), BillingPeriod.MONTHLY);
+
+ PlanPhase currentPhase = newAoSubscription.getCurrentPhase();
+ assertNotNull(currentPhase);
+ assertEquals(currentPhase.getPhaseType(), PhaseType.DISCOUNT);
+
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+ someTimeLater = getDurationDay(60);
+ clock.addDeltaFromReality(someTimeLater);
+ assertTrue(testListener.isApiCompleted(5000));
+
+ newAoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
+ currentPhase = newAoSubscription.getCurrentPhase();
+ assertNotNull(currentPhase);
+ assertEquals(currentPhase.getPhaseType(), PhaseType.EVERGREEN);
+ }
+}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairWithError.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairWithError.java
new file mode 100644
index 0000000..d82f680
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/repair/TestRepairWithError.java
@@ -0,0 +1,428 @@
+/*
+ * 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.
+ */
+package com.ning.billing.entitlement.api.repair;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Stage;
+import com.ning.billing.ErrorCode;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.Duration;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.DeletedEvent;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.NewEvent;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.glue.MockEngineModuleMemory;
+
+public class TestRepairWithError extends TestApiBaseRepair {
+
+ private static final String baseProduct = "Shotgun";
+ private TestWithException test;
+ private Subscription baseSubscription;
+ private DateTime startDate;
+ @Override
+ public Injector getInjector() {
+ return Guice.createInjector(Stage.DEVELOPMENT, new MockEngineModuleMemory());
+ }
+
+
+ @BeforeMethod(groups={"fast"})
+ public void beforeMethod() throws Exception {
+ test = new TestWithException();
+ startDate = clock.getUTCNow();
+ baseSubscription = createSubscription(baseProduct, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, startDate);
+ testListener.reset();
+ clock.resetDeltaFromReality();
+ }
+
+ @Test(groups={"fast"})
+ public void testENT_REPAIR_NEW_EVENT_BEFORE_LAST_BP_REMAINING() throws Exception {
+ test.withException(new TestWithExceptionCallback() {
+ @Override
+ public void doTest() throws EntitlementRepairException {
+
+ // MOVE AFTER TRIAL
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+ Duration durationShift = getDurationDay(40);
+ clock.setDeltaFromReality(durationShift, 0);
+ assertTrue(testListener.isApiCompleted(5000));
+
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+ PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, baseSubscription.getStartDate().plusDays(10), spec);
+
+ SubscriptionRepair sRepair = createSubscriptionReapir(baseSubscription.getId(), Collections.<DeletedEvent>emptyList(), Collections.singletonList(ne));
+
+ BundleRepair bRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
+
+ repairApi.repairBundle(bRepair, true, context);
+ }
+ }, ErrorCode.ENT_REPAIR_NEW_EVENT_BEFORE_LAST_BP_REMAINING);
+ }
+
+ @Test(groups={"fast"})
+ public void testENT_REPAIR_INVALID_DELETE_SET() throws Exception {
+ test.withException(new TestWithExceptionCallback() {
+ @Override
+ public void doTest() throws EntitlementRepairException, EntitlementUserApiException {
+
+ Duration durationShift = getDurationDay(3);
+ clock.setDeltaFromReality(durationShift, 0);
+
+ testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
+ DateTime changeTime = clock.getUTCNow();
+ baseSubscription.changePlan("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, changeTime, context);
+ assertTrue(testListener.isApiCompleted(5000));
+
+ // MOVE AFTER TRIAL
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+ durationShift = getDurationDay(40);
+ clock.addDeltaFromReality(durationShift);
+ assertTrue(testListener.isApiCompleted(5000));
+
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+ PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, baseSubscription.getStartDate().plusDays(10), spec);
+ DeletedEvent de = createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(1).getEventId());
+
+ SubscriptionRepair sRepair = createSubscriptionReapir(baseSubscription.getId(), Collections.singletonList(de), Collections.singletonList(ne));
+ BundleRepair bRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
+
+ repairApi.repairBundle(bRepair, true, context);
+ }
+ }, ErrorCode.ENT_REPAIR_INVALID_DELETE_SET);
+ }
+
+ @Test(groups={"fast"})
+ public void testENT_REPAIR_NON_EXISTENT_DELETE_EVENT() throws Exception {
+ test.withException(new TestWithExceptionCallback() {
+ @Override
+ public void doTest() throws EntitlementRepairException {
+
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+ PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, baseSubscription.getStartDate().plusDays(10), spec);
+ DeletedEvent de = createDeletedEvent(UUID.randomUUID());
+ SubscriptionRepair sRepair = createSubscriptionReapir(baseSubscription.getId(), Collections.singletonList(de), Collections.singletonList(ne));
+
+ BundleRepair bRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
+
+ repairApi.repairBundle(bRepair, true, context);
+ }
+ }, ErrorCode.ENT_REPAIR_NON_EXISTENT_DELETE_EVENT);
+ }
+
+ @Test(groups={"fast"})
+ public void testENT_REPAIR_SUB_RECREATE_NOT_EMPTY() throws Exception {
+ test.withException(new TestWithExceptionCallback() {
+ @Override
+ public void doTest() throws EntitlementRepairException {
+
+ // MOVE AFTER TRIAL
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+ Duration durationShift = getDurationDay(40);
+ clock.setDeltaFromReality(durationShift, 0);
+ assertTrue(testListener.isApiCompleted(5000));
+
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+ PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CREATE, baseSubscription.getStartDate().plusDays(10), spec);
+ List<DeletedEvent> des = new LinkedList<SubscriptionRepair.DeletedEvent>();
+ des.add(createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(1).getEventId()));
+ SubscriptionRepair sRepair = createSubscriptionReapir(baseSubscription.getId(), des, Collections.singletonList(ne));
+
+ BundleRepair bRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
+
+ repairApi.repairBundle(bRepair, true, context);
+
+ }
+ }, ErrorCode.ENT_REPAIR_SUB_RECREATE_NOT_EMPTY);
+ }
+
+ @Test(groups={"fast"})
+ public void testENT_REPAIR_SUB_EMPTY() throws Exception {
+ test.withException(new TestWithExceptionCallback() {
+ @Override
+ public void doTest() throws EntitlementRepairException {
+
+ // MOVE AFTER TRIAL
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+ Duration durationShift = getDurationDay(40);
+ clock.setDeltaFromReality(durationShift, 0);
+ assertTrue(testListener.isApiCompleted(5000));
+
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+ PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, baseSubscription.getStartDate().plusDays(10), spec);
+ List<DeletedEvent> des = new LinkedList<SubscriptionRepair.DeletedEvent>();
+ des.add(createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(0).getEventId()));
+ des.add(createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(1).getEventId()));
+ SubscriptionRepair sRepair = createSubscriptionReapir(baseSubscription.getId(), des, Collections.singletonList(ne));
+
+ BundleRepair bRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
+
+ repairApi.repairBundle(bRepair, true, context);
+ }
+ }, ErrorCode.ENT_REPAIR_SUB_EMPTY);
+ }
+
+ @Test(groups={"fast"})
+ public void testENT_REPAIR_AO_CREATE_BEFORE_BP_START() throws Exception {
+ test.withException(new TestWithExceptionCallback() {
+ @Override
+ public void doTest() throws EntitlementRepairException, EntitlementUserApiException {
+
+
+ // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
+ Duration someTimeLater = getDurationDay(3);
+ clock.setDeltaFromReality(someTimeLater, DAY_IN_MS);
+
+ SubscriptionData aoSubscription = createSubscription("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+
+ // MOVE CLOCK A LITTLE BIT MORE -- STILL IN TRIAL
+ clock.addDeltaFromReality(someTimeLater);
+
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+
+ // Quick check
+ SubscriptionRepair bpRepair = getSubscriptionRepair(baseSubscription.getId(), bundleRepair);
+ assertEquals(bpRepair.getExistingEvents().size(), 2);
+
+ SubscriptionRepair aoRepair = getSubscriptionRepair(aoSubscription.getId(), bundleRepair);
+ assertEquals(aoRepair.getExistingEvents().size(), 2);
+
+
+ List<DeletedEvent> des = new LinkedList<SubscriptionRepair.DeletedEvent>();
+ des.add(createDeletedEvent(aoRepair.getExistingEvents().get(0).getEventId()));
+ des.add(createDeletedEvent(aoRepair.getExistingEvents().get(1).getEventId()));
+
+ DateTime aoRecreateDate = aoSubscription.getStartDate().minusDays(5);
+ PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.DISCOUNT);
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CREATE, aoRecreateDate, spec);
+
+ SubscriptionRepair saoRepair = createSubscriptionReapir(aoSubscription.getId(), des, Collections.singletonList(ne));
+
+ BundleRepair bRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(saoRepair));
+
+ boolean dryRun = true;
+ repairApi.repairBundle(bRepair, dryRun, context);
+ }
+ }, ErrorCode.ENT_REPAIR_AO_CREATE_BEFORE_BP_START);
+ }
+
+ @Test(groups={"fast"})
+ public void testENT_REPAIR_NEW_EVENT_BEFORE_LAST_AO_REMAINING() throws Exception {
+ test.withException(new TestWithExceptionCallback() {
+ @Override
+ public void doTest() throws EntitlementRepairException, EntitlementUserApiException {
+
+
+ // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
+ Duration someTimeLater = getDurationDay(3);
+ clock.setDeltaFromReality(someTimeLater, DAY_IN_MS);
+
+ SubscriptionData aoSubscription = createSubscription("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+
+ // MOVE CLOCK A LITTLE BIT MORE -- STILL IN TRIAL
+ clock.addDeltaFromReality(someTimeLater);
+
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+
+ // Quick check
+ SubscriptionRepair bpRepair = getSubscriptionRepair(baseSubscription.getId(), bundleRepair);
+ assertEquals(bpRepair.getExistingEvents().size(), 2);
+
+ SubscriptionRepair aoRepair = getSubscriptionRepair(aoSubscription.getId(), bundleRepair);
+ assertEquals(aoRepair.getExistingEvents().size(), 2);
+
+
+ List<DeletedEvent> des = new LinkedList<SubscriptionRepair.DeletedEvent>();
+ //des.add(createDeletedEvent(aoRepair.getExistingEvents().get(1).getEventId()));
+ DateTime aoCancelDate = aoSubscription.getStartDate().plusDays(10);
+
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CANCEL, aoCancelDate, null);
+
+ SubscriptionRepair saoRepair = createSubscriptionReapir(aoSubscription.getId(), des, Collections.singletonList(ne));
+
+ bundleRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(saoRepair));
+
+ boolean dryRun = true;
+ repairApi.repairBundle(bundleRepair, dryRun, context);
+ }
+ }, ErrorCode.ENT_REPAIR_NEW_EVENT_BEFORE_LAST_AO_REMAINING);
+ }
+
+
+ @Test(groups={"fast"})
+ public void testENT_REPAIR_BP_RECREATE_MISSING_AO() throws Exception {
+ test.withException(new TestWithExceptionCallback() {
+ @Override
+ public void doTest() throws EntitlementRepairException, EntitlementUserApiException {
+
+ //testListener.pushExpectedEvent(NextEvent.PHASE);
+
+ clock.setDeltaFromReality(getDurationDay(5), 0);
+ //assertTrue(testListener.isCompleted(5000));
+
+ SubscriptionData aoSubscription = createSubscription("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+
+ DateTime newCreateTime = baseSubscription.getStartDate().plusDays(3);
+
+ PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.TRIAL);
+
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CREATE, newCreateTime, spec);
+ List<DeletedEvent> des = new LinkedList<SubscriptionRepair.DeletedEvent>();
+ des.add(createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(0).getEventId()));
+ des.add(createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(1).getEventId()));
+
+ SubscriptionRepair sRepair = createSubscriptionReapir(baseSubscription.getId(), des, Collections.singletonList(ne));
+
+ // FIRST ISSUE DRY RUN
+ BundleRepair bRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(sRepair));
+
+ boolean dryRun = true;
+ repairApi.repairBundle(bRepair, dryRun, context);
+ }
+ }, ErrorCode.ENT_REPAIR_BP_RECREATE_MISSING_AO);
+ }
+
+ //
+ // CAN'T seem to trigger such case easily, other errors trigger before...
+ //
+ @Test(groups={"fast"}, enabled=false)
+ public void testENT_REPAIR_BP_RECREATE_MISSING_AO_CREATE() throws Exception {
+ test.withException(new TestWithExceptionCallback() {
+ @Override
+ public void doTest() throws EntitlementRepairException, EntitlementUserApiException {
+ /*
+ //testListener.pushExpectedEvent(NextEvent.PHASE);
+
+ clock.setDeltaFromReality(getDurationDay(5), 0);
+ //assertTrue(testListener.isCompleted(5000));
+
+ SubscriptionData aoSubscription = createSubscription("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+
+ DateTime newCreateTime = baseSubscription.getStartDate().plusDays(3);
+
+ PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.TRIAL);
+
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CREATE, newCreateTime, spec);
+ List<DeletedEvent> des = new LinkedList<SubscriptionRepair.DeletedEvent>();
+ des.add(createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(0).getEventId()));
+ des.add(createDeletedEvent(bundleRepair.getSubscriptions().get(0).getExistingEvents().get(1).getEventId()));
+
+ SubscriptionRepair bpRepair = createSubscriptionReapir(baseSubscription.getId(), des, Collections.singletonList(ne));
+
+ ne = createNewEvent(SubscriptionTransitionType.CANCEL, clock.getUTCNow().minusDays(1), null);
+ SubscriptionRepair aoRepair = createSubscriptionReapir(aoSubscription.getId(), Collections.<SubscriptionRepair.DeletedEvent>emptyList(), Collections.singletonList(ne));
+
+
+ List<SubscriptionRepair> allRepairs = new LinkedList<SubscriptionRepair>();
+ allRepairs.add(bpRepair);
+ allRepairs.add(aoRepair);
+ bundleRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), allRepairs);
+ // FIRST ISSUE DRY RUN
+ BundleRepair bRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), allRepairs);
+
+ boolean dryRun = true;
+ repairApi.repairBundle(bRepair, dryRun, context);
+ */
+ }
+ }, ErrorCode.ENT_REPAIR_BP_RECREATE_MISSING_AO_CREATE);
+ }
+
+ @Test(groups={"fast"}, enabled=false)
+ public void testENT_REPAIR_MISSING_AO_DELETE_EVENT() throws Exception {
+ test.withException(new TestWithExceptionCallback() {
+ @Override
+ public void doTest() throws EntitlementRepairException, EntitlementUserApiException {
+
+
+ /*
+ // MOVE CLOCK -- JUST BEFORE END OF TRIAL
+ clock.setDeltaFromReality(getDurationDay(29), 0);
+
+ SubscriptionData aoSubscription = createSubscription("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+
+ // MOVE CLOCK -- RIGHT OUT OF TRIAL
+ testListener.pushExpectedEvent(NextEvent.PHASE);
+ clock.addDeltaFromReality(getDurationDay(5));
+ assertTrue(testListener.isCompleted(5000));
+
+ DateTime requestedChange = clock.getUTCNow();
+ baseSubscription.changePlan("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, requestedChange, context);
+
+ DateTime reapairTime = clock.getUTCNow().minusDays(1);
+
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+
+ SubscriptionRepair bpRepair = getSubscriptionRepair(baseSubscription.getId(), bundleRepair);
+ SubscriptionRepair aoRepair = getSubscriptionRepair(aoSubscription.getId(), bundleRepair);
+
+ List<DeletedEvent> bpdes = new LinkedList<SubscriptionRepair.DeletedEvent>();
+ bpdes.add(createDeletedEvent(bpRepair.getExistingEvents().get(2).getEventId()));
+ bpRepair = createSubscriptionReapir(baseSubscription.getId(), bpdes, Collections.<NewEvent>emptyList());
+
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CANCEL, reapairTime, null);
+ aoRepair = createSubscriptionReapir(aoSubscription.getId(), Collections.<SubscriptionRepair.DeletedEvent>emptyList(), Collections.singletonList(ne));
+
+ List<SubscriptionRepair> allRepairs = new LinkedList<SubscriptionRepair>();
+ allRepairs.add(bpRepair);
+ allRepairs.add(aoRepair);
+ bundleRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), allRepairs);
+
+ boolean dryRun = false;
+ repairApi.repairBundle(bundleRepair, dryRun, context);
+ */
+ }
+ }, ErrorCode.ENT_REPAIR_MISSING_AO_DELETE_EVENT);
+ }
+
+}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
index 3783849..74354a7 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
@@ -57,6 +57,7 @@ import com.ning.billing.dbi.MysqlTestingHelper;
import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
import com.ning.billing.entitlement.api.billing.ChargeThruApi;
import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
+import com.ning.billing.entitlement.api.repair.EntitlementRepairApi;
import com.ning.billing.entitlement.api.user.EntitlementUserApi;
import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
@@ -70,7 +71,6 @@ import com.ning.billing.entitlement.events.EntitlementEvent;
import com.ning.billing.entitlement.events.phase.PhaseEvent;
import com.ning.billing.entitlement.events.user.ApiEvent;
import com.ning.billing.entitlement.events.user.ApiEventType;
-import com.ning.billing.util.bus.Bus.EventBusException;
import com.ning.billing.util.bus.BusService;
import com.ning.billing.util.bus.DefaultBusService;
import com.ning.billing.util.callcontext.CallContext;
@@ -89,6 +89,7 @@ public abstract class TestApiBase {
protected ChargeThruApi billingApi;
protected EntitlementMigrationApi migrationApi;
+ protected EntitlementRepairApi repairApi;
protected CatalogService catalogService;
protected EntitlementConfig config;
@@ -139,6 +140,7 @@ public abstract class TestApiBase {
entitlementApi = g.getInstance(EntitlementUserApi.class);
billingApi = g.getInstance(ChargeThruApi.class);
migrationApi = g.getInstance(EntitlementMigrationApi.class);
+ repairApi = g.getInstance(EntitlementRepairApi.class);
catalogService = g.getInstance(CatalogService.class);
busService = g.getInstance(BusService.class);
config = g.getInstance(EntitlementConfig.class);
@@ -146,11 +148,10 @@ public abstract class TestApiBase {
clock = (ClockMock) g.getInstance(Clock.class);
helper = (isSqlTest(dao)) ? g.getInstance(MysqlTestingHelper.class) : null;
-
((DefaultCatalogService) catalogService).loadCatalog();
((DefaultBusService) busService).startBus();
((Engine) entitlementService).initialize();
- init();
+ init(g);
}
private static boolean isSqlTest(EntitlementDao theDao) {
@@ -167,16 +168,13 @@ public abstract class TestApiBase {
}
}
- private void init() throws Exception {
+ private void init(Injector g) throws Exception {
setupMySQL();
-
accountData = getAccountData();
assertNotNull(accountData);
-
catalog = catalogService.getFullCatalog();
assertNotNull(catalog);
-
testListener = new ApiTestListener(busService.getBus());
}
@@ -207,18 +205,24 @@ public abstract class TestApiBase {
log.warn("DONE WITH TEST\n");
}
- protected SubscriptionData createSubscription(final String productName, final BillingPeriod term, final String planSet) throws EntitlementUserApiException {
- return createSubscriptionWithBundle(bundle.getId(), productName, term, planSet);
+ protected SubscriptionData createSubscription(final String productName, final BillingPeriod term, final String planSet, final DateTime requestedDate)
+ throws EntitlementUserApiException {
+ return createSubscriptionWithBundle(bundle.getId(), productName, term, planSet, requestedDate);
+ }
+ protected SubscriptionData createSubscription(final String productName, final BillingPeriod term, final String planSet)
+ throws EntitlementUserApiException {
+ return createSubscriptionWithBundle(bundle.getId(), productName, term, planSet, null);
}
- protected SubscriptionData createSubscriptionWithBundle(final UUID bundleId, final String productName, final BillingPeriod term, final String planSet) throws EntitlementUserApiException {
- testListener.pushExpectedEvent(NextEvent.CREATE);
+ protected SubscriptionData createSubscriptionWithBundle(final UUID bundleId, final String productName, final BillingPeriod term, final String planSet, final DateTime requestedDate)
+ throws EntitlementUserApiException {
+ testListener.pushNextApiExpectedEvent(NextEvent.CREATE);
SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundleId,
new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSet, null),
- clock.getUTCNow(), context);
+ requestedDate == null ? clock.getUTCNow() : requestedDate, context);
assertNotNull(subscription);
- assertTrue(testListener.isCompleted(5000));
+ assertTrue(testListener.isApiCompleted(5000));
return subscription;
}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
index 7c2fd3f..e9bfe5c 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
@@ -75,8 +75,8 @@ public class TestUserApiAddOn extends TestApiBase {
aoSubscription.cancel(now, false, context);
testListener.reset();
- testListener.pushExpectedEvent(NextEvent.CANCEL);
- assertTrue(testListener.isCompleted(5000));
+ testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
+ assertTrue(testListener.isApiCompleted(5000));
assertEquals(aoSubscription.getState(), SubscriptionState.CANCELLED);
@@ -103,13 +103,13 @@ public class TestUserApiAddOn extends TestApiBase {
SubscriptionData aoSubscription = createSubscription(aoProduct, aoTerm, aoPriceList);
testListener.reset();
- testListener.pushExpectedEvent(NextEvent.PHASE);
- testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
// MOVE CLOCK AFTER TRIAL + AO DISCOUNT
Duration twoMonths = getDurationMonth(2);
clock.setDeltaFromReality(twoMonths, DAY_IN_MS);
- assertTrue(testListener.isCompleted(5000));
+ assertTrue(testListener.isApiCompleted(5000));
// SET CTD TO CANCEL IN FUTURE
DateTime now = clock.getUTCNow();
@@ -129,10 +129,10 @@ public class TestUserApiAddOn extends TestApiBase {
// MOVE AFTER CANCELLATION
testListener.reset();
- testListener.pushExpectedEvent(NextEvent.CANCEL);
- testListener.pushExpectedEvent(NextEvent.CANCEL);
+ testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
+ testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
clock.addDeltaFromReality(ctd);
- assertTrue(testListener.isCompleted(5000));
+ assertTrue(testListener.isApiCompleted(5000));
// REFETCH AO SUBSCRIPTION AND CHECK THIS IS CANCELLED
aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
@@ -162,13 +162,13 @@ public class TestUserApiAddOn extends TestApiBase {
SubscriptionData aoSubscription = createSubscription(aoProduct, aoTerm, aoPriceList);
testListener.reset();
- testListener.pushExpectedEvent(NextEvent.PHASE);
- testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
// MOVE CLOCK AFTER TRIAL + AO DISCOUNT
Duration twoMonths = getDurationMonth(2);
clock.setDeltaFromReality(twoMonths, DAY_IN_MS);
- assertTrue(testListener.isCompleted(5000));
+ assertTrue(testListener.isApiCompleted(5000));
// SET CTD TO CHANGE IN FUTURE
DateTime now = clock.getUTCNow();
@@ -183,10 +183,10 @@ public class TestUserApiAddOn extends TestApiBase {
String newBasePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
testListener.reset();
- testListener.pushExpectedEvent(NextEvent.CHANGE);
- testListener.pushExpectedEvent(NextEvent.CANCEL);
+ testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
baseSubscription.changePlan(newBaseProduct, newBaseTerm, newBasePriceList, now, context);
- assertTrue(testListener.isCompleted(5000));
+ assertTrue(testListener.isApiCompleted(5000));
// REFETCH AO SUBSCRIPTION AND CHECK THIS CANCELLED
aoSubscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(aoSubscription.getId());
@@ -215,13 +215,13 @@ public class TestUserApiAddOn extends TestApiBase {
SubscriptionData aoSubscription = createSubscription(aoProduct, aoTerm, aoPriceList);
testListener.reset();
- testListener.pushExpectedEvent(NextEvent.PHASE);
- testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
// MOVE CLOCK AFTER TRIAL + AO DISCOUNT
Duration twoMonths = getDurationMonth(2);
clock.setDeltaFromReality(twoMonths, DAY_IN_MS);
- assertTrue(testListener.isCompleted(5000));
+ assertTrue(testListener.isApiCompleted(5000));
// SET CTD TO CANCEL IN FUTURE
DateTime now = clock.getUTCNow();
@@ -245,10 +245,10 @@ public class TestUserApiAddOn extends TestApiBase {
// MOVE AFTER CHANGE
testListener.reset();
- testListener.pushExpectedEvent(NextEvent.CHANGE);
- testListener.pushExpectedEvent(NextEvent.CANCEL);
+ testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
clock.addDeltaFromReality(ctd);
- assertTrue(testListener.isCompleted(5000));
+ assertTrue(testListener.isApiCompleted(5000));
// REFETCH AO SUBSCRIPTION AND CHECK THIS CANCELLED
@@ -352,8 +352,8 @@ public class TestUserApiAddOn extends TestApiBase {
// ADD TWO PHASE EVENTS (BP + AO)
testListener.reset();
- testListener.pushExpectedEvent(NextEvent.PHASE);
- testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
// MOVE THROUGH TIME TO GO INTO EVERGREEN
@@ -384,7 +384,8 @@ public class TestUserApiAddOn extends TestApiBase {
};
clock.addDeltaFromReality(someTimeLater);
- assertTrue(testListener.isCompleted(5000));
+ clock.addDeltaFromReality(getDurationDay(1));
+ assertTrue(testListener.isApiCompleted(5000));
// CHECK EVERYTHING AGAIN
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancel.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancel.java
index 5a7d33e..09046f1 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancel.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancel.java
@@ -59,12 +59,12 @@ public abstract class TestUserApiCancel extends TestApiBase {
clock.setDeltaFromReality(moveALittleInTime, 0);
DateTime future = clock.getUTCNow();
- testListener.pushExpectedEvent(NextEvent.CANCEL);
+ testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
// CANCEL in trial period to get IMM policy
subscription.cancel(clock.getUTCNow(), false, context);
currentPhase = subscription.getCurrentPhase();
- testListener.isCompleted(1000);
+ testListener.isApiCompleted(1000);
assertNull(currentPhase);
checkNextPhaseChange(subscription, 0, null);
@@ -94,9 +94,9 @@ public abstract class TestUserApiCancel extends TestApiBase {
checkNextPhaseChange(subscription, 1, expectedPhaseTrialChange);
// MOVE TO NEXT PHASE
- testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
clock.setDeltaFromReality(trialPhase.getDuration(), DAY_IN_MS);
- assertTrue(testListener.isCompleted(2000));
+ assertTrue(testListener.isApiCompleted(2000));
trialPhase = subscription.getCurrentPhase();
assertEquals(trialPhase.getPhaseType(), PhaseType.EVERGREEN);
@@ -106,16 +106,16 @@ public abstract class TestUserApiCancel extends TestApiBase {
billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate, context);
subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
- testListener.pushExpectedEvent(NextEvent.CANCEL);
+ testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
// CANCEL
subscription.cancel(clock.getUTCNow(), false, context);
- assertFalse(testListener.isCompleted(2000));
+ assertFalse(testListener.isApiCompleted(2000));
// MOVE TO EOT + RECHECK
clock.addDeltaFromReality(ctd);
DateTime future = clock.getUTCNow();
- assertTrue(testListener.isCompleted(2000));
+ assertTrue(testListener.isApiCompleted(2000));
PlanPhase currentPhase = subscription.getCurrentPhase();
assertNull(currentPhase);
@@ -147,17 +147,17 @@ public abstract class TestUserApiCancel extends TestApiBase {
checkNextPhaseChange(subscription, 1, expectedPhaseTrialChange);
// MOVE TO NEXT PHASE
- testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
clock.setDeltaFromReality(trialPhase.getDuration(), DAY_IN_MS);
- assertTrue(testListener.isCompleted(2000));
+ assertTrue(testListener.isApiCompleted(2000));
trialPhase = subscription.getCurrentPhase();
assertEquals(trialPhase.getPhaseType(), PhaseType.EVERGREEN);
- testListener.pushExpectedEvent(NextEvent.CANCEL);
+ testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
// CANCEL
subscription.cancel(clock.getUTCNow(), false, context);
- assertTrue(testListener.isCompleted(2000));
+ assertTrue(testListener.isApiCompleted(2000));
PlanPhase currentPhase = subscription.getCurrentPhase();
assertNull(currentPhase);
@@ -191,9 +191,9 @@ public abstract class TestUserApiCancel extends TestApiBase {
checkNextPhaseChange(subscription, 1, expectedPhaseTrialChange);
// MOVE TO NEXT PHASE
- testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
clock.setDeltaFromReality(trialPhase.getDuration(), DAY_IN_MS);
- assertTrue(testListener.isCompleted(2000));
+ assertTrue(testListener.isApiCompleted(2000));
PlanPhase currentPhase = subscription.getCurrentPhase();
assertEquals(currentPhase.getPhaseType(), PhaseType.EVERGREEN);
@@ -203,18 +203,18 @@ public abstract class TestUserApiCancel extends TestApiBase {
billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate, context);
subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
- testListener.pushExpectedEvent(NextEvent.CANCEL);
+ testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
// CANCEL
subscription.cancel(clock.getUTCNow(), false, context);
- assertFalse(testListener.isCompleted(2000));
+ assertFalse(testListener.isApiCompleted(2000));
subscription.uncancel(context);
// MOVE TO EOT + RECHECK
clock.addDeltaFromReality(ctd);
DateTime future = clock.getUTCNow();
- assertFalse(testListener.isCompleted(2000));
+ assertFalse(testListener.isApiCompleted(2000));
Plan currentPlan = subscription.getCurrentPlan();
assertEquals(currentPlan.getProduct().getName(), prod);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
index 98e495d..f43e843 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
@@ -78,17 +78,17 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
// MOVE TO NEXT PHASE
PlanPhase currentPhase = subscription.getCurrentPhase();
- testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
clock.setDeltaFromReality(currentPhase.getDuration(), DAY_IN_MS);
DateTime futureNow = clock.getUTCNow();
DateTime nextExpectedPhaseChange = DefaultClock.addDuration(subscription.getStartDate(), currentPhase.getDuration());
assertTrue(futureNow.isAfter(nextExpectedPhaseChange));
- assertTrue(testListener.isCompleted(3000));
+ assertTrue(testListener.isApiCompleted(3000));
// CHANGE PLAN
- testListener.pushExpectedEvent(NextEvent.CHANGE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
subscription.changePlan(toProd, toTerm, toPlanSet, clock.getUTCNow(), context);
- assertTrue(testListener.isCompleted(2000));
+ assertTrue(testListener.isApiCompleted(2000));
// CHECK CHANGE PLAN
currentPhase = subscription.getCurrentPhase();
@@ -118,9 +118,9 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
// MOVE TO NEXT PHASE
- testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
clock.setDeltaFromReality(trialPhase.getDuration(), DAY_IN_MS);
- assertTrue(testListener.isCompleted(2000));
+ assertTrue(testListener.isApiCompleted(2000));
PlanPhase currentPhase = subscription.getCurrentPhase();
assertEquals(currentPhase.getPhaseType(), PhaseType.DISCOUNT);
@@ -131,10 +131,10 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate, context);
// RE READ SUBSCRIPTION + CHANGE PLAN
- testListener.pushExpectedEvent(NextEvent.CHANGE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
subscription.changePlan(toProd, toTerm, toPlanSet, clock.getUTCNow(), context);
- assertFalse(testListener.isCompleted(2000));
+ assertFalse(testListener.isApiCompleted(2000));
testListener.reset();
// CHECK CHANGE PLAN
@@ -151,9 +151,9 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
// MOVE TO EOT
- testListener.pushExpectedEvent(NextEvent.CHANGE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
clock.addDeltaFromReality(ctd);
- assertTrue(testListener.isCompleted(5000));
+ assertTrue(testListener.isApiCompleted(5000));
subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
currentPhase = subscription.getCurrentPhase();
@@ -179,7 +179,7 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
SubscriptionData subscription = createSubscription(fromProd, fromTerm, fromPlanSet);
- testListener.pushExpectedEvent(NextEvent.CHANGE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
Duration moveALittleInTime = getDurationDay(3);
clock.setDeltaFromReality(moveALittleInTime, 0);
@@ -188,14 +188,14 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
subscription.changePlan(toProd, toTerm, toPlanSet, clock.getUTCNow(), context);
checkChangePlan(subscription, toProd, ProductCategory.BASE, toTerm, PhaseType.TRIAL);
- assertTrue(testListener.isCompleted(2000));
+ assertTrue(testListener.isApiCompleted(2000));
PlanPhase currentPhase = subscription.getCurrentPhase();
DateTime nextExpectedPhaseChange = DefaultClock.addDuration(subscription.getStartDate(), currentPhase.getDuration());
checkNextPhaseChange(subscription, 1, nextExpectedPhaseChange);
// NEXT PHASE
- testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
clock.addDeltaFromReality(currentPhase.getDuration());
DateTime futureNow = clock.getUTCNow();
@@ -208,7 +208,7 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
*/
assertTrue(futureNow.isAfter(nextExpectedPhaseChange));
- assertTrue(testListener.isCompleted(3000));
+ assertTrue(testListener.isApiCompleted(3000));
} catch (EntitlementUserApiException e) {
Assert.fail(e.getMessage());
@@ -235,11 +235,11 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
assertEquals(trialPhase.getPhaseType(), PhaseType.TRIAL);
// MOVE TO NEXT PHASE
- testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
currentTime = clock.getUTCNow();
clock.setDeltaFromReality(trialPhase.getDuration(), DAY_IN_MS);
currentTime = clock.getUTCNow();
- assertTrue(testListener.isCompleted(2000));
+ assertTrue(testListener.isApiCompleted(2000));
// SET CTD
Duration ctd = getDurationMonth(1);
@@ -255,18 +255,18 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
// CHANGE PLAN
currentTime = clock.getUTCNow();
- testListener.pushExpectedEvent(NextEvent.CHANGE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
subscription.changePlan(toProd, toTerm, toPlanSet, clock.getUTCNow(), context);
checkChangePlan(subscription, fromProd, ProductCategory.BASE, fromTerm, PhaseType.EVERGREEN);
// CHECK CHANGE DID NOT KICK IN YET
- assertFalse(testListener.isCompleted(2000));
+ assertFalse(testListener.isApiCompleted(2000));
// MOVE TO AFTER CTD
clock.addDeltaFromReality(ctd);
currentTime = clock.getUTCNow();
- assertTrue(testListener.isCompleted(2000));
+ assertTrue(testListener.isApiCompleted(2000));
// CHECK CORRECT PRODUCT, PHASE, PLAN SET
String currentProduct = subscription.getCurrentPlan().getProduct().getName();
@@ -276,13 +276,13 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
assertNotNull(currentPhase);
assertEquals(currentPhase.getPhaseType(), PhaseType.DISCOUNT);
- testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
// MOVE TIME ABOUT ONE MONTH BEFORE NEXT EXPECTED PHASE CHANGE
clock.addDeltaFromReality(getDurationMonth(11));
currentTime = clock.getUTCNow();
- assertFalse(testListener.isCompleted(2000));
+ assertFalse(testListener.isApiCompleted(2000));
DateTime nextExpectedPhaseChange = DefaultClock.addDuration(newChargedThroughDate, currentPhase.getDuration());
checkNextPhaseChange(subscription, 1, nextExpectedPhaseChange);
@@ -290,7 +290,7 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
// MOVE TIME RIGHT AFTER NEXT EXPECTED PHASE CHANGE
clock.addDeltaFromReality(getDurationMonth(1));
currentTime = clock.getUTCNow();
- assertTrue(testListener.isCompleted(2000));
+ assertTrue(testListener.isApiCompleted(2000));
} catch (EntitlementUserApiException e) {
Assert.fail(e.getMessage());
@@ -305,9 +305,9 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
assertEquals(trialPhase.getPhaseType(), PhaseType.TRIAL);
// MOVE TO NEXT PHASE
- testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
clock.setDeltaFromReality(trialPhase.getDuration(), DAY_IN_MS);
- assertTrue(testListener.isCompleted(2000));
+ assertTrue(testListener.isApiCompleted(2000));
// SET CTD
List<Duration> durationList = new ArrayList<Duration>();
@@ -320,14 +320,14 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
// CHANGE EOT
- testListener.pushExpectedEvent(NextEvent.CHANGE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
subscription.changePlan("Pistol", BillingPeriod.MONTHLY, "gunclubDiscount", clock.getUTCNow(), context);
- assertFalse(testListener.isCompleted(2000));
+ assertFalse(testListener.isApiCompleted(2000));
// CHANGE
- testListener.pushExpectedEvent(NextEvent.CHANGE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
subscription.changePlan("Assault-Rifle", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow(), context);
- assertFalse(testListener.isCompleted(2000));
+ assertFalse(testListener.isApiCompleted(2000));
Plan currentPlan = subscription.getCurrentPlan();
assertNotNull(currentPlan);
@@ -352,9 +352,9 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
PlanPhase trialPhase = subscription.getCurrentPhase();
assertEquals(trialPhase.getPhaseType(), PhaseType.TRIAL);
- testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
clock.setDeltaFromReality(trialPhase.getDuration(), DAY_IN_MS);
- assertTrue(testListener.isCompleted(2000));
+ assertTrue(testListener.isApiCompleted(2000));
// SET CTD
List<Duration> durationList = new ArrayList<Duration>();
@@ -366,15 +366,15 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
// CHANGE EOT
- testListener.pushExpectedEvent(NextEvent.CHANGE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
subscription.changePlan("Shotgun", BillingPeriod.MONTHLY, "gunclubDiscount", clock.getUTCNow(), context);
- assertFalse(testListener.isCompleted(2000));
+ assertFalse(testListener.isApiCompleted(2000));
testListener.reset();
// CHANGE EOT
- testListener.pushExpectedEvent(NextEvent.CHANGE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
subscription.changePlan("Pistol", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow(), context);
- assertFalse(testListener.isCompleted(2000));
+ assertFalse(testListener.isApiCompleted(2000));
testListener.reset();
// CHECK NO CHANGE OCCURED YET
@@ -389,9 +389,9 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
assertEquals(currentPhase.getPhaseType(), PhaseType.DISCOUNT);
// ACTIVATE CHNAGE BY MOVING AFTER CTD
- testListener.pushExpectedEvent(NextEvent.CHANGE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
clock.addDeltaFromReality(ctd);
- assertTrue(testListener.isCompleted(3000));
+ assertTrue(testListener.isApiCompleted(3000));
currentPlan = subscription.getCurrentPlan();
assertNotNull(currentPlan);
@@ -406,9 +406,9 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
// MOVE TO NEXT PHASE
- testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
clock.addDeltaFromReality(currentPhase.getDuration());
- assertTrue(testListener.isCompleted(3000));
+ assertTrue(testListener.isApiCompleted(3000));
subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
currentPlan = subscription.getCurrentPlan();
@@ -440,9 +440,9 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
// CHANGE IMMEDIATE TO A 3 PHASES PLAN
testListener.reset();
- testListener.pushExpectedEvent(NextEvent.CHANGE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
subscription.changePlan("Assault-Rifle", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow(), context);
- assertTrue(testListener.isCompleted(3000));
+ assertTrue(testListener.isApiCompleted(3000));
testListener.reset();
// CHECK EVERYTHING LOOKS CORRECT
@@ -456,9 +456,9 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
assertEquals(trialPhase.getPhaseType(), PhaseType.TRIAL);
// MOVE AFTER TRIAL PERIOD -> DISCOUNT
- testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
clock.addDeltaFromReality(trialPhase.getDuration());
- assertTrue(testListener.isCompleted(3000));
+ assertTrue(testListener.isApiCompleted(3000));
trialPhase = subscription.getCurrentPhase();
assertEquals(trialPhase.getPhaseType(), PhaseType.DISCOUNT);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java
index 01ced07..ef4533d 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java
@@ -56,8 +56,8 @@ public abstract class TestUserApiCreate extends TestApiBase {
String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
- testListener.pushExpectedEvent(NextEvent.PHASE);
- testListener.pushExpectedEvent(NextEvent.CREATE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CREATE);
SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
@@ -69,7 +69,7 @@ public abstract class TestUserApiCreate extends TestApiBase {
assertEquals(subscription.getBundleId(), bundle.getId());
assertEquals(subscription.getStartDate(), requestedDate);
- assertTrue(testListener.isCompleted(5000));
+ assertTrue(testListener.isApiCompleted(5000));
} catch (EntitlementUserApiException e) {
log.error("Unexpected exception",e);
@@ -89,7 +89,7 @@ public abstract class TestUserApiCreate extends TestApiBase {
BillingPeriod term = BillingPeriod.MONTHLY;
String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
- testListener.pushExpectedEvent(NextEvent.CREATE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CREATE);
SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
getProductSpecifier(productName, planSetName, term, PhaseType.EVERGREEN), clock.getUTCNow(), context);
@@ -127,7 +127,7 @@ public abstract class TestUserApiCreate extends TestApiBase {
BillingPeriod term = BillingPeriod.MONTHLY;
String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
- testListener.pushExpectedEvent(NextEvent.CREATE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CREATE);
SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
getProductSpecifier(productName, planSetName, term, null),
@@ -149,7 +149,7 @@ public abstract class TestUserApiCreate extends TestApiBase {
PlanPhase currentPhase = subscription.getCurrentPhase();
assertNotNull(currentPhase);
assertEquals(currentPhase.getPhaseType(), PhaseType.TRIAL);
- assertTrue(testListener.isCompleted(5000));
+ assertTrue(testListener.isApiCompleted(5000));
List<EntitlementEvent> events = dao.getPendingEventsForSubscription(subscription.getId());
assertNotNull(events);
@@ -160,14 +160,14 @@ public abstract class TestUserApiCreate extends TestApiBase {
DateTime nextExpectedPhaseChange = DefaultClock.addDuration(subscription.getStartDate(), currentPhase.getDuration());
assertEquals(nextPhaseChange, nextExpectedPhaseChange);
- testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
clock.setDeltaFromReality(currentPhase.getDuration(), DAY_IN_MS);
DateTime futureNow = clock.getUTCNow();
assertTrue(futureNow.isAfter(nextPhaseChange));
- assertTrue(testListener.isCompleted(5000));
+ assertTrue(testListener.isApiCompleted(5000));
} catch (EntitlementUserApiException e) {
Assert.fail(e.getMessage());
@@ -184,7 +184,7 @@ public abstract class TestUserApiCreate extends TestApiBase {
BillingPeriod term = BillingPeriod.ANNUAL;
String planSetName = "gunclubDiscount";
- testListener.pushExpectedEvent(NextEvent.CREATE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CREATE);
// CREATE SUBSCRIPTION
SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
@@ -194,20 +194,20 @@ public abstract class TestUserApiCreate extends TestApiBase {
PlanPhase currentPhase = subscription.getCurrentPhase();
assertNotNull(currentPhase);
assertEquals(currentPhase.getPhaseType(), PhaseType.TRIAL);
- assertTrue(testListener.isCompleted(5000));
+ assertTrue(testListener.isApiCompleted(5000));
// MOVE TO DISCOUNT PHASE
- testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
clock.setDeltaFromReality(currentPhase.getDuration(), DAY_IN_MS);
currentPhase = subscription.getCurrentPhase();
assertNotNull(currentPhase);
assertEquals(currentPhase.getPhaseType(), PhaseType.DISCOUNT);
- assertTrue(testListener.isCompleted(2000));
+ assertTrue(testListener.isApiCompleted(2000));
// MOVE TO EVERGREEN PHASE + RE-READ SUBSCRIPTION
- testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
clock.addDeltaFromReality(currentPhase.getDuration());
- assertTrue(testListener.isCompleted(2000));
+ assertTrue(testListener.isApiCompleted(2000));
subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
currentPhase = subscription.getCurrentPhase();
@@ -229,7 +229,7 @@ public abstract class TestUserApiCreate extends TestApiBase {
BillingPeriod term = BillingPeriod.ANNUAL;
String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
- testListener.pushExpectedEvent(NextEvent.CREATE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CREATE);
SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
getProductSpecifier(productName, planSetName, term, null), clock.getUTCNow(), context);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiDemos.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiDemos.java
index b21764d..502d133 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiDemos.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiDemos.java
@@ -80,16 +80,16 @@ public class TestUserApiDemos extends TestApiBase {
displayState(subscription.getId(), "STEP 1. CREATED SUBSCRIPTION");
/* STEP 2. CHANGE PLAN WHILE IN TRIAL */
- testListener.pushExpectedEvent(NextEvent.CHANGE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
subscription.changePlan("Assault-Rifle", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow(), context);
- assertTrue(testListener.isCompleted(3000));
+ assertTrue(testListener.isApiCompleted(3000));
displayState(subscription.getId(), "STEP 2. CHANGED PLAN WHILE IN TRIAL");
/* STEP 3. MOVE TO DISCOUNT PHASE */
- testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
clock.setDeltaFromReality(trialPhase.getDuration(), DAY_IN_MS);
- assertTrue(testListener.isCompleted(3000));
+ assertTrue(testListener.isApiCompleted(3000));
displayState(subscription.getId(), "STEP 3. MOVE TO DISCOUNT PHASE");
@@ -103,25 +103,25 @@ public class TestUserApiDemos extends TestApiBase {
billingApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate, context);
subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
- testListener.pushExpectedEvent(NextEvent.CHANGE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
subscription.changePlan("Shotgun", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow(), context);
- assertFalse(testListener.isCompleted(2000));
+ assertFalse(testListener.isApiCompleted(2000));
testListener.reset();
displayState(subscription.getId(), "STEP 4. SET CTD AND CHANGE PLAN EOT (Shotgun)");
/* STEP 5. CHANGE AGAIN */
- testListener.pushExpectedEvent(NextEvent.CHANGE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
subscription.changePlan("Pistol", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow(), context);
- assertFalse(testListener.isCompleted(2000));
+ assertFalse(testListener.isApiCompleted(2000));
testListener.reset();
displayState(subscription.getId(), "STEP 5. CHANGE AGAIN EOT (Pistol)");
/* STEP 6. MOVE TO EOT AND CHECK CHANGE OCCURED */
- testListener.pushExpectedEvent(NextEvent.CHANGE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
clock.addDeltaFromReality(ctd);
- assertTrue(testListener.isCompleted(2000));
+ assertTrue(testListener.isApiCompleted(2000));
Plan currentPlan = subscription.getCurrentPlan();
assertNotNull(currentPlan);
@@ -136,9 +136,9 @@ public class TestUserApiDemos extends TestApiBase {
displayState(subscription.getId(), "STEP 6. MOVE TO EOT");
/* STEP 7. MOVE TO NEXT PHASE */
- testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
clock.addDeltaFromReality(currentPhase.getDuration());
- assertTrue(testListener.isCompleted(5000));
+ assertTrue(testListener.isApiCompleted(5000));
subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
currentPlan = subscription.getCurrentPlan();
@@ -154,7 +154,7 @@ public class TestUserApiDemos extends TestApiBase {
displayState(subscription.getId(), "STEP 7. MOVE TO NEXT PHASE");
/* STEP 8. CANCEL IMM (NO CTD) */
- testListener.pushExpectedEvent(NextEvent.CANCEL);
+ testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
subscription.cancel(clock.getUTCNow(), false, context);
displayState(subscription.getId(), "STEP 8. CANCELLATION");
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java
index 440b780..c3f6061 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java
@@ -108,7 +108,7 @@ public class TestUserApiError extends TestApiBase {
try {
UUID accountId = UUID.randomUUID();
SubscriptionBundle aoBundle = entitlementApi.createBundleForAccount(accountId, "myAOBundle", context);
- createSubscriptionWithBundle(aoBundle.getId(), "Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+ createSubscriptionWithBundle(aoBundle.getId(), "Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
tCreateSubscriptionInternal(aoBundle.getId(), "Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.ENT_CREATE_AO_NOT_AVAILABLE);
} catch (Exception e) {
e.printStackTrace();
@@ -121,7 +121,7 @@ public class TestUserApiError extends TestApiBase {
try {
UUID accountId = UUID.randomUUID();
SubscriptionBundle aoBundle = entitlementApi.createBundleForAccount(accountId, "myAOBundle", context);
- createSubscriptionWithBundle(aoBundle.getId(), "Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME);
+ createSubscriptionWithBundle(aoBundle.getId(), "Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
tCreateSubscriptionInternal(aoBundle.getId(), "Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.ENT_CREATE_AO_ALREADY_INCLUDED);
} catch (Exception e) {
e.printStackTrace();
@@ -153,7 +153,7 @@ public class TestUserApiError extends TestApiBase {
try {
Subscription subscription = createSubscription("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME);
- testListener.pushExpectedEvent(NextEvent.CANCEL);
+ testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
subscription.cancel(clock.getUTCNow(), false, context);
try {
subscription.changePlan("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, clock.getUTCNow(), context);
@@ -180,9 +180,9 @@ public class TestUserApiError extends TestApiBase {
// MOVE TO NEXT PHASE
PlanPhase currentPhase = subscription.getCurrentPhase();
- testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
clock.setDeltaFromReality(currentPhase.getDuration(), DAY_IN_MS);
- assertTrue(testListener.isCompleted(3000));
+ assertTrue(testListener.isApiCompleted(3000));
// SET CTD TO CANCEL IN FUTURE
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiRecreate.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiRecreate.java
index 91aaf02..b7506dd 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiRecreate.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiRecreate.java
@@ -65,8 +65,8 @@ public abstract class TestUserApiRecreate extends TestApiBase {
BillingPeriod term = BillingPeriod.MONTHLY;
String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
- testListener.pushExpectedEvent(NextEvent.PHASE);
- testListener.pushExpectedEvent(NextEvent.CREATE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CREATE);
SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
getProductSpecifier(productName, planSetName, term, null), requestedDate, context);
assertNotNull(subscription);
@@ -75,7 +75,7 @@ public abstract class TestUserApiRecreate extends TestApiBase {
assertEquals(subscription.getStartDate(), requestedDate);
assertEquals(productName, subscription.getCurrentPlan().getProduct().getName());
- assertTrue(testListener.isCompleted(5000));
+ assertTrue(testListener.isApiCompleted(5000));
// CREATE (AGAIN) WITH NEW PRODUCT
productName = "Pistol";
@@ -95,11 +95,11 @@ public abstract class TestUserApiRecreate extends TestApiBase {
}
// NOW CANCEL ADN THIS SHOULD WORK
- testListener.pushExpectedEvent(NextEvent.CANCEL);
+ testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
subscription.cancel(null, false, context);
- testListener.pushExpectedEvent(NextEvent.PHASE);
- testListener.pushExpectedEvent(NextEvent.RE_CREATE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.RE_CREATE);
// Avoid ordering issue for events at exact same date; this is actually a real good test, we
// we test it at Beatrix level. At this level that would work for sql tests but not for in memory.
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiScenarios.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiScenarios.java
index 08f1ac1..92ac1b0 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiScenarios.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiScenarios.java
@@ -54,14 +54,14 @@ public class TestUserApiScenarios extends TestApiBase {
PlanPhase trialPhase = subscription.getCurrentPhase();
assertEquals(trialPhase.getPhaseType(), PhaseType.TRIAL);
- testListener.pushExpectedEvent(NextEvent.CHANGE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
subscription.changePlan("Pistol", BillingPeriod.ANNUAL, "gunclubDiscount", clock.getUTCNow(), context);
- testListener.isCompleted(3000);
+ testListener.isApiCompleted(3000);
// MOVE TO NEXT PHASE
- testListener.pushExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
clock.setDeltaFromReality(trialPhase.getDuration(), DAY_IN_MS);
- assertTrue(testListener.isCompleted(2000));
+ assertTrue(testListener.isApiCompleted(2000));
// SET CTD
Duration ctd = getDurationMonth(1);
@@ -71,21 +71,21 @@ public class TestUserApiScenarios extends TestApiBase {
subscription = (SubscriptionData) entitlementApi.getSubscriptionFromId(subscription.getId());
// CANCEL EOT
- testListener.pushExpectedEvent(NextEvent.CANCEL);
+ testListener.pushNextApiExpectedEvent(NextEvent.CANCEL);
subscription.cancel(clock.getUTCNow(), false, context);
- assertFalse(testListener.isCompleted(2000));
+ assertFalse(testListener.isApiCompleted(2000));
testListener.reset();
// UNCANCEL
subscription.uncancel(context);
// CHANGE EOT
- testListener.pushExpectedEvent(NextEvent.CHANGE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CHANGE);
subscription.changePlan("Pistol", BillingPeriod.MONTHLY, "gunclubDiscount", clock.getUTCNow(), context);
- assertFalse(testListener.isCompleted(2000));
+ assertFalse(testListener.isApiCompleted(2000));
clock.addDeltaFromReality(ctd);
- assertTrue(testListener.isCompleted(3000));
+ assertTrue(testListener.isApiCompleted(3000));
} catch (EntitlementUserApiException e) {
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserCustomFieldsSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserCustomFieldsSql.java
index afc5591..90966d8 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserCustomFieldsSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserCustomFieldsSql.java
@@ -76,8 +76,8 @@ public class TestUserCustomFieldsSql extends TestApiBase {
BillingPeriod term = BillingPeriod.MONTHLY;
String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
- testListener.pushExpectedEvent(NextEvent.PHASE);
- testListener.pushExpectedEvent(NextEvent.CREATE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CREATE);
SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
getProductSpecifier(productName, planSetName, term, null), requestedDate, context);
assertNotNull(subscription);
@@ -132,8 +132,8 @@ public class TestUserCustomFieldsSql extends TestApiBase {
BillingPeriod term = BillingPeriod.MONTHLY;
String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
- testListener.pushExpectedEvent(NextEvent.PHASE);
- testListener.pushExpectedEvent(NextEvent.CREATE);
+ testListener.pushNextApiExpectedEvent(NextEvent.PHASE);
+ testListener.pushNextApiExpectedEvent(NextEvent.CREATE);
SubscriptionData subscription = (SubscriptionData) entitlementApi.createSubscription(bundle.getId(),
getProductSpecifier(productName, planSetName, term, null), requestedDate, context);
assertNotNull(subscription);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
index 5bb0fb5..f777204 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
@@ -21,6 +21,7 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.TreeSet;
import java.util.UUID;
@@ -35,15 +36,16 @@ import com.ning.billing.catalog.api.CatalogService;
import com.ning.billing.catalog.api.ProductCategory;
import com.ning.billing.catalog.api.TimeUnit;
import com.ning.billing.config.EntitlementConfig;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
import com.ning.billing.entitlement.api.migration.AccountMigrationData;
import com.ning.billing.entitlement.api.migration.AccountMigrationData.BundleMigrationData;
import com.ning.billing.entitlement.api.migration.AccountMigrationData.SubscriptionMigrationData;
+import com.ning.billing.entitlement.api.repair.SubscriptionDataRepair;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
import com.ning.billing.entitlement.api.user.SubscriptionData;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.entitlement.engine.core.Engine;
import com.ning.billing.entitlement.events.EntitlementEvent;
import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
@@ -263,7 +265,7 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
}
@Override
- public void updateSubscription(final SubscriptionData subscription, final CallContext context) {
+ public void updateChargedThroughDate(final SubscriptionData subscription, final CallContext context) {
boolean found = false;
Iterator<Subscription> it = subscriptions.iterator();
@@ -446,4 +448,15 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
public void saveCustomFields(SubscriptionData subscription, CallContext context) {
throw new NotImplementedException();
}
+
+ @Override
+ public Map<UUID, List<EntitlementEvent>> getEventsForBundle(UUID bundleId) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void repair(UUID accountId, UUID bundleId, List<SubscriptionDataRepair> inRepair,
+ CallContext context) {
+ }
}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java
index 8deaef6..3851eb2 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java
@@ -16,6 +16,9 @@
package com.ning.billing.entitlement.engine.dao;
+
+import com.ning.billing.util.bus.Bus;
+import com.ning.billing.util.customfield.dao.CustomFieldDao;
import org.skife.jdbi.v2.IDBI;
import org.skife.jdbi.v2.Transaction;
import org.skife.jdbi.v2.TransactionStatus;
@@ -24,10 +27,9 @@ import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
import com.google.inject.Inject;
-import com.ning.billing.catalog.api.CatalogService;
import com.ning.billing.entitlement.engine.addon.AddonUtils;
import com.ning.billing.util.clock.Clock;
-import com.ning.billing.util.customfield.dao.CustomFieldDao;
+
import com.ning.billing.util.notificationq.NotificationQueueService;
public class MockEntitlementDaoSql extends EntitlementSqlDao implements MockEntitlementDao {
@@ -36,9 +38,8 @@ public class MockEntitlementDaoSql extends EntitlementSqlDao implements MockEnti
@Inject
public MockEntitlementDaoSql(IDBI dbi, Clock clock, AddonUtils addonUtils, NotificationQueueService notificationQueueService,
- CustomFieldDao customFieldDao,
- final CatalogService catalogService) {
- super(dbi, clock, addonUtils, notificationQueueService, customFieldDao, catalogService);
+ CustomFieldDao customFieldDao, final Bus eventBus) {
+ super(dbi, clock, addonUtils, notificationQueueService, customFieldDao, eventBus);
this.resetDao = dbi.onDemand(ResetSqlDao.class);
}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleMemory.java
index 8b78c2d..20b91e1 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleMemory.java
@@ -17,8 +17,11 @@
package com.ning.billing.entitlement.glue;
+import com.google.inject.name.Names;
+import com.ning.billing.entitlement.api.repair.RepairEntitlementLifecycleDao;
import com.ning.billing.entitlement.engine.dao.EntitlementDao;
import com.ning.billing.entitlement.engine.dao.MockEntitlementDaoMemory;
+import com.ning.billing.entitlement.engine.dao.RepairEntitlementDao;
import com.ning.billing.util.notificationq.MockNotificationQueueService;
import com.ning.billing.util.notificationq.NotificationQueueService;
@@ -27,6 +30,9 @@ public class MockEngineModuleMemory extends MockEngineModule {
@Override
protected void installEntitlementDao() {
bind(EntitlementDao.class).to(MockEntitlementDaoMemory.class).asEagerSingleton();
+ bind(EntitlementDao.class).annotatedWith(Names.named(REPAIR_NAMED)).to(RepairEntitlementDao.class);
+ bind(RepairEntitlementLifecycleDao.class).annotatedWith(Names.named(REPAIR_NAMED)).to(RepairEntitlementDao.class);
+ bind(RepairEntitlementDao.class).asEagerSingleton();
}
private void installNotificationQueue() {
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java
index a1744ca..6d863a9 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java
@@ -16,14 +16,22 @@
package com.ning.billing.entitlement.glue;
+
+import com.google.inject.name.Names;
+
import org.skife.config.ConfigurationObjectFactory;
import org.skife.jdbi.v2.IDBI;
+
import com.ning.billing.dbi.DBIProvider;
import com.ning.billing.dbi.DbiConfig;
import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.entitlement.api.repair.RepairEntitlementLifecycleDao;
import com.ning.billing.entitlement.engine.dao.EntitlementDao;
import com.ning.billing.entitlement.engine.dao.MockEntitlementDaoSql;
+
+import com.ning.billing.entitlement.engine.dao.RepairEntitlementDao;
+
import com.ning.billing.util.glue.FieldStoreModule;
import com.ning.billing.util.glue.NotificationQueueModule;
@@ -33,7 +41,11 @@ public class MockEngineModuleSql extends MockEngineModule {
@Override
protected void installEntitlementDao() {
bind(EntitlementDao.class).to(MockEntitlementDaoSql.class).asEagerSingleton();
+ bind(EntitlementDao.class).annotatedWith(Names.named(REPAIR_NAMED)).to(RepairEntitlementDao.class);
+ bind(RepairEntitlementLifecycleDao.class).annotatedWith(Names.named(REPAIR_NAMED)).to(RepairEntitlementDao.class);
+ bind(RepairEntitlementDao.class).asEagerSingleton();
}
+
protected void installDBI() {
final MysqlTestingHelper helper = new MysqlTestingHelper();
entitlement/src/test/resources/testInput.xml 515(+351 -164)
diff --git a/entitlement/src/test/resources/testInput.xml b/entitlement/src/test/resources/testInput.xml
index 8a97d57..bdc73db 100644
--- a/entitlement/src/test/resources/testInput.xml
+++ b/entitlement/src/test/resources/testInput.xml
@@ -1,41 +1,21 @@
<?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.
- -->
+<!-- ~ 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
-
-
-
- -->
+<!-- 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 ">
@@ -47,21 +27,21 @@ Use Cases to do:
<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>
+ <available>
+ <addonProduct>Telescopic-Scope</addonProduct>
+ <addonProduct>Laser-Scope</addonProduct>
+ </available>
</product>
<product name="Assault-Rifle">
<category>BASE</category>
- <included>
+ <included>
<addonProduct>Telescopic-Scope</addonProduct>
</included>
<available>
@@ -84,37 +64,37 @@ Use Cases to do:
<category>ADD_ON</category>
</product>
</products>
-
+
<rules>
<changePolicy>
- <changePolicyCase>
+ <changePolicyCase>
<phaseType>TRIAL</phaseType>
<policy>IMMEDIATE</policy>
</changePolicyCase>
- <changePolicyCase>
- <toProduct>Assault-Rifle</toProduct>
- <policy>IMMEDIATE</policy>
- </changePolicyCase>
- <changePolicyCase>
- <fromProduct>Pistol</fromProduct>
- <toProduct>Shotgun</toProduct>
- <policy>IMMEDIATE</policy>
- </changePolicyCase>
- <changePolicyCase>
+ <changePolicyCase>
+ <toProduct>Assault-Rifle</toProduct>
+ <policy>IMMEDIATE</policy>
+ </changePolicyCase>
+ <changePolicyCase>
+ <fromProduct>Pistol</fromProduct>
+ <toProduct>Shotgun</toProduct>
+ <policy>IMMEDIATE</policy>
+ </changePolicyCase>
+ <changePolicyCase>
<toPriceList>rescue</toPriceList>
<policy>END_OF_TERM</policy>
</changePolicyCase>
- <changePolicyCase>
+ <changePolicyCase>
<fromBillingPeriod>MONTHLY</fromBillingPeriod>
<toBillingPeriod>ANNUAL</toBillingPeriod>
<policy>IMMEDIATE</policy>
</changePolicyCase>
- <changePolicyCase>
+ <changePolicyCase>
<fromBillingPeriod>ANNUAL</fromBillingPeriod>
<toBillingPeriod>MONTHLY</toBillingPeriod>
<policy>END_OF_TERM</policy>
</changePolicyCase>
- <changePolicyCase>
+ <changePolicyCase>
<policy>END_OF_TERM</policy>
</changePolicyCase>
</changePolicy>
@@ -128,27 +108,27 @@ Use Cases to do:
<toPriceList>rescue</toPriceList>
<alignment>CHANGE_OF_PRICELIST</alignment>
</changeAlignmentCase>
- <changeAlignmentCase>
- <alignment>START_OF_SUBSCRIPTION</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>
+ <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>
+ <createAlignmentCase>
+ <product>Laser-Scope</product>
+ <alignment>START_OF_SUBSCRIPTION</alignment>
+ </createAlignmentCase>
+ <createAlignmentCase>
+ <alignment>START_OF_BUNDLE</alignment>
+ </createAlignmentCase>
</createAlignment>
<billingAlignment>
<billingAlignmentCase>
@@ -191,9 +171,18 @@ Use Cases to do:
</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>
+ <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>
@@ -207,7 +196,7 @@ Use Cases to do:
</duration>
<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
<fixedPrice></fixedPrice>
- <!-- no price implies $0 -->
+ <!-- no price implies $0 -->
</phase>
</initialPhases>
<finalPhase type="EVERGREEN">
@@ -217,9 +206,18 @@ Use Cases to do:
</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>
+ <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>
@@ -242,9 +240,18 @@ Use Cases to do:
</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>
+ <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>
@@ -267,9 +274,18 @@ Use Cases to do:
</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>
+ <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>
@@ -292,9 +308,18 @@ Use Cases to do:
</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>
+ <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>
@@ -317,9 +342,18 @@ Use Cases to do:
</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>
+ <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>
@@ -342,9 +376,18 @@ Use Cases to do:
</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>
+ <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>
@@ -354,9 +397,18 @@ Use Cases to do:
</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>
+ <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>
@@ -379,9 +431,18 @@ Use Cases to do:
</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>
+ <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>
@@ -391,9 +452,18 @@ Use Cases to do:
</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>
+ <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>
@@ -416,10 +486,19 @@ Use Cases to do:
</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>
+ <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">
@@ -428,65 +507,110 @@ Use Cases to do:
</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>
+ <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>
+ <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>
+ <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>
+ <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>
+ <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>
@@ -498,9 +622,18 @@ Use Cases to do:
</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>
+ <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) -->
@@ -524,9 +657,18 @@ Use Cases to do:
</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>
+ <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>
@@ -549,9 +691,18 @@ Use Cases to do:
</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>
+ <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>
@@ -565,9 +716,18 @@ Use Cases to do:
</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>
+ <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>
@@ -577,9 +737,18 @@ Use Cases to do:
</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>
+ <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>
@@ -592,20 +761,38 @@ Use Cases to do:
</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>
+ <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>
+ <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">
+ <defaultPriceList name="DEFAULT">
<plans>
<plan>pistol-monthly</plan>
<plan>shotgun-monthly</plan>
invoice/pom.xml 22(+22 -0)
diff --git a/invoice/pom.xml b/invoice/pom.xml
index 2083a0c..5f5522a 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -39,6 +39,22 @@
<type>test-jar</type>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
+ <artifactId>killbill-entitlement</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
+ <artifactId>killbill-entitlement</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
+ <artifactId>killbill-junction</artifactId>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>com.ning.billing</groupId>
<artifactId>killbill-catalog</artifactId>
@@ -51,6 +67,12 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>com.ning.billing</groupId>
+ <artifactId>killbill-account</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<scope>test</scope>
diff --git a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
index f45da81..96dca20 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
@@ -141,6 +141,7 @@ public class InvoiceDispatcher {
log.error("Failed to post DefaultEmptyInvoiceNotification event for account {} ", accountId, e);
}
}
+
private Invoice processAccountWithLock(final UUID accountId, final DateTime targetDate,
final boolean dryRun, final CallContext context) throws InvoiceApiException {
@@ -180,7 +181,7 @@ public class InvoiceDispatcher {
}
if (account.isNotifiedForInvoices()) {
-
+ invoiceNotifier.notify(account, invoice);
}
return invoice;
diff --git a/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java b/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
index b02c3e8..3a83729 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
@@ -28,6 +28,7 @@ import org.slf4j.LoggerFactory;
import com.google.common.eventbus.Subscribe;
import com.google.inject.Inject;
+import com.ning.billing.entitlement.api.repair.RepairEntitlementEvent;
import com.ning.billing.entitlement.api.user.SubscriptionEventTransition;
import com.ning.billing.invoice.api.InvoiceApiException;
@@ -43,6 +44,16 @@ public class InvoiceListener {
}
@Subscribe
+ public void handleRepairEntitlementEvent(final RepairEntitlementEvent repairEvent) {
+ try {
+ CallContext context = factory.createCallContext("RepairBundle", CallOrigin.INTERNAL, UserType.SYSTEM, repairEvent.getUserToken());
+ dispatcher.processAccount(repairEvent.getAccountId(), repairEvent.getEffectiveDate(), false, context);
+ } catch (InvoiceApiException e) {
+ log.error(e.getMessage());
+ }
+ }
+
+ @Subscribe
public void handleSubscriptionTransition(final SubscriptionEventTransition transition) {
try {
if (transition.getRemainingEventsForUserOperation() > 0) {
diff --git a/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDatePoster.java b/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDatePoster.java
index 8ddb29d..4aad036 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDatePoster.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDatePoster.java
@@ -31,7 +31,8 @@ import com.ning.billing.util.notificationq.NotificationQueueService;
import com.ning.billing.util.notificationq.NotificationQueueService.NoSuchNotificationQueue;
public class DefaultNextBillingDatePoster implements NextBillingDatePoster {
- private final static Logger log = LoggerFactory.getLogger(DefaultNextBillingDateNotifier.class);
+
+ private final static Logger log = LoggerFactory.getLogger(DefaultNextBillingDatePoster.class);
private final NotificationQueueService notificationQueueService;
diff --git a/invoice/src/main/java/com/ning/billing/invoice/notification/EmailInvoiceNotifier.java b/invoice/src/main/java/com/ning/billing/invoice/notification/EmailInvoiceNotifier.java
index aee0f66..58026b2 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/notification/EmailInvoiceNotifier.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/notification/EmailInvoiceNotifier.java
@@ -22,7 +22,9 @@ import com.ning.billing.account.api.Account;
import com.ning.billing.account.api.AccountEmail;
import com.ning.billing.account.api.AccountUserApi;
import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.invoice.api.InvoiceNotifier;
+import com.ning.billing.invoice.template.HtmlInvoiceGenerator;
import com.ning.billing.util.email.DefaultEmailSender;
import com.ning.billing.util.email.EmailApiException;
import com.ning.billing.util.email.EmailConfig;
@@ -34,16 +36,18 @@ import java.util.List;
public class EmailInvoiceNotifier implements InvoiceNotifier {
private final AccountUserApi accountUserApi;
+ private final HtmlInvoiceGenerator generator;
private final EmailConfig config;
@Inject
- public EmailInvoiceNotifier(AccountUserApi accountUserApi, EmailConfig config) {
+ public EmailInvoiceNotifier(AccountUserApi accountUserApi, HtmlInvoiceGenerator generator, EmailConfig config) {
this.accountUserApi = accountUserApi;
+ this.generator = generator;
this.config = config;
}
@Override
- public void notify(Account account, Invoice invoice) throws EmailApiException {
+ public void notify(Account account, Invoice invoice) throws InvoiceApiException {
List<String> to = new ArrayList<String>();
to.add(account.getEmail());
@@ -53,8 +57,12 @@ public class EmailInvoiceNotifier implements InvoiceNotifier {
cc.add(email.getEmail());
}
- // TODO: get html body from template
- String htmlBody = "";
+ String htmlBody = null;
+ try {
+ htmlBody = generator.generateInvoice(account, invoice, "HtmlInvoiceTemplate");
+ } catch (IOException e) {
+ throw new InvoiceApiException(e, ErrorCode.EMAIL_SENDING_FAILED);
+ }
// TODO: get subject
String subject = "";
@@ -62,8 +70,10 @@ public class EmailInvoiceNotifier implements InvoiceNotifier {
EmailSender sender = new DefaultEmailSender(config);
try {
sender.sendSecureEmail(to, cc, subject, htmlBody);
+ } catch (EmailApiException e) {
+ throw new InvoiceApiException(e, ErrorCode.EMAIL_SENDING_FAILED);
} catch (IOException e) {
- throw new EmailApiException(e, ErrorCode.EMAIL_SENDING_FAILED);
+ throw new InvoiceApiException(e, ErrorCode.EMAIL_SENDING_FAILED);
}
}
}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java b/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
index e94d4a3..fc90832 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
@@ -45,10 +45,10 @@ 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.dbi.MysqlTestingHelper;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
import com.ning.billing.entitlement.api.billing.BillingEvent;
import com.ning.billing.entitlement.api.billing.BillingModeType;
import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition.SubscriptionTransitionType;
import com.ning.billing.invoice.InvoiceDispatcher;
import com.ning.billing.invoice.TestInvoiceDispatcher;
import com.ning.billing.invoice.api.Invoice;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
index 423777f..d5edd6e 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
@@ -41,10 +41,10 @@ import com.ning.billing.catalog.api.Currency;
import com.ning.billing.catalog.api.PhaseType;
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
import com.ning.billing.entitlement.api.billing.BillingEvent;
import com.ning.billing.entitlement.api.billing.BillingModeType;
import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition.SubscriptionTransitionType;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.invoice.api.InvoiceItem;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java b/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java
index fc2e865..2ac6483 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java
@@ -25,38 +25,37 @@ import java.util.UUID;
import java.util.concurrent.Callable;
import com.ning.billing.account.api.AccountUserApi;
-import com.ning.billing.invoice.api.InvoiceNotifier;
-import com.ning.billing.junction.api.BillingApi;
-import org.apache.commons.io.IOUtils;
-import org.joda.time.DateTime;
-import org.skife.config.ConfigurationObjectFactory;
-import org.skife.jdbi.v2.IDBI;
-import org.skife.jdbi.v2.Transaction;
-import org.skife.jdbi.v2.TransactionStatus;
-import org.testng.Assert;
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-import com.google.inject.AbstractModule;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.google.inject.Stage;
+import com.ning.billing.account.api.MockAccountUserApi;
import com.ning.billing.catalog.DefaultCatalogService;
import com.ning.billing.catalog.api.CatalogService;
import com.ning.billing.config.CatalogConfig;
import com.ning.billing.config.InvoiceConfig;
import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.entitlement.api.SubscriptionApiService;
+import com.ning.billing.entitlement.api.SubscriptionFactory;
import com.ning.billing.entitlement.api.billing.ChargeThruApi;
+import com.ning.billing.entitlement.api.repair.RepairEntitlementLifecycleDao;
+import com.ning.billing.entitlement.api.repair.RepairSubscriptionApiService;
+import com.ning.billing.entitlement.api.repair.RepairSubscriptionFactory;
+import com.ning.billing.entitlement.api.user.DefaultEntitlementUserApi;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionApiService;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory;
import com.ning.billing.entitlement.api.user.EntitlementUserApi;
import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.engine.dao.EntitlementDao;
+import com.ning.billing.entitlement.engine.dao.EntitlementSqlDao;
+import com.ning.billing.entitlement.engine.dao.RepairEntitlementDao;
+import com.ning.billing.entitlement.glue.EntitlementModule;
import com.ning.billing.invoice.InvoiceDispatcher;
import com.ning.billing.invoice.InvoiceListener;
+import com.ning.billing.invoice.api.InvoiceNotifier;
import com.ning.billing.invoice.dao.DefaultInvoiceDao;
import com.ning.billing.invoice.dao.InvoiceDao;
import com.ning.billing.invoice.model.DefaultInvoiceGenerator;
import com.ning.billing.invoice.model.InvoiceGenerator;
-import com.ning.billing.lifecycle.KillbillService.ServiceException;
+import com.ning.billing.junction.api.BillingApi;
+import com.ning.billing.junction.plumbing.billing.DefaultBillingApi;
+import com.ning.billing.lifecycle.KillbillService;
import com.ning.billing.mock.BrainDeadProxyFactory;
import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
import com.ning.billing.util.bus.Bus;
@@ -75,6 +74,22 @@ import com.ning.billing.util.notificationq.NotificationQueueService;
import com.ning.billing.util.notificationq.dao.NotificationSqlDao;
import com.ning.billing.util.tag.dao.AuditedTagDao;
import com.ning.billing.util.tag.dao.TagDao;
+import org.apache.commons.io.IOUtils;
+import org.joda.time.DateTime;
+import org.skife.config.ConfigurationObjectFactory;
+import org.skife.jdbi.v2.IDBI;
+import org.skife.jdbi.v2.Transaction;
+import org.skife.jdbi.v2.TransactionStatus;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Stage;
+import com.google.inject.name.Names;
public class TestNextBillingDateNotifier {
private Clock clock;
@@ -85,6 +100,7 @@ public class TestNextBillingDateNotifier {
private InvoiceListenerMock listener;
private NotificationQueueService notificationQueueService;
+
private static final class InvoiceListenerMock extends InvoiceListener {
int eventCount = 0;
UUID latestSubscriptionId = null;
@@ -111,7 +127,7 @@ public class TestNextBillingDateNotifier {
}
@BeforeClass(groups={"slow"})
- public void setup() throws ServiceException, IOException, ClassNotFoundException, SQLException {
+ public void setup() throws KillbillService.ServiceException, IOException, ClassNotFoundException, SQLException {
//TestApiBase.loadSystemPropertiesFromClasspath("/entitlement.properties");
final Injector g = Guice.createInjector(Stage.PRODUCTION, new AbstractModule() {
@@ -131,19 +147,28 @@ public class TestNextBillingDateNotifier {
IDBI dbi = helper.getDBI();
bind(IDBI.class).toInstance(dbi);
bind(TagDao.class).to(AuditedTagDao.class).asEagerSingleton();
+ bind(EntitlementDao.class).to(EntitlementSqlDao.class).asEagerSingleton();
+ bind(EntitlementDao.class).annotatedWith(Names.named(EntitlementModule.REPAIR_NAMED)).to(RepairEntitlementDao.class);
+ bind(RepairEntitlementLifecycleDao.class).annotatedWith(Names.named(EntitlementModule.REPAIR_NAMED)).to(RepairEntitlementDao.class);
+ bind(RepairEntitlementDao.class).asEagerSingleton();
bind(CustomFieldDao.class).to(AuditedCustomFieldDao.class).asEagerSingleton();
bind(GlobalLocker.class).to(MySqlGlobalLocker.class).asEagerSingleton();
bind(InvoiceGenerator.class).to(DefaultInvoiceGenerator.class).asEagerSingleton();
- bind(InvoiceDao.class).to(DefaultInvoiceDao.class);
+ bind(InvoiceDao.class).to(DefaultInvoiceDao.class).asEagerSingleton();
bind(NextBillingDatePoster.class).to(DefaultNextBillingDatePoster.class).asEagerSingleton();
- bind(BillingApi.class).toInstance(BrainDeadProxyFactory.createBrainDeadProxyFor(BillingApi.class));
- bind(ChargeThruApi.class).toInstance(BrainDeadProxyFactory.createBrainDeadProxyFor(ChargeThruApi.class));
+ bind(AccountUserApi.class).to(MockAccountUserApi.class).asEagerSingleton();
- AccountUserApi accountUserApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);
- bind(AccountUserApi.class).toInstance(accountUserApi);
+
- EntitlementUserApi entitlementUserApi = BrainDeadProxyFactory.createBrainDeadProxyFor(EntitlementUserApi.class);
- bind(EntitlementUserApi.class).toInstance(entitlementUserApi);
+ bind(SubscriptionApiService.class).annotatedWith(Names.named(EntitlementModule.REPAIR_NAMED)).to(RepairSubscriptionApiService.class).asEagerSingleton();
+ bind(SubscriptionApiService.class).to(DefaultSubscriptionApiService.class).asEagerSingleton();
+ bind(SubscriptionFactory.class).annotatedWith(Names.named(EntitlementModule.REPAIR_NAMED)).to(RepairSubscriptionFactory.class).asEagerSingleton();
+ bind(SubscriptionFactory.class).to(DefaultSubscriptionFactory.class).asEagerSingleton();
+
+ bind(BillingApi.class).to(DefaultBillingApi.class).asEagerSingleton();
+ bind(EntitlementUserApi.class).to(DefaultEntitlementUserApi.class).asEagerSingleton();
+ bind(ChargeThruApi.class).toInstance(BrainDeadProxyFactory.createBrainDeadProxyFor(ChargeThruApi.class));
+
}
});
diff --git a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
index 753fda2..24073a8 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
@@ -46,10 +46,10 @@ 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.dbi.MysqlTestingHelper;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
import com.ning.billing.entitlement.api.billing.BillingEvent;
import com.ning.billing.entitlement.api.billing.BillingModeType;
import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition.SubscriptionTransitionType;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.invoice.dao.InvoiceDao;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
index 8492691..1f446d0 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
@@ -41,10 +41,10 @@ import com.ning.billing.catalog.api.PhaseType;
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
import com.ning.billing.config.InvoiceConfig;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
import com.ning.billing.entitlement.api.billing.BillingEvent;
import com.ning.billing.entitlement.api.billing.BillingModeType;
import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition.SubscriptionTransitionType;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.invoice.model.BillingEventSet;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/InvoicingTestBase.java b/invoice/src/test/java/com/ning/billing/invoice/tests/InvoicingTestBase.java
index 0f6d9db..132ac1c 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/InvoicingTestBase.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/InvoicingTestBase.java
@@ -23,11 +23,10 @@ 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.SubscriptionTransitionType;
import com.ning.billing.entitlement.api.billing.BillingEvent;
import com.ning.billing.entitlement.api.billing.BillingModeType;
import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition.SubscriptionTransitionType;
import org.joda.time.DateTime;
import com.ning.billing.invoice.model.InvoicingConfiguration;
@@ -90,7 +89,7 @@ public abstract class InvoicingTestBase {
final Currency currency, final BillingPeriod billingPeriod, final int billCycleDay,
final BillingModeType billingModeType, final String description,
final long totalOrdering,
- final SubscriptionEventTransition.SubscriptionTransitionType type) {
+ final SubscriptionTransitionType type) {
return new BillingEvent() {
@Override
public Account getAccount() {
junction/pom.xml 7(+0 -7)
diff --git a/junction/pom.xml b/junction/pom.xml
index b5dfd03..a33d843 100644
--- a/junction/pom.xml
+++ b/junction/pom.xml
@@ -110,13 +110,6 @@
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <configuration>
- <groups>fast,slow, stress</groups>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
diff --git a/junction/src/main/java/com/ning/billing/junction/api/billing/BlockingEventCalculator.java b/junction/src/main/java/com/ning/billing/junction/api/billing/BlockingEventCalculator.java
index c9edcbb..a1111b4 100644
--- a/junction/src/main/java/com/ning/billing/junction/api/billing/BlockingEventCalculator.java
+++ b/junction/src/main/java/com/ning/billing/junction/api/billing/BlockingEventCalculator.java
@@ -34,10 +34,10 @@ import com.ning.billing.catalog.api.CatalogService;
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.SubscriptionTransitionType;
import com.ning.billing.entitlement.api.billing.BillingEvent;
import com.ning.billing.entitlement.api.billing.BillingModeType;
import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition.SubscriptionTransitionType;
import com.ning.billing.junction.api.Blockable;
import com.ning.billing.junction.api.BlockingApi;
import com.ning.billing.junction.api.BlockingState;
diff --git a/junction/src/main/java/com/ning/billing/junction/glue/JunctionModule.java b/junction/src/main/java/com/ning/billing/junction/glue/JunctionModule.java
index 80cfe58..0d42e5f 100644
--- a/junction/src/main/java/com/ning/billing/junction/glue/JunctionModule.java
+++ b/junction/src/main/java/com/ning/billing/junction/glue/JunctionModule.java
@@ -64,8 +64,6 @@ public class JunctionModule extends AbstractModule {
public BlockingDaoProvider(IDBI dbi){
this.dbi = dbi;
}
-
-
@Override
public BlockingStateDao get() {
return dbi.onDemand(BlockingStateSqlDao.class);
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BillCycleDayCalculator.java b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BillCycleDayCalculator.java
index 0a0ddfc..f07fa04 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BillCycleDayCalculator.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BillCycleDayCalculator.java
@@ -32,11 +32,11 @@ import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
import com.ning.billing.catalog.api.PlanPhaseSpecifier;
import com.ning.billing.catalog.api.Product;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
import com.ning.billing.entitlement.api.user.EntitlementUserApi;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.entitlement.api.user.SubscriptionEventTransition;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition.SubscriptionTransitionType;
public class BillCycleDayCalculator {
private static final Logger log = LoggerFactory.getLogger(BillCycleDayCalculator.class);
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingEvent.java b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingEvent.java
index ec1a575..bee9113 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingEvent.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingEvent.java
@@ -26,11 +26,12 @@ import com.ning.billing.catalog.api.CatalogApiException;
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.SubscriptionTransitionType;
import com.ning.billing.entitlement.api.billing.BillingEvent;
import com.ning.billing.entitlement.api.billing.BillingModeType;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.api.user.SubscriptionEventTransition;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition.SubscriptionTransitionType;
public class DefaultBillingEvent implements BillingEvent {
final private Account account;
diff --git a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultBillingEvent.java b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultBillingEvent.java
index bd7d159..92e2bf2 100644
--- a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultBillingEvent.java
+++ b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultBillingEvent.java
@@ -37,11 +37,14 @@ import com.ning.billing.catalog.api.Currency;
import com.ning.billing.catalog.api.PhaseType;
import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
+
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.user.Subscription;
+
import com.ning.billing.entitlement.api.billing.BillingEvent;
import com.ning.billing.entitlement.api.billing.BillingModeType;
-import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.entitlement.api.user.SubscriptionEventTransition.SubscriptionTransitionType;
import com.ning.billing.junction.plumbing.billing.DefaultBillingEvent;
+
import com.ning.billing.mock.BrainDeadProxyFactory;
import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
diff --git a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultEntitlementBillingApi.java b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultEntitlementBillingApi.java
index 68f28c5..c05f262 100644
--- a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultEntitlementBillingApi.java
+++ b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultEntitlementBillingApi.java
@@ -21,6 +21,7 @@ import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import java.util.ArrayList;
+import java.util.LinkedList;
import java.util.List;
import java.util.SortedSet;
import java.util.UUID;
@@ -34,7 +35,6 @@ import org.testng.annotations.Test;
import com.ning.billing.account.api.Account;
import com.ning.billing.account.api.AccountUserApi;
-import com.ning.billing.catalog.DefaultCatalogService;
import com.ning.billing.catalog.MockCatalog;
import com.ning.billing.catalog.MockCatalogService;
import com.ning.billing.catalog.api.BillingAlignment;
@@ -53,8 +53,9 @@ import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
+
import com.ning.billing.entitlement.api.user.SubscriptionEventTransition;
-import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.entitlement.api.user.SubscriptionTransitionData;
import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
import com.ning.billing.entitlement.events.user.ApiEventType;
@@ -73,11 +74,13 @@ public class TestDefaultEntitlementBillingApi {
private static final UUID twoId = new UUID(2L,0L);
private CatalogService catalogService;
- private ArrayList<SubscriptionBundle> bundles;
- private ArrayList<Subscription> subscriptions;
- private ArrayList<SubscriptionEventTransition> transitions;
+
+ private List<SubscriptionBundle> bundles;
+ private List<Subscription> subscriptions;
+ private List<SubscriptionEventTransition> subscriptionTransitions;
private EntitlementUserApi entitlementApi;
+
private Clock clock;
private Subscription subscription;
private DateTime subscriptionStartDate;
@@ -95,8 +98,8 @@ public class TestDefaultEntitlementBillingApi {
bundles.add(bundle);
- transitions = new ArrayList<SubscriptionEventTransition>();
- subscriptions = new ArrayList<Subscription>();
+ subscriptionTransitions = new LinkedList<SubscriptionEventTransition>();
+ subscriptions = new LinkedList<Subscription>();
SubscriptionBuilder builder = new SubscriptionBuilder();
subscriptionStartDate = clock.getUTCNow().minusDays(3);
@@ -104,7 +107,7 @@ public class TestDefaultEntitlementBillingApi {
subscription = new SubscriptionData(builder) {
@Override
public List<SubscriptionEventTransition> getBillingTransitions() {
- return transitions;
+ return subscriptionTransitions;
}
};
@@ -145,7 +148,7 @@ public class TestDefaultEntitlementBillingApi {
PriceList nextPriceList = catalogService.getFullCatalog().findPriceList(PriceListSet.DEFAULT_PRICELIST_NAME, now);
SubscriptionEventTransition t = new SubscriptionTransitionData(
zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList, 1, null, true);
- transitions.add(t);
+ subscriptionTransitions.add(t);
AccountUserApi accountApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);
@@ -171,7 +174,7 @@ public class TestDefaultEntitlementBillingApi {
PriceList nextPriceList = catalogService.getFullCatalog().findPriceList(PriceListSet.DEFAULT_PRICELIST_NAME, now);
SubscriptionEventTransition t = new SubscriptionTransitionData(
zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList, 1, null, true);
- transitions.add(t);
+ subscriptionTransitions.add(t);
Account account = BrainDeadProxyFactory.createBrainDeadProxyFor(Account.class);
((ZombieControl)account).addResult("getBillCycleDay", 1).addResult("getTimeZone", DateTimeZone.UTC)
@@ -199,7 +202,7 @@ public class TestDefaultEntitlementBillingApi {
PriceList nextPriceList = catalogService.getFullCatalog().findPriceList(PriceListSet.DEFAULT_PRICELIST_NAME, now);
SubscriptionEventTransition t = new SubscriptionTransitionData(
zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList, 1, null, true);
- transitions.add(t);
+ subscriptionTransitions.add(t);
AccountUserApi accountApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);
Account account = BrainDeadProxyFactory.createBrainDeadProxyFor(Account.class);
@@ -226,7 +229,7 @@ public class TestDefaultEntitlementBillingApi {
PriceList nextPriceList = catalogService.getFullCatalog().findPriceList(PriceListSet.DEFAULT_PRICELIST_NAME, now);
SubscriptionEventTransition t = new SubscriptionTransitionData(
zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList, 1, null, true);
- transitions.add(t);
+ subscriptionTransitions.add(t);
Account account = BrainDeadProxyFactory.createBrainDeadProxyFor(Account.class);
((ZombieControl)account).addResult("getBillCycleDay", 1).addResult("getTimeZone", DateTimeZone.UTC);
@@ -272,6 +275,4 @@ public class TestDefaultEntitlementBillingApi {
Assert.assertEquals(BillingModeType.IN_ADVANCE, event.getBillingMode());
Assert.assertEquals(desc, event.getDescription());
}
-
-
}
pom.xml 5(+1 -4)
diff --git a/pom.xml b/pom.xml
index fd4c6bd..02b1aa6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -461,9 +461,6 @@
<exclude>**/target/**</exclude>
<exclude>**/.settings/**</exclude>
<exclude>.travis.yml</exclude>
- <!-- until we merge from server branch we disable rat for those -->
- <exclude>jaxrs/**</exclude>
- <exclude>server/**</exclude>
<exclude>bin/**</exclude>
<!-- exclude mustache template files -->
<exclude>**/*.mustache</exclude>
@@ -550,7 +547,7 @@
<systemPropertyVariables>
<log4j.configuration>file:${project.basedir}/src/test/resources/log4j.xml</log4j.configuration>
<com.ning.billing.dbi.test.useLocalDb>true</com.ning.billing.dbi.test.useLocalDb>
- <com.ning.billing.dbi.jdbc.url>jdbc:mysql://127.0.0.1:3306/test_killbill</com.ning.billing.dbi.jdbc.url>
+ <com.ning.billing.dbi.jdbc.url>jdbc:mysql://127.0.0.1:3306/killbill</com.ning.billing.dbi.jdbc.url>
<file.encoding>UTF-8</file.encoding>
<user.timezone>GMT</user.timezone>
</systemPropertyVariables>
server/pom.xml 14(+9 -5)
diff --git a/server/pom.xml b/server/pom.xml
index ea39345..4bc0388 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -34,8 +34,7 @@
</properties>
<dependencies>
-
-
+ <!-- Jetty provided scope -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-http</artifactId>
@@ -80,8 +79,6 @@
<scope>test</scope>
</dependency>
-
-
<!-- NOT in master POM; include version as well -->
<dependency>
<groupId>org.weakref</groupId>
@@ -118,7 +115,6 @@
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>
- <!-- Do we want to depend on skeleton -->
<dependency>
<groupId>com.ning.jetty</groupId>
<artifactId>ning-service-skeleton-base</artifactId>
@@ -195,6 +191,10 @@
<groupId>com.ning.billing</groupId>
<artifactId>killbill-analytics</artifactId>
</dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
+ <artifactId>killbill-junction</artifactId>
+ </dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
@@ -233,6 +233,10 @@
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ </dependency>
<dependency>
<groupId>com.ning</groupId>
<artifactId>async-http-client</artifactId>
diff --git a/server/src/main/java/com/ning/billing/server/listeners/KillbillGuiceListener.java b/server/src/main/java/com/ning/billing/server/listeners/KillbillGuiceListener.java
index 11de6ec..224d637 100644
--- a/server/src/main/java/com/ning/billing/server/listeners/KillbillGuiceListener.java
+++ b/server/src/main/java/com/ning/billing/server/listeners/KillbillGuiceListener.java
@@ -42,8 +42,7 @@ public class KillbillGuiceListener extends SetupServer
private BusService killbillBusService;
private KillbillEventHandler killbilleventHandler;
- protected Injector theInjector;
-
+
protected Module getModule() {
return new KillbillServerModule();
}
@@ -66,7 +65,7 @@ public class KillbillGuiceListener extends SetupServer
super.contextInitialized(event);
logger.info("KillbillLifecycleListener : contextInitialized");
- theInjector = injector(event);
+ Injector theInjector = injector(event);
killbillLifecycle = theInjector.getInstance(DefaultLifecycle.class);
killbillBusService = theInjector.getInstance(BusService.class);
killbilleventHandler = theInjector.getInstance(KillbillEventHandler.class);
diff --git a/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java b/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
index 4f10f28..8a9e490 100644
--- a/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
+++ b/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
@@ -35,6 +35,7 @@ import com.ning.billing.jaxrs.resources.InvoiceResource;
import com.ning.billing.jaxrs.resources.PaymentResource;
import com.ning.billing.jaxrs.resources.SubscriptionResource;
import com.ning.billing.jaxrs.util.KillbillEventHandler;
+import com.ning.billing.junction.glue.JunctionModule;
import com.ning.billing.payment.setup.PaymentModule;
import com.ning.billing.util.glue.BusModule;
import com.ning.billing.util.glue.CallContextModule;
@@ -87,6 +88,7 @@ public class KillbillServerModule extends AbstractModule
install(new AnalyticsModule());
install(new PaymentModule());
install(new BeatrixModule());
+ install(new JunctionModule());
installClock();
}
}
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
index 62d8505..401db90 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
@@ -30,9 +30,9 @@ import java.util.concurrent.TimeUnit;
import javax.ws.rs.core.Response.Status;
-import com.ning.billing.util.email.EmailConfig;
import com.ning.billing.util.email.EmailModule;
import com.ning.billing.util.glue.GlobalLockerModule;
+import org.apache.commons.io.IOUtils;
import org.codehaus.jackson.map.ObjectMapper;
import org.eclipse.jetty.servlet.FilterHolder;
import org.skife.config.ConfigurationObjectFactory;
@@ -45,12 +45,12 @@ import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeSuite;
-import com.google.inject.Injector;
import com.google.inject.Module;
import com.ning.billing.account.glue.AccountModule;
import com.ning.billing.analytics.setup.AnalyticsModule;
import com.ning.billing.beatrix.glue.BeatrixModule;
import com.ning.billing.beatrix.integration.TestBusHandler;
+import com.ning.billing.beatrix.integration.TestIntegration;
import com.ning.billing.catalog.api.PriceListSet;
import com.ning.billing.catalog.glue.CatalogModule;
import com.ning.billing.config.PaymentConfig;
@@ -96,13 +96,12 @@ public class TestJaxrsBase {
public static final String HEADER_CONTENT_TYPE = "Content-type";
public static final String CONTENT_TYPE = "application/json";
+ private static TestKillbillGuiceListener listener;
+
+
private MysqlTestingHelper helper;
private HttpServer server;
-
- // YAck...
- private static Injector injector;
-
protected CoreConfig config;
protected AsyncHttpClient httpClient;
protected ObjectMapper mapper;
@@ -120,23 +119,48 @@ public class TestJaxrsBase {
}
public static class TestKillbillGuiceListener extends KillbillGuiceListener {
- public TestKillbillGuiceListener() {
+
+ private final MysqlTestingHelper helper;
+ private final Clock clock;
+
+ public TestKillbillGuiceListener(final MysqlTestingHelper helper, final Clock clock) {
super();
+ this.helper = helper;
+ this.clock = clock;
}
@Override
protected Module getModule() {
- return new TestKillbillServerModule();
+ return new TestKillbillServerModule(helper, clock);
+ }
+
+ //
+ // Listener is created once befire Suite and keeps pointer to helper and clock so they can get
+ // reset for each test Class-- needed in order to ONLY start Jetty once across all the test classes
+ // while still being able to start mysql before Jetty is started
+ //
+ public MysqlTestingHelper getMysqlTestingHelper() {
+ return helper;
}
- public Injector getTheInjector() {
- return theInjector;
+
+ public Clock getClock() {
+ return clock;
}
}
public static class TestKillbillServerModule extends KillbillServerModule {
+ private final MysqlTestingHelper helper;
+ private final Clock clock;
+
+ public TestKillbillServerModule(final MysqlTestingHelper helper, final Clock clock) {
+ super();
+ this.helper = helper;
+ this.clock = clock;
+ }
+
@Override
protected void installClock() {
- bind(Clock.class).to(ClockMock.class).asEagerSingleton();
+ bind(Clock.class).toInstance(clock);
}
@@ -175,7 +199,6 @@ public class TestJaxrsBase {
@Override
protected void configureDao() {
- final MysqlTestingHelper helper = new MysqlTestingHelper();
bind(MysqlTestingHelper.class).toInstance(helper);
if (helper.isUsingLocalInstance()) {
bind(IDBI.class).toProvider(DBIProvider.class).asEagerSingleton();
@@ -189,20 +212,35 @@ public class TestJaxrsBase {
}
@BeforeMethod(groups="slow")
- public void cleanupTables() {
- helper.cleanupAllTables();
+ public void cleanupBeforeMethod() {
busHandler.reset();
+ helper.cleanupAllTables();
}
@BeforeClass(groups="slow")
- public void setupClass() {
-
+ public void setupClass() throws IOException {
loadConfig();
httpClient = new AsyncHttpClient();
mapper = new ObjectMapper();
- helper = injector.getInstance(MysqlTestingHelper.class);
- clock = (ClockMock) injector.getInstance(Clock.class);
- busHandler = new TestBusHandler();
+ busHandler = new TestBusHandler(null);
+ this.helper = listener.getMysqlTestingHelper();
+ this.clock = (ClockMock) listener.getClock();
+ }
+
+ private void setupMySQL() throws IOException {
+ final String accountDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
+ final String entitlementDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/entitlement/ddl.sql"));
+ final String invoiceDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
+ final String paymentDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/payment/ddl.sql"));
+ final String utilDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
+
+ helper.startMysql();
+
+ helper.initDb(accountDdl);
+ helper.initDb(entitlementDdl);
+ helper.initDb(invoiceDdl);
+ helper.initDb(paymentDdl);
+ helper.initDb(utilDdl);
}
@@ -216,33 +254,39 @@ public class TestJaxrsBase {
public void setup() throws Exception {
loadSystemPropertiesFromClasspath("/killbill.properties");
-
- final EventListener eventListener = new TestKillbillGuiceListener();
- server = new HttpServer();
loadConfig();
+
+ this.helper = new MysqlTestingHelper();
+ this.clock = new ClockMock();
+ listener = new TestKillbillGuiceListener(helper, clock);
+ server = new HttpServer();
+
final Iterable<EventListener> eventListeners = new Iterable<EventListener>() {
@Override
public Iterator<EventListener> iterator() {
ArrayList<EventListener> array = new ArrayList<EventListener>();
- array.add(eventListener);
+ array.add(listener);
return array.iterator();
}
};
server.configure(config, eventListeners, new HashMap<FilterHolder, String>());
+
+ setupMySQL();
+ helper.cleanupAllTables();
+
server.start();
- injector = ((TestKillbillGuiceListener) eventListener).getTheInjector();
}
@AfterSuite(groups="slow")
public void tearDown() {
- if (helper != null) {
- helper.stopMysql();
- }
try {
server.stop();
} catch (Exception e) {
}
+ if (helper != null) {
+ helper.stopMysql();
+ }
}
diff --git a/server/src/test/resources/killbill.properties b/server/src/test/resources/killbill.properties
index 2ff0814..3463266 100644
--- a/server/src/test/resources/killbill.properties
+++ b/server/src/test/resources/killbill.properties
@@ -1,5 +1,5 @@
-# Use killbill util test properties (DbiProvider/MysqltestingHelper) on the test side configured with test_killbill
-com.ning.billing.dbi.jdbc.url=jdbc:mysql://127.0.0.1:3306/test_killbill
+# Use killbill util test properties (DbiProvider/MysqltestingHelper) on the test side configured with killbill
+com.ning.billing.dbi.jdbc.url=jdbc:mysql://127.0.0.1:3306/killbill
killbill.catalog.uri=file:src/test/resources/catalog-weapons.xml
@@ -8,9 +8,10 @@ killbill.payment.retry.days=8,8,8
user.timezone=UTC
-com.ning.billing.dbi.test.useLocalDb=true
-
com.ning.core.server.jetty.logPath=/var/tmp/.logs
+# Local DB
+#com.ning.billing.dbi.test.useLocalDb=true
+
diff --git a/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java b/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
index 9995229..1b0d9f6 100644
--- a/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
+++ b/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
@@ -46,7 +46,7 @@ public class MysqlTestingHelper
private static final Logger log = LoggerFactory.getLogger(MysqlTestingHelper.class);
- private static final String DB_NAME = "test_killbill";
+ private static final String DB_NAME = "killbill";
private static final String USERNAME = "root";
private static final String PASSWORD = "root";
diff --git a/util/src/test/java/com/ning/billing/util/validation/TestValidationManager.java b/util/src/test/java/com/ning/billing/util/validation/TestValidationManager.java
index 1848950..4e845db 100644
--- a/util/src/test/java/com/ning/billing/util/validation/TestValidationManager.java
+++ b/util/src/test/java/com/ning/billing/util/validation/TestValidationManager.java
@@ -67,7 +67,9 @@ public class TestValidationManager {
}
private void stopDatabase() {
- helper.stopMysql();
+ if (helper != null) {
+ helper.stopMysql();
+ }
}
@Test(groups = "slow")