/*
* Copyright 2010-2013 Ning, Inc.
* Copyright 2014-2017 Groupon, Inc
* Copyright 2014-2017 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package org.killbill.billing.beatrix.integration;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Stage;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
import org.killbill.billing.ErrorCode;
import org.killbill.billing.ObjectType;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.AccountData;
import org.killbill.billing.account.api.AccountInternalApi;
import org.killbill.billing.account.api.AccountService;
import org.killbill.billing.account.api.AccountUserApi;
import org.killbill.billing.api.TestApiListener;
import org.killbill.billing.api.TestApiListener.NextEvent;
import org.killbill.billing.beatrix.BeatrixTestSuiteWithEmbeddedDB;
import org.killbill.billing.beatrix.util.AccountChecker;
import org.killbill.billing.beatrix.util.InvoiceChecker;
import org.killbill.billing.beatrix.util.PaymentChecker;
import org.killbill.billing.beatrix.util.RefundChecker;
import org.killbill.billing.beatrix.util.SubscriptionChecker;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.BillingActionPolicy;
import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.catalog.api.PhaseType;
import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
import org.killbill.billing.catalog.api.PlanSpecifier;
import org.killbill.billing.catalog.api.PriceListSet;
import org.killbill.billing.catalog.api.ProductCategory;
import org.killbill.billing.entitlement.api.BlockingState;
import org.killbill.billing.entitlement.api.BlockingStateType;
import org.killbill.billing.entitlement.api.DefaultEntitlement;
import org.killbill.billing.entitlement.api.Entitlement;
import org.killbill.billing.entitlement.api.EntitlementApi;
import org.killbill.billing.entitlement.api.EntitlementApiException;
import org.killbill.billing.entitlement.api.SubscriptionApi;
import org.killbill.billing.entitlement.api.SubscriptionEventType;
import org.killbill.billing.invoice.ParkedAccountsManager;
import org.killbill.billing.invoice.api.DryRunArguments;
import org.killbill.billing.invoice.api.DryRunType;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoicePaymentApi;
import org.killbill.billing.invoice.api.InvoiceService;
import org.killbill.billing.invoice.api.InvoiceUserApi;
import org.killbill.billing.junction.BlockingInternalApi;
import org.killbill.billing.lifecycle.api.BusService;
import org.killbill.billing.lifecycle.api.Lifecycle;
import org.killbill.billing.lifecycle.glue.BusModule;
import org.killbill.billing.mock.MockAccountBuilder;
import org.killbill.billing.osgi.config.OSGIConfig;
import org.killbill.billing.overdue.OverdueService;
import org.killbill.billing.overdue.api.OverdueApi;
import org.killbill.billing.overdue.api.OverdueConfig;
import org.killbill.billing.overdue.caching.OverdueConfigCache;
import org.killbill.billing.overdue.listener.OverdueListener;
import org.killbill.billing.overdue.wrapper.OverdueWrapper;
import org.killbill.billing.overdue.wrapper.OverdueWrapperFactory;
import org.killbill.billing.payment.api.AdminPaymentApi;
import org.killbill.billing.payment.api.Payment;
import org.killbill.billing.payment.api.PaymentApi;
import org.killbill.billing.payment.api.PaymentApiException;
import org.killbill.billing.payment.api.PaymentMethodPlugin;
import org.killbill.billing.payment.api.PaymentOptions;
import org.killbill.billing.payment.api.PaymentTransaction;
import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.payment.api.TestPaymentMethodPluginBase;
import org.killbill.billing.payment.api.TransactionStatus;
import org.killbill.billing.payment.api.TransactionType;
import org.killbill.billing.payment.invoice.InvoicePaymentControlPluginApi;
import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
import org.killbill.billing.subscription.api.SubscriptionBase;
import org.killbill.billing.subscription.api.SubscriptionBaseService;
import org.killbill.billing.subscription.api.timeline.SubscriptionBaseTimelineApi;
import org.killbill.billing.subscription.api.transfer.SubscriptionBaseTransferApi;
import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
import org.killbill.billing.tenant.api.TenantUserApi;
import org.killbill.billing.usage.api.UsageUserApi;
import org.killbill.billing.util.api.RecordIdApi;
import org.killbill.billing.util.api.TagApiException;
import org.killbill.billing.util.api.TagDefinitionApiException;
import org.killbill.billing.util.api.TagUserApi;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
import org.killbill.billing.util.config.definition.InvoiceConfig;
import org.killbill.billing.util.config.definition.PaymentConfig;
import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.billing.util.nodes.KillbillNodesApi;
import org.killbill.billing.util.tag.ControlTagType;
import org.killbill.billing.util.tag.Tag;
import org.killbill.bus.api.PersistentBus;
import org.skife.config.ConfigurationObjectFactory;
import org.skife.config.TimeSpan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Callable;
import static org.awaitility.Awaitility.await;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
protected static final DateTimeZone testTimeZone = DateTimeZone.UTC;
protected static final Logger log = LoggerFactory.getLogger(TestIntegrationBase.class);
protected static long AT_LEAST_ONE_MONTH_MS = 32L * 24L * 3600L * 1000L;
protected static final PaymentOptions PAYMENT_OPTIONS = new PaymentOptions() {
@Override
public boolean isExternalPayment() {
return false;
}
@Override
public List<String> getPaymentControlPluginNames() {
return ImmutableList.<String>of(InvoicePaymentControlPluginApi.PLUGIN_NAME);
}
};
protected static final PaymentOptions EXTERNAL_PAYMENT_OPTIONS = new PaymentOptions() {
@Override
public boolean isExternalPayment() {
return true;
}
@Override
public List<String> getPaymentControlPluginNames() {
return ImmutableList.<String>of(InvoicePaymentControlPluginApi.PLUGIN_NAME);
}
};
protected final Iterable<PluginProperty> PLUGIN_PROPERTIES = ImmutableList.<PluginProperty>of();
@Inject
protected Lifecycle lifecycle;
@Inject
protected BusService busService;
@Inject
protected SubscriptionBaseService subscriptionBaseService;
@Inject
protected InvoiceService invoiceService;
@Inject
protected AccountService accountService;
@Inject
protected SubscriptionBaseTransferApi transferApi;
@Inject
protected SubscriptionBaseTimelineApi repairApi;
@Inject
protected OverdueApi overdueUserApi;
@Inject
protected InvoiceUserApi invoiceUserApi;
@Inject
protected InvoicePaymentApi invoicePaymentApi;
@Inject
protected BlockingInternalApi blockingApi;
@Inject
protected PaymentApi paymentApi;
@Inject
protected AdminPaymentApi adminPaymentApi;
@Inject
protected EntitlementApi entitlementApi;
@Inject
protected SubscriptionApi subscriptionApi;
@Named(BeatrixIntegrationModule.NON_OSGI_PLUGIN_NAME)
@Inject
protected MockPaymentProviderPlugin paymentPlugin;
@Inject
protected OverdueWrapperFactory overdueWrapperFactory;
@Inject
protected OverdueListener overdueListener;
@Inject
protected AccountUserApi accountUserApi;
@Inject
protected TagUserApi tagUserApi;
@Inject
protected InvoiceChecker invoiceChecker;
@Inject
protected PaymentChecker paymentChecker;
@Inject
protected AccountChecker accountChecker;
@Inject
@Named(BusModule.EXTERNAL_BUS_NAMED)
protected PersistentBus externalBus;
@Inject
protected RefundChecker refundChecker;
@Inject
protected SubscriptionChecker subscriptionChecker;
@Inject
protected AccountInternalApi accountInternalApi;
@Inject
protected UsageUserApi usageUserApi;
@Inject
protected OSGIConfig osgiConfig;
@Inject
protected RecordIdApi recordIdApi;
@Inject
protected NonEntityDao nonEntityDao;
@Inject
protected TestApiListener busHandler;
@Inject
protected OverdueConfigCache overdueConfigCache;
@Inject
protected TenantUserApi tenantUserApi;
@Inject
protected KillbillNodesApi nodesApi;
@Inject
protected CacheControllerDispatcher controllerDispatcher;
@Inject
protected ParkedAccountsManager parkedAccountsManager;
@Inject
protected PaymentConfig paymentConfig;
protected ConfigurableInvoiceConfig invoiceConfig;
protected void assertListenerStatus() {
busHandler.assertListenerStatus();
}
@BeforeClass(groups = "slow")
public void beforeClass() throws Exception {
final InvoiceConfig defaultInvoiceConfig = new ConfigurationObjectFactory(skifeConfigSource).build(InvoiceConfig.class);
invoiceConfig = new ConfigurableInvoiceConfig(defaultInvoiceConfig);
final Injector g = Guice.createInjector(Stage.PRODUCTION, new BeatrixIntegrationModule(configSource, invoiceConfig));
g.injectMembers(this);
}
@BeforeMethod(groups = "slow")
public void beforeMethod() throws Exception {
super.beforeMethod();
log.debug("beforeMethod callcontext classLoader = " + (Thread.currentThread().getContextClassLoader() != null ? Thread.currentThread().getContextClassLoader().toString() : "null"));
//Thread.currentThread().setContextClassLoader(null);
log.debug("RESET TEST FRAMEWORK");
controllerDispatcher.clearAll();
overdueConfigCache.loadDefaultOverdueConfig((OverdueConfig) null);
clock.resetDeltaFromReality();
busHandler.reset();
// Start services
lifecycle.fireStartupSequencePriorEventRegistration();
busService.getBus().register(busHandler);
lifecycle.fireStartupSequencePostEventRegistration();
paymentPlugin.clear();
// Make sure we start with a clean state
assertListenerStatus();
}
@AfterMethod(groups = "slow")
public void afterMethod() throws Exception {
// Make sure we finish in a clean state
assertListenerStatus();
lifecycle.fireShutdownSequencePriorEventUnRegistration();
busService.getBus().unregister(busHandler);
lifecycle.fireShutdownSequencePostEventUnRegistration();
log.debug("afterMethod callcontext classLoader = " + (Thread.currentThread().getContextClassLoader() != null ? Thread.currentThread().getContextClassLoader().toString() : "null"));
log.debug("DONE WITH TEST");
}
protected void checkNoMoreInvoiceToGenerate(final Account account) {
busHandler.pushExpectedEvent(NextEvent.NULL_INVOICE);
try {
invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), null, callContext);
fail("Should not have generated an extra invoice");
} catch (final InvoiceApiException e) {
assertListenerStatus();
assertEquals(e.getCode(), ErrorCode.INVOICE_NOTHING_TO_DO.getCode());
}
}
protected void verifyTestResult(final UUID accountId, final UUID subscriptionId,
final DateTime startDate, @Nullable final DateTime endDate,
final BigDecimal amount, final DateTime chargeThroughDate,
final int totalInvoiceItemCount) throws EntitlementApiException {
final Entitlement entitlement = entitlementApi.getEntitlementForId(subscriptionId, callContext);
final SubscriptionBase subscription = ((DefaultEntitlement) entitlement).getSubscriptionBase();
final DateTime ctd = subscription.getChargedThroughDate();
assertNotNull(ctd);
log.info("Checking CTD: " + ctd.toString() + "; clock is " + clock.getUTCNow().toString());
// Either the ctd is today (start of the trial) or the clock is strictly before the CTD
assertTrue(clock.getUTCToday().compareTo(new LocalDate(ctd)) == 0 || clock.getUTCNow().isBefore(ctd));
assertTrue(ctd.toDateTime(testTimeZone).toLocalDate().compareTo(new LocalDate(chargeThroughDate.getYear(), chargeThroughDate.getMonthOfYear(), chargeThroughDate.getDayOfMonth())) == 0);
}
protected void checkODState(final String expected, final UUID accountId) {
try {
// This will test the overdue notification queue: when we move the clock, the overdue system
// should get notified to refresh its state.
// Calling explicitly refresh here (overdueApi.refreshOverdueStateFor(account)) would not fully
// test overdue.
// Since we're relying on the notification queue, we may need to wait a bit (hence await()).
await().atMost(10, SECONDS).until(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
final BlockingState blockingStateForService = blockingApi.getBlockingStateForService(accountId, BlockingStateType.ACCOUNT, OverdueService.OVERDUE_SERVICE_NAME, internalCallContext);
final String stateName = blockingStateForService != null ? blockingStateForService.getStateName() : OverdueWrapper.CLEAR_STATE_NAME;
return expected.equals(stateName);
}
});
} catch (final Exception e) {
final BlockingState blockingStateForService = blockingApi.getBlockingStateForService(accountId, BlockingStateType.ACCOUNT, OverdueService.OVERDUE_SERVICE_NAME, internalCallContext);
final String stateName = blockingStateForService != null ? blockingStateForService.getStateName() : OverdueWrapper.CLEAR_STATE_NAME;
Assert.assertEquals(stateName, expected, "Got exception: " + e.toString());
}
}
protected DefaultSubscriptionBase subscriptionDataFromSubscription(final SubscriptionBase sub) {
return (DefaultSubscriptionBase) sub;
}
protected Account createAccountWithOsgiPaymentMethod(final AccountData accountData) throws Exception {
return createAccountWithPaymentMethod(accountData, BeatrixIntegrationModule.OSGI_PLUGIN_NAME);
}
protected Account createAccountWithNonOsgiPaymentMethod(final AccountData accountData) throws Exception {
return createAccountWithPaymentMethod(accountData, BeatrixIntegrationModule.NON_OSGI_PLUGIN_NAME);
}
private Account createAccountWithPaymentMethod(final AccountData accountData, final String paymentPluginName) throws Exception {
final Account account = accountUserApi.createAccount(accountData, callContext);
assertNotNull(account);
refreshCallContext(account.getId());
final PaymentMethodPlugin info = createPaymentMethodPlugin();
paymentApi.addPaymentMethod(account, UUID.randomUUID().toString(), paymentPluginName, true, info, PLUGIN_PROPERTIES, callContext);
return accountUserApi.getAccountById(account.getId(), callContext);
}
protected PaymentMethodPlugin createPaymentMethodPlugin() {
return new TestPaymentMethodPlugin();
}
protected AccountData getAccountData(@Nullable final Integer billingDay) {
final MockAccountBuilder builder = new MockAccountBuilder()
.name(UUID.randomUUID().toString().substring(1, 8))
.firstNameLength(6)
.email(UUID.randomUUID().toString().substring(1, 8))
.phone(UUID.randomUUID().toString().substring(1, 8))
.migrated(false)
.isNotifiedForInvoices(false)
.externalKey(UUID.randomUUID().toString().substring(1, 8))
.currency(Currency.USD)
.timeZone(DateTimeZone.UTC);
if (billingDay != null) {
builder.billingCycleDayLocal(billingDay);
}
return builder.build();
}
protected AccountData getChildAccountData(final int billingDay, final UUID parentAccountId, final boolean isPaymentDelegatedToParent) {
return new MockAccountBuilder().name(UUID.randomUUID().toString().substring(1, 8))
.firstNameLength(6)
.email(UUID.randomUUID().toString().substring(1, 8))
.phone(UUID.randomUUID().toString().substring(1, 8))
.migrated(false)
.isNotifiedForInvoices(false)
.externalKey(UUID.randomUUID().toString().substring(1, 8))
.billingCycleDayLocal(billingDay)
.currency(Currency.USD)
.paymentMethodId(UUID.randomUUID())
.timeZone(DateTimeZone.UTC)
.parentAccountId(parentAccountId)
.isPaymentDelegatedToParent(isPaymentDelegatedToParent)
.build();
}
protected void addMonthsAndCheckForCompletion(final int nbMonth, final NextEvent... events) {
doCallAndCheckForCompletion(new Function<Void, Void>() {
@Override
public Void apply(@Nullable final Void dontcare) {
clock.addMonths(nbMonth);
return null;
}
}, events);
}
protected void setDateAndCheckForCompletion(final DateTime date, final List<NextEvent> events) {
setDateAndCheckForCompletion(date, events.toArray(new NextEvent[events.size()]));
}
protected void setDateAndCheckForCompletion(final DateTime date, final NextEvent... events) {
doCallAndCheckForCompletion(new Function<Void, Void>() {
@Override
public Void apply(@Nullable final Void dontcare) {
//final Interval it = new Interval(clock.getUTCNow(), date);
//final int days = it.toPeriod().toStandardDays().getDays();
clock.setTime(date);
return null;
}
}, events);
}
protected void addDaysAndCheckForCompletion(final int nbDays, final NextEvent... events) {
doCallAndCheckForCompletion(new Function<Void, Void>() {
@Override
public Void apply(@Nullable final Void dontcare) {
clock.addDays(nbDays);
return null;
}
}, events);
}
protected Payment createPaymentAndCheckForCompletion(final Account account, final Invoice invoice, final BigDecimal amount, final Currency currency, final NextEvent... events) {
return doCallAndCheckForCompletion(new Function<Void, Payment>() {
@Override
public Payment apply(@Nullable final Void input) {
try {
final List<PluginProperty> properties = new ArrayList<PluginProperty>();
final PluginProperty prop1 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, invoice.getId().toString(), false);
properties.add(prop1);
return paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, amount, currency, UUID.randomUUID().toString(),
UUID.randomUUID().toString(), properties, PAYMENT_OPTIONS, callContext);
} catch (final PaymentApiException e) {
fail(e.toString());
return null;
}
}
}, events);
}
protected Payment createPaymentAndCheckForCompletion(final Account account, final Invoice invoice, final NextEvent... events) {
return doCallAndCheckForCompletion(new Function<Void, Payment>() {
@Override
public Payment apply(@Nullable final Void input) {
try {
final List<PluginProperty> properties = new ArrayList<PluginProperty>();
final PluginProperty prop1 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, invoice.getId().toString(), false);
properties.add(prop1);
return paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, invoice.getBalance(), invoice.getCurrency(), UUID.randomUUID().toString(),
UUID.randomUUID().toString(), properties, PAYMENT_OPTIONS, callContext);
} catch (final PaymentApiException e) {
fail(e.toString());
return null;
}
}
}, events);
}
protected Payment createExternalPaymentAndCheckForCompletion(final Account account, final Invoice invoice, final NextEvent... events) {
return doCallAndCheckForCompletion(new Function<Void, Payment>() {
@Override
public Payment apply(@Nullable final Void input) {
try {
final List<PluginProperty> properties = new ArrayList<PluginProperty>();
final PluginProperty prop1 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, invoice.getId().toString(), false);
properties.add(prop1);
return paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, invoice.getBalance(), invoice.getCurrency(), UUID.randomUUID().toString(),
UUID.randomUUID().toString(), properties, EXTERNAL_PAYMENT_OPTIONS, callContext);
} catch (final PaymentApiException e) {
fail(e.toString());
return null;
}
}
}, events);
}
protected Payment refundPaymentAndCheckForCompletion(final Account account, final Payment payment, final NextEvent... events) {
return refundPaymentAndCheckForCompletion(account, payment, payment.getPurchasedAmount(), payment.getCurrency(), events);
}
protected Payment refundPaymentAndCheckForCompletion(final Account account, final Payment payment, final BigDecimal amount, final Currency currency, final NextEvent... events) {
return doCallAndCheckForCompletion(new Function<Void, Payment>() {
@Override
public Payment apply(@Nullable final Void input) {
try {
return paymentApi.createRefundWithPaymentControl(account, payment.getId(), amount, currency, UUID.randomUUID().toString(),
PLUGIN_PROPERTIES, PAYMENT_OPTIONS, callContext);
} catch (final PaymentApiException e) {
fail(e.toString());
return null;
}
}
}, events);
}
protected Payment refundPaymentWithInvoiceItemAdjAndCheckForCompletion(final Account account, final Payment payment, final Map<UUID, BigDecimal> iias, final NextEvent... events) {
return refundPaymentWithInvoiceItemAdjAndCheckForCompletion(account, payment, payment.getPurchasedAmount(), payment.getCurrency(), iias, events);
}
protected Payment refundPaymentWithInvoiceItemAdjAndCheckForCompletion(final Account account, final Payment payment, final BigDecimal amount, final Currency currency, final Map<UUID, BigDecimal> iias, final NextEvent... events) {
return doCallAndCheckForCompletion(new Function<Void, Payment>() {
@Override
public Payment apply(@Nullable final Void input) {
final Collection<PluginProperty> properties = new ArrayList<PluginProperty>();
final PluginProperty prop1 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_REFUND_WITH_ADJUSTMENTS, "true", false);
properties.add(prop1);
final PluginProperty prop2 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_REFUND_IDS_WITH_AMOUNT_KEY, iias, false);
properties.add(prop2);
try {
return paymentApi.createRefundWithPaymentControl(account, payment.getId(), amount, currency, UUID.randomUUID().toString(),
properties, PAYMENT_OPTIONS, callContext);
} catch (final PaymentApiException e) {
fail(e.toString());
return null;
}
}
}, events);
}
protected Payment createChargeBackAndCheckForCompletion(final Account account, final Payment payment, final NextEvent... events) {
return createChargeBackAndCheckForCompletion(account, payment, payment.getPurchasedAmount(), payment.getCurrency(), events);
}
protected Payment createChargeBackAndCheckForCompletion(final Account account, final Payment payment, final BigDecimal amount, final Currency currency, final NextEvent... events) {
return doCallAndCheckForCompletion(new Function<Void, Payment>() {
@Override
public Payment apply(@Nullable final Void input) {
try {
return paymentApi.createChargebackWithPaymentControl(account, payment.getId(), amount, currency, UUID.randomUUID().toString(),
PAYMENT_OPTIONS, callContext);
} catch (final PaymentApiException e) {
fail(e.toString());
return null;
}
}
}, events);
}
protected Payment createChargeBackReversalAndCheckForCompletion(final Account account, final Payment payment, final NextEvent... events) {
final PaymentTransaction chargeback = Iterables.<PaymentTransaction>find(Lists.<PaymentTransaction>reverse(payment.getTransactions()),
new Predicate<PaymentTransaction>() {
@Override
public boolean apply(final PaymentTransaction input) {
return TransactionType.CHARGEBACK.equals(input.getTransactionType()) &&
TransactionStatus.SUCCESS.equals(input.getTransactionStatus());
}
});
return createChargeBackReversalAndCheckForCompletion(account, payment, chargeback.getExternalKey(), events);
}
protected Payment createChargeBackReversalAndCheckForCompletion(final Account account, final Payment payment, final String chargebackTransactionExternalKey, final NextEvent... events) {
return doCallAndCheckForCompletion(new Function<Void, Payment>() {
@Override
public Payment apply(@Nullable final Void input) {
try {
return paymentApi.createChargebackReversalWithPaymentControl(account, payment.getId(), chargebackTransactionExternalKey, PAYMENT_OPTIONS, callContext);
} catch (final PaymentApiException e) {
fail(e.toString());
return null;
}
}
}, events);
}
protected DefaultEntitlement createBaseEntitlementWithPriceOverrideAndCheckForCompletion(final UUID accountId,
final String bundleExternalKey,
final String productName,
final ProductCategory productCategory,
final BillingPeriod billingPeriod,
final List<PlanPhasePriceOverride> overrides,
final NextEvent... events) {
if (productCategory == ProductCategory.ADD_ON) {
throw new RuntimeException("Unxepected Call for creating ADD_ON");
}
return (DefaultEntitlement) doCallAndCheckForCompletion(new Function<Void, Entitlement>() {
@Override
public Entitlement apply(@Nullable final Void dontcare) {
try {
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, null);
final Entitlement entitlement = entitlementApi.createBaseEntitlement(accountId, spec, bundleExternalKey, overrides, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertNotNull(entitlement);
return entitlement;
} catch (final EntitlementApiException e) {
fail("Unable to create entitlement", e);
return null;
}
}
}, events);
}
protected DefaultEntitlement createBaseEntitlementAndCheckForCompletion(final UUID accountId,
final String bundleExternalKey,
final String productName,
final ProductCategory productCategory,
final BillingPeriod billingPeriod,
final NextEvent... events) {
return createBaseEntitlementWithPriceOverrideAndCheckForCompletion(accountId, bundleExternalKey, productName, productCategory, billingPeriod, null, events);
}
protected DefaultEntitlement addAOEntitlementAndCheckForCompletion(final UUID bundleId,
final String productName,
final ProductCategory productCategory,
final BillingPeriod billingPeriod,
final NextEvent... events) {
if (productCategory != ProductCategory.ADD_ON) {
throw new RuntimeException("Unexpected Call for creating a productCategory " + productCategory);
}
return (DefaultEntitlement) doCallAndCheckForCompletion(new Function<Void, Entitlement>() {
@Override
public Entitlement apply(@Nullable final Void dontcare) {
try {
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, null);
final Entitlement entitlement = entitlementApi.addEntitlement(bundleId, spec, null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
assertNotNull(entitlement);
return entitlement;
} catch (final EntitlementApiException e) {
fail(e.getMessage());
return null;
}
}
}, events);
}
protected DefaultEntitlement changeEntitlementAndCheckForCompletion(final Entitlement entitlement,
final String productName,
final BillingPeriod billingPeriod,
final String priceList,
final BillingActionPolicy billingPolicy,
final NextEvent... events) {
return (DefaultEntitlement) doCallAndCheckForCompletion(new Function<Void, Entitlement>() {
@Override
public Entitlement apply(@Nullable final Void dontcare) {
try {
// Need to fetch again to get latest CTD updated from the system
Entitlement refreshedEntitlement = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
if (billingPolicy == null) {
refreshedEntitlement = refreshedEntitlement.changePlan(new PlanSpecifier(productName, billingPeriod, priceList), null, ImmutableList.<PluginProperty>of(), callContext);
} else {
refreshedEntitlement = refreshedEntitlement.changePlanOverrideBillingPolicy(new PlanSpecifier(productName, billingPeriod, priceList), null, null, billingPolicy, ImmutableList.<PluginProperty>of(), callContext);
}
return refreshedEntitlement;
} catch (final EntitlementApiException e) {
fail(e.getMessage());
return null;
}
}
}, events);
}
protected DefaultEntitlement changeEntitlementAndCheckForCompletion(final Entitlement entitlement,
final String productName,
final BillingPeriod billingPeriod,
final BillingActionPolicy billingPolicy,
final NextEvent... events) {
return changeEntitlementAndCheckForCompletion(entitlement, productName, billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, billingPolicy, events);
}
protected DefaultEntitlement cancelEntitlementAndCheckForCompletion(final Entitlement entitlement,
final NextEvent... events) {
return cancelEntitlementAndCheckForCompletion(entitlement, null, events);
}
protected DefaultEntitlement cancelEntitlementAndCheckForCompletion(final Entitlement entitlement,
final LocalDate requestedDate,
final NextEvent... events) {
return (DefaultEntitlement) doCallAndCheckForCompletion(new Function<Void, Entitlement>() {
@Override
public Entitlement apply(@Nullable final Void dontcare) {
try {
// Need to fetch again to get latest CTD updated from the system
Entitlement refreshedEntitlement = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
refreshedEntitlement = refreshedEntitlement.cancelEntitlementWithDate(requestedDate, false, ImmutableList.<PluginProperty>of(), callContext);
return refreshedEntitlement;
} catch (final EntitlementApiException e) {
fail(e.getMessage());
return null;
}
}
}, events);
}
protected void fullyAdjustInvoiceAndCheckForCompletion(final Account account, final Invoice invoice, final NextEvent... events) {
doCallAndCheckForCompletion(new Function<Void, Void>() {
@Override
public Void apply(@Nullable final Void input) {
try {
for (final InvoiceItem invoiceItem : invoice.getInvoiceItems()) {
invoiceUserApi.insertInvoiceItemAdjustment(account.getId(),
invoice.getId(),
invoiceItem.getId(),
invoice.getInvoiceDate(),
null,
callContext);
}
} catch (final InvoiceApiException e) {
fail(e.toString());
}
return null;
}
}, events);
}
protected void fullyAdjustInvoiceItemAndCheckForCompletion(final Account account, final Invoice invoice, final int itemNb, final NextEvent... events) {
doCallAndCheckForCompletion(new Function<Void, Void>() {
@Override
public Void apply(@Nullable final Void input) {
try {
invoiceUserApi.insertInvoiceItemAdjustment(account.getId(), invoice.getId(), invoice.getInvoiceItems().get(itemNb - 1).getId(),
invoice.getInvoiceDate(), null, callContext);
} catch (final InvoiceApiException e) {
fail(e.toString());
}
return null;
}
}, events);
}
protected void add_AUTO_PAY_OFF_Tag(final UUID id, final ObjectType type) throws TagDefinitionApiException, TagApiException {
busHandler.pushExpectedEvent(NextEvent.TAG);
tagUserApi.addTag(id, type, ControlTagType.AUTO_PAY_OFF.getId(), callContext);
assertListenerStatus();
final List<Tag> tags = tagUserApi.getTagsForObject(id, type, false, callContext);
assertEquals(tags.size(), 1);
}
protected void remove_AUTO_PAY_OFF_Tag(final UUID id, final ObjectType type, final NextEvent... additionalEvents) throws TagDefinitionApiException, TagApiException {
busHandler.pushExpectedEvent(NextEvent.TAG);
busHandler.pushExpectedEvents(additionalEvents);
tagUserApi.removeTag(id, type, ControlTagType.AUTO_PAY_OFF.getId(), callContext);
assertListenerStatus();
}
private <T> T doCallAndCheckForCompletion(final Function<Void, T> f, final NextEvent... events) {
final Joiner joiner = Joiner.on(", ");
log.debug(" ************ STARTING BUS HANDLER CHECK : {} ********************", joiner.join(events));
busHandler.pushExpectedEvents(events);
final T result = f.apply(null);
assertListenerStatus();
log.debug(" ************ DONE WITH BUS HANDLER CHECK ********************");
return result;
}
protected static class TestDryRunArguments implements DryRunArguments {
private final DryRunType dryRunType;
private final PlanPhaseSpecifier spec;
private final SubscriptionEventType action;
private final UUID subscriptionId;
private final UUID bundleId;
private final LocalDate effectiveDate;
private final BillingActionPolicy billingPolicy;
public TestDryRunArguments(final DryRunType dryRunType) {
this.dryRunType = dryRunType;
this.spec = null;
this.action = null;
this.subscriptionId = null;
this.bundleId = null;
this.effectiveDate = null;
this.billingPolicy = null;
}
public TestDryRunArguments(final DryRunType dryRunType,
final String productName,
final ProductCategory category,
final BillingPeriod billingPeriod,
final String priceList,
final PhaseType phaseType,
final SubscriptionEventType action,
final UUID subscriptionId,
final UUID bundleId,
final LocalDate effectiveDate,
final BillingActionPolicy billingPolicy) {
this.dryRunType = dryRunType;
this.spec = new PlanPhaseSpecifier(productName, billingPeriod, priceList, phaseType);
this.action = action;
this.subscriptionId = subscriptionId;
this.bundleId = bundleId;
this.effectiveDate = effectiveDate;
this.billingPolicy = billingPolicy;
}
@Override
public DryRunType getDryRunType() {
return dryRunType;
}
@Override
public PlanPhaseSpecifier getPlanPhaseSpecifier() {
return spec;
}
@Override
public SubscriptionEventType getAction() {
return action;
}
@Override
public UUID getSubscriptionId() {
return subscriptionId;
}
@Override
public LocalDate getEffectiveDate() {
return effectiveDate;
}
@Override
public UUID getBundleId() {
return bundleId;
}
@Override
public BillingActionPolicy getBillingActionPolicy() {
return billingPolicy;
}
@Override
public List<PlanPhasePriceOverride> getPlanPhasePriceOverrides() {
return null;
}
}
private class TestPaymentMethodPlugin extends TestPaymentMethodPluginBase {
@Override
public List<PluginProperty> getProperties() {
final PluginProperty prop = new PluginProperty("whatever", "cool", Boolean.TRUE);
final List<PluginProperty> res = new ArrayList<PluginProperty>();
res.add(prop);
return res;
}
}
static class ConfigurableInvoiceConfig implements InvoiceConfig {
private final InvoiceConfig defaultInvoiceConfig;
private boolean isInvoicingSystemEnabled;
public ConfigurableInvoiceConfig(final InvoiceConfig defaultInvoiceConfig) {
this.defaultInvoiceConfig = defaultInvoiceConfig;
isInvoicingSystemEnabled = defaultInvoiceConfig.isInvoicingSystemEnabled();
}
@Override
public int getNumberOfMonthsInFuture() {
return defaultInvoiceConfig.getNumberOfMonthsInFuture();
}
@Override
public int getNumberOfMonthsInFuture(final InternalTenantContext tenantContext) {
return defaultInvoiceConfig.getNumberOfMonthsInFuture();
}
@Override
public boolean isSanitySafetyBoundEnabled() {
return defaultInvoiceConfig.isSanitySafetyBoundEnabled();
}
@Override
public boolean isSanitySafetyBoundEnabled(final InternalTenantContext tenantContext) {
return defaultInvoiceConfig.isSanitySafetyBoundEnabled();
}
@Override
public int getMaxDailyNumberOfItemsSafetyBound() {
return defaultInvoiceConfig.getMaxDailyNumberOfItemsSafetyBound();
}
@Override
public int getMaxDailyNumberOfItemsSafetyBound(final InternalTenantContext tenantContext) {
return defaultInvoiceConfig.getMaxDailyNumberOfItemsSafetyBound();
}
@Override
public TimeSpan getDryRunNotificationSchedule() {
return defaultInvoiceConfig.getDryRunNotificationSchedule();
}
@Override
public TimeSpan getDryRunNotificationSchedule(final InternalTenantContext tenantContext) {
return defaultInvoiceConfig.getDryRunNotificationSchedule();
}
@Override
public int getMaxRawUsagePreviousPeriod() {
return defaultInvoiceConfig.getMaxRawUsagePreviousPeriod();
}
@Override
public int getMaxRawUsagePreviousPeriod(final InternalTenantContext tenantContext) {
return defaultInvoiceConfig.getMaxRawUsagePreviousPeriod();
}
@Override
public int getMaxGlobalLockRetries() {
return defaultInvoiceConfig.getMaxGlobalLockRetries();
}
@Override
public boolean isEmailNotificationsEnabled() {
return defaultInvoiceConfig.isEmailNotificationsEnabled();
}
@Override
public boolean isInvoicingSystemEnabled() {
return isInvoicingSystemEnabled;
}
@Override
public boolean isInvoicingSystemEnabled(final InternalTenantContext tenantContext) {
return isInvoicingSystemEnabled();
}
public void setInvoicingSystemEnabled(final boolean invoicingSystemEnabled) {
isInvoicingSystemEnabled = invoicingSystemEnabled;
}
}
}