killbill-aplcache

Plugin catalogPluginApi into catalog Wite a beatrix integration

6/19/2015 11:29:06 PM

Details

diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithCatalogPlugin.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithCatalogPlugin.java
new file mode 100644
index 0000000..5fe80e5
--- /dev/null
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithCatalogPlugin.java
@@ -0,0 +1,733 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 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 java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.account.api.AccountData;
+import org.killbill.billing.api.TestApiListener.NextEvent;
+import org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.catalog.StandaloneCatalog;
+import org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride;
+import org.killbill.billing.catalog.VersionedCatalog;
+import org.killbill.billing.catalog.api.BillingActionPolicy;
+import org.killbill.billing.catalog.api.BillingAlignment;
+import org.killbill.billing.catalog.api.BillingMode;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.CatalogApiException;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.PhaseType;
+import org.killbill.billing.catalog.api.Plan;
+import org.killbill.billing.catalog.api.PlanAlignmentChange;
+import org.killbill.billing.catalog.api.PlanAlignmentCreate;
+import org.killbill.billing.catalog.api.PriceList;
+import org.killbill.billing.catalog.api.Product;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.catalog.api.Unit;
+import org.killbill.billing.catalog.api.rules.Case;
+import org.killbill.billing.catalog.api.rules.CaseBillingAlignment;
+import org.killbill.billing.catalog.api.rules.CaseCancelPolicy;
+import org.killbill.billing.catalog.api.rules.CaseChange;
+import org.killbill.billing.catalog.api.rules.CaseChangePlanAlignment;
+import org.killbill.billing.catalog.api.rules.CaseChangePlanPolicy;
+import org.killbill.billing.catalog.api.rules.CaseCreateAlignment;
+import org.killbill.billing.catalog.api.rules.CasePhase;
+import org.killbill.billing.catalog.api.rules.CasePriceList;
+import org.killbill.billing.catalog.api.rules.PlanRules;
+import org.killbill.billing.catalog.override.PriceOverride;
+import org.killbill.billing.catalog.plugin.api.StandalonePluginCatalog;
+import org.killbill.billing.catalog.plugin.api.VersionedPluginCatalog;
+import org.killbill.billing.entitlement.api.DefaultEntitlement;
+import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.invoice.plugin.api.CatalogPluginApi;
+import org.killbill.billing.osgi.api.OSGIServiceDescriptor;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.callcontext.TenantContext;
+import org.killbill.xmlloader.XMLLoader;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.io.Resources;
+
+public class TestWithCatalogPlugin extends TestIntegrationBase {
+
+    @Inject
+    private OSGIServiceRegistration<CatalogPluginApi> pluginRegistry;
+
+    @Inject
+    private PriceOverride priceOverride;
+
+    @Inject
+    private InternalCallContextFactory internalCallContextFactory;
+
+    private TestCatalogPluginApi testCatalogPluginApi;
+
+    @BeforeClass(groups = "slow")
+    public void beforeClass() throws Exception {
+        super.beforeClass();
+
+        this.testCatalogPluginApi = new TestCatalogPluginApi(priceOverride, internalCallContext, internalCallContextFactory);
+        pluginRegistry.registerService(new OSGIServiceDescriptor() {
+            @Override
+            public String getPluginSymbolicName() {
+                return "TestCatalogPluginApi";
+            }
+
+            @Override
+            public String getRegistrationName() {
+                return "TestCatalogPluginApi";
+            }
+        }, testCatalogPluginApi);
+    }
+
+    @Test(groups = "slow")
+    public void testCreateSubscriptionWithCatalogPlugin() throws Exception {
+
+        final AccountData accountData = getAccountData(1);
+        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+        accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+        // We take april as it has 30 days (easier to play with BCD)
+        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+        clock.setDay(new LocalDate(2012, 4, 1));
+
+        //
+        // Create original subscription (Trial PHASE) -> $0 invoice.
+        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        subscriptionChecker.checkSubscriptionCreated(bpSubscription.getId(), internalCallContext);
+    }
+
+    public static class TestCatalogPluginApi implements CatalogPluginApi {
+
+        private final VersionedCatalog versionedCatalog;
+
+        public TestCatalogPluginApi(final PriceOverride priceOverride, final InternalTenantContext internalTenantContext, final InternalCallContextFactory internalCallContextFactory) throws Exception {
+            final StandaloneCatalog inputCatalog = XMLLoader.getObjectFromString(Resources.getResource("WeaponsHire.xml").toExternalForm(), StandaloneCatalog.class);
+            final List<StandaloneCatalogWithPriceOverride> versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
+            final StandaloneCatalogWithPriceOverride standaloneCatalogWithPriceOverride = new StandaloneCatalogWithPriceOverride(inputCatalog, priceOverride, internalTenantContext.getTenantRecordId(), internalCallContextFactory);
+            versions.add(standaloneCatalogWithPriceOverride);
+            versionedCatalog = new VersionedCatalog(getClock(), inputCatalog.getCatalogName(), inputCatalog.getRecurringBillingMode(), versions, internalTenantContext);
+        }
+
+        @Override
+        public VersionedPluginCatalog getVersionedPluginCatalog(final Iterable<PluginProperty> properties, final TenantContext tenantContext) {
+            return new TestVersionedPluginCatalog(versionedCatalog.getCatalogName(), versionedCatalog.getRecurringBillingMode(), toStandalonePluginCatalogs(versionedCatalog.getVersions()));
+        }
+
+        private Iterable<StandalonePluginCatalog> toStandalonePluginCatalogs(final List<StandaloneCatalogWithPriceOverride> input) {
+            return Iterables.transform(input, new Function<StandaloneCatalogWithPriceOverride, StandalonePluginCatalog>() {
+                @Override
+                public StandalonePluginCatalog apply(final StandaloneCatalogWithPriceOverride input) {
+                    try {
+                        return new TestStandalonePluginCatalog(new DateTime(input.getEffectiveDate()),
+                                                               ImmutableList.copyOf(input.getCurrentSupportedCurrencies()),
+                                                               ImmutableList.<Product>copyOf(input.getCurrentProducts()),
+                                                               ImmutableList.<Plan>copyOf(input.getCurrentPlans()),
+                                                               input.getStandaloneCatalog().getPriceLists().getDefaultPricelist(),
+                                                               ImmutableList.<PriceList>copyOf(input.getStandaloneCatalog().getPriceLists().getChildPriceLists()),
+                                                               input.getStandaloneCatalog().getPlanRules(),
+                                                               null /*ImmutableList.<Unit>copyOf(input.getStandaloneCatalog().getCurrentUnits()) */);
+                    } catch (CatalogApiException e) {
+                        throw new IllegalStateException(e);
+                    }
+                }
+
+                private <I, C extends I> List<I> listOf(@Nullable C [] input) {
+                    return (input != null) ? ImmutableList.<I>copyOf(input) : ImmutableList.<I>of();
+                }
+            });
+        }
+    }
+
+    private static final class TestVersionedPluginCatalog implements VersionedPluginCatalog {
+
+        private final String catalogName;
+
+        private final BillingMode billingMode;
+
+        private final Iterable<StandalonePluginCatalog> standalonePluginCatalogs;
+
+        public TestVersionedPluginCatalog(final String catalogName,
+                                          final BillingMode billingMode,
+                                          final Iterable<StandalonePluginCatalog> standalonePluginCatalogs) {
+            this.catalogName = catalogName;
+            this.billingMode = billingMode;
+            this.standalonePluginCatalogs = standalonePluginCatalogs;
+        }
+
+        @Override
+        public String getCatalogName() {
+            return catalogName;
+        }
+
+        @Override
+        public BillingMode getRecurringBillingMode() {
+            return billingMode;
+        }
+
+        @Override
+        public Iterable<StandalonePluginCatalog> getStandalonePluginCatalogs() {
+            return standalonePluginCatalogs;
+        }
+    }
+
+    private static class TestStandalonePluginCatalog implements StandalonePluginCatalog {
+
+        private final DateTime effectiveDate;
+
+        private final Iterable<Currency> currency;
+
+        private final Iterable<Product> products;
+
+        private final Iterable<Plan> plans;
+
+        private final PriceList defaultPriceList;
+
+        private final Iterable<PriceList> childrenPriceLists;
+
+        private final PlanRules planRules;
+
+        private final Iterable<Unit> units;
+
+        public TestStandalonePluginCatalog(final DateTime effectiveDate,
+                                           final Iterable<Currency> currency,
+                                           final Iterable<Product> products,
+                                           final Iterable<Plan> plans,
+                                           final PriceList defaultPriceList,
+                                           final Iterable<PriceList> childrenPriceLists,
+                                           final PlanRules planRules,
+                                           final Iterable<Unit> units) {
+            this.effectiveDate = effectiveDate;
+            this.currency = currency;
+            this.products = products;
+            this.plans = plans;
+            this.defaultPriceList = defaultPriceList;
+            this.childrenPriceLists = childrenPriceLists;
+            this.planRules = planRules;
+            this.units = units;
+        }
+
+        @Override
+        public DateTime getEffectiveDate() {
+            return effectiveDate;
+        }
+
+        @Override
+        public Iterable<Currency> getCurrencies() {
+            return currency;
+        }
+
+        @Override
+        public Iterable<Unit> getUnits() {
+            return units;
+        }
+
+        @Override
+        public Iterable<Product> getProducts() {
+            return products;
+        }
+
+        @Override
+        public Iterable<Plan> getPlans() {
+            return plans;
+        }
+
+        @Override
+        public PriceList getDefaultPriceList() {
+            return defaultPriceList;
+        }
+
+        @Override
+        public Iterable<PriceList> getChildrenPriceList() {
+            return childrenPriceLists;
+        }
+
+        @Override
+        public PlanRules getPlanRules() {
+            return planRules;
+        }
+    }
+
+    private static final class TestPlanRules implements PlanRules {
+
+        private final Iterable<Product> products;
+        private final Iterable<Plan> plans;
+        private final Iterable<PriceList> priceLists;
+
+        private final List<CaseChangePlanPolicy> caseChangePlanPolicy;
+        private final List<CaseChangePlanAlignment> caseChangePlanAlignment;
+        private final List<CaseCancelPolicy> caseCancelPolicy;
+        private final List<CaseCreateAlignment> caseCreateAlignment;
+        private final List<CaseBillingAlignment> caseBillingAlignments;
+        private final List<CasePriceList> casePriceList;
+
+        public TestPlanRules(final Iterable<Product> products, Iterable<Plan> plans, Iterable<PriceList> priceLists) {
+            this.products = products;
+            this.plans = plans;
+            this.priceLists = priceLists;
+            this.caseChangePlanPolicy = new ArrayList<CaseChangePlanPolicy>();
+            this.caseChangePlanAlignment = new ArrayList<CaseChangePlanAlignment>();
+            this.caseCancelPolicy = new ArrayList<CaseCancelPolicy>();
+            this.caseCreateAlignment = new ArrayList<CaseCreateAlignment>();
+            this.caseBillingAlignments = new ArrayList<CaseBillingAlignment>();
+            this.casePriceList = new ArrayList<CasePriceList>();
+        }
+
+        public void addCaseBillingAlignmentRule(final CaseBillingAlignment input) {
+            caseBillingAlignments.add(input);
+        }
+
+        public void addCaseBillingAlignmentRule(final Product product,
+                                                final ProductCategory productCategory,
+                                                final BillingPeriod billingPeriod,
+                                                final PriceList priceList,
+                                                final PhaseType phaseType,
+                                                final BillingAlignment billingAlignment) {
+            caseBillingAlignments.add(new TestCaseBillingAlignment(product, productCategory, billingPeriod, priceList, phaseType, billingAlignment));
+        }
+
+        public void addCaseCancelRule(final CaseCancelPolicy input) {
+            caseCancelPolicy.add(input);
+        }
+
+        public void addCaseCancelRule(final Product product,
+                                      final ProductCategory productCategory,
+                                      final BillingPeriod billingPeriod,
+                                      final PriceList priceList,
+                                      final PhaseType phaseType,
+                                      final BillingActionPolicy billingActionPolicy) {
+            caseCancelPolicy.add(new TestCaseCancelPolicy(product, productCategory, billingPeriod, priceList, phaseType, billingActionPolicy));
+
+        }
+
+        public void addCaseChangeAlignmentRule(final CaseChangePlanAlignment input) {
+            caseChangePlanAlignment.add(input);
+        }
+
+        public void addCaseChangeAlignmentRule(final PhaseType phaseType,
+                                               final String fromProduct,
+                                               final ProductCategory fromProductCategory,
+                                               final BillingPeriod fromBillingPeriod,
+                                               final String fromPriceList,
+                                               final String toProduct,
+                                               final ProductCategory toProductCategory,
+                                               final BillingPeriod toBillingPeriod,
+                                               final String toPriceList,
+                                               final PlanAlignmentChange planAlignmentChange) {
+            caseChangePlanAlignment.add(new TestCaseChangePlanAlignment(phaseType, findProduct(fromProduct), fromProductCategory, fromBillingPeriod, findPriceList(fromPriceList), findProduct(toProduct), toProductCategory, toBillingPeriod, findPriceList(toPriceList), planAlignmentChange));
+        }
+
+        public void addCaseChangePlanPolicyRule(final CaseChangePlanPolicy input) {
+            caseChangePlanPolicy.add(input);
+        }
+
+        public void addCaseChangePlanPolicyRule(final PhaseType phaseType,
+                                                final String fromProduct,
+                                                final ProductCategory fromProductCategory,
+                                                final BillingPeriod fromBillingPeriod,
+                                                final String fromPriceList,
+                                                final String toProduct,
+                                                final ProductCategory toProductCategory,
+                                                final BillingPeriod toBillingPeriod,
+                                                final String toPriceList,
+                                                final BillingActionPolicy policy) {
+            caseChangePlanPolicy.add(new TestCaseChangePlanPolicy(phaseType, findProduct(fromProduct), fromProductCategory, fromBillingPeriod, findPriceList(fromPriceList), findProduct(toProduct), toProductCategory, toBillingPeriod, findPriceList(toPriceList), policy));
+        }
+
+        public void addCaseCreateAlignmentRule(final CaseCreateAlignment input) {
+            caseCreateAlignment.add(input);
+        }
+
+        public void addCaseCreateAlignmentRule(final Product product,
+                                               final ProductCategory productCategory,
+                                               final BillingPeriod billingPeriod,
+                                               final PriceList priceList,
+                                               final PlanAlignmentCreate planAlignmentCreate) {
+            caseCreateAlignment.add(new TestCaseCreateAlignment(product, productCategory, billingPeriod, priceList, planAlignmentCreate));
+        }
+
+        public void addPriceListRule(final CasePriceList input) {
+            casePriceList.add(input);
+        }
+
+        public void addPriceListRule(final Product product,
+                                     final ProductCategory productCategory,
+                                     final BillingPeriod billingPeriod,
+                                     final PriceList priceList,
+                                     final PriceList destPriceList) {
+            casePriceList.add(new TestCasePriceList(product, productCategory, billingPeriod, priceList, destPriceList));
+        }
+
+        @Override
+        public Iterable<CaseChangePlanPolicy> getCaseChangePlanPolicy() {
+            return caseChangePlanPolicy;
+        }
+
+        @Override
+        public Iterable<CaseChangePlanAlignment> getCaseChangePlanAlignment() {
+            return caseChangePlanAlignment;
+        }
+
+        @Override
+        public Iterable<CaseCancelPolicy> getCaseCancelPolicy() {
+            return caseCancelPolicy;
+        }
+
+        @Override
+        public Iterable<CaseCreateAlignment> getCaseCreateAlignment() {
+            return caseCreateAlignment;
+        }
+
+        @Override
+        public Iterable<CaseBillingAlignment> getCaseBillingAlignment() {
+            return caseBillingAlignments;
+        }
+
+        @Override
+        public Iterable<CasePriceList> getCasePriceList() {
+            return casePriceList;
+        }
+
+        private Product findProduct(@Nullable final String productName) {
+            return find(products, productName, "products", new Predicate<Product>() {
+                @Override
+                public boolean apply(final Product input) {
+                    return input.getName().equals(productName);
+                }
+            });
+        }
+
+        private Plan findPlan(@Nullable final String planName) {
+            return find(plans, planName, "plans", new Predicate<Plan>() {
+                @Override
+                public boolean apply(final Plan input) {
+                    return input.getName().equals(planName);
+                }
+            });
+        }
+
+        private PriceList findPriceList(@Nullable final String priceListName) {
+            return find(priceLists, priceListName, "pricelists", new Predicate<PriceList>() {
+                @Override
+                public boolean apply(final PriceList input) {
+                    return input.getName().equals(priceListName);
+                }
+            });
+        }
+
+        private <T> T find(final Iterable<T> all, @Nullable final String name, final String what, final Predicate<T> predicate) {
+            if (name == null) {
+                return null;
+            }
+            final T result = Iterables.tryFind(all, predicate).orNull();
+            if (result == null) {
+                throw new IllegalStateException(String.format("%s : cannot find entry %s", what, name));
+            }
+            return result;
+        }
+    }
+
+    public static class TestCasePriceList extends TestCase implements CasePriceList {
+
+        private final PriceList destPriceList;
+
+        public TestCasePriceList(final Product product,
+                                 final ProductCategory productCategory,
+                                 final BillingPeriod billingPeriod,
+                                 final PriceList priceList,
+                                 final PriceList destPriceList) {
+            super(product, productCategory, billingPeriod, priceList);
+            this.destPriceList = destPriceList;
+        }
+
+        @Override
+        public PriceList getDestinationPriceList() {
+            return destPriceList;
+        }
+    }
+
+    public static class TestCaseCreateAlignment extends TestCase implements CaseCreateAlignment {
+
+        private final PlanAlignmentCreate planAlignmentCreate;
+
+        public TestCaseCreateAlignment(final Product product,
+                                       final ProductCategory productCategory,
+                                       final BillingPeriod billingPeriod,
+                                       final PriceList priceList,
+                                       final PlanAlignmentCreate planAlignmentCreate) {
+            super(product, productCategory, billingPeriod, priceList);
+            this.planAlignmentCreate = planAlignmentCreate;
+        }
+
+        @Override
+        public PlanAlignmentCreate getPlanAlignmentCreate() {
+            return planAlignmentCreate;
+        }
+    }
+
+    public static class TestCaseCancelPolicy extends TestCasePhase implements CaseCancelPolicy {
+
+        private final BillingActionPolicy billingActionPolicy;
+
+        public TestCaseCancelPolicy(final Product product,
+                                    final ProductCategory productCategory,
+                                    final BillingPeriod billingPeriod,
+                                    final PriceList priceList,
+                                    final PhaseType phaseType,
+                                    final BillingActionPolicy billingActionPolicy) {
+            super(product, productCategory, billingPeriod, priceList, phaseType);
+            this.billingActionPolicy = billingActionPolicy;
+        }
+
+        @Override
+        public BillingActionPolicy getBillingActionPolicy() {
+            return billingActionPolicy;
+        }
+    }
+
+    public static class TestCaseBillingAlignment extends TestCasePhase implements CaseBillingAlignment {
+
+        private final BillingAlignment billingAlignment;
+
+        public TestCaseBillingAlignment(final Product product,
+                                        final ProductCategory productCategory,
+                                        final BillingPeriod billingPeriod,
+                                        final PriceList priceList,
+                                        final PhaseType phaseType,
+                                        final BillingAlignment billingAlignment) {
+            super(product, productCategory, billingPeriod, priceList, phaseType);
+            this.billingAlignment = billingAlignment;
+        }
+
+        @Override
+        public BillingAlignment getBillingAlignment() {
+            return billingAlignment;
+        }
+    }
+
+    public static class TestCasePhase extends TestCase implements CasePhase {
+
+        private final PhaseType phaseType;
+
+        public TestCasePhase(final Product product,
+                             final ProductCategory productCategory,
+                             final BillingPeriod billingPeriod,
+                             final PriceList priceList,
+                             final PhaseType phaseType) {
+            super(product, productCategory, billingPeriod, priceList);
+            this.phaseType = phaseType;
+        }
+
+        @Override
+        public PhaseType getPhaseType() {
+            return phaseType;
+        }
+    }
+
+    public static class TestCase implements Case {
+
+        private final Product product;
+
+        private final ProductCategory productCategory;
+
+        private final BillingPeriod billingPeriod;
+
+        private final PriceList priceList;
+
+        public TestCase(final Product product,
+                        final ProductCategory productCategory,
+                        final BillingPeriod billingPeriod,
+                        final PriceList priceList) {
+            this.product = product;
+            this.productCategory = productCategory;
+            this.billingPeriod = billingPeriod;
+            this.priceList = priceList;
+        }
+
+        @Override
+        public Product getProduct() {
+            return product;
+        }
+
+        @Override
+        public ProductCategory getProductCategory() {
+            return productCategory;
+        }
+
+        @Override
+        public BillingPeriod getBillingPeriod() {
+            return billingPeriod;
+        }
+
+        @Override
+        public PriceList getPriceList() {
+            return priceList;
+        }
+    }
+
+    public static class TestCaseChangePlanAlignment extends TestCaseChange implements CaseChangePlanAlignment {
+
+        private final PlanAlignmentChange planAlignmentChange;
+
+        public TestCaseChangePlanAlignment(final PhaseType phaseType,
+                                           final Product fromProduct,
+                                           final ProductCategory fromProductCategory,
+                                           final BillingPeriod fromBillingPeriod,
+                                           final PriceList fromPriceList,
+                                           final Product toProduct,
+                                           final ProductCategory toProductCategory,
+                                           final BillingPeriod toBillingPeriod,
+                                           final PriceList toPriceList,
+                                           final PlanAlignmentChange planAlignmentChange) {
+            super(phaseType, fromProduct, fromProductCategory, fromBillingPeriod, fromPriceList, toProduct, toProductCategory, toBillingPeriod, toPriceList);
+            this.planAlignmentChange = planAlignmentChange;
+        }
+
+        @Override
+        public PlanAlignmentChange getAlignment() {
+            return planAlignmentChange;
+        }
+    }
+
+    public static class TestCaseChangePlanPolicy extends TestCaseChange implements CaseChangePlanPolicy {
+
+        private final BillingActionPolicy billingPolicy;
+
+        public TestCaseChangePlanPolicy(final PhaseType phaseType,
+                                        final Product fromProduct,
+                                        final ProductCategory fromProductCategory,
+                                        final BillingPeriod fromBillingPeriod,
+                                        final PriceList fromPriceList,
+                                        final Product toProduct,
+                                        final ProductCategory toProductCategory,
+                                        final BillingPeriod toBillingPeriod,
+                                        final PriceList toPriceList,
+                                        final BillingActionPolicy billingPolicy) {
+            super(phaseType, fromProduct, fromProductCategory, fromBillingPeriod, fromPriceList, toProduct, toProductCategory, toBillingPeriod, toPriceList);
+            this.billingPolicy = billingPolicy;
+        }
+
+        @Override
+        public BillingActionPolicy getBillingActionPolicy() {
+            return billingPolicy;
+        }
+    }
+
+    public static class TestCaseChange implements CaseChange {
+
+        private final PhaseType phaseType;
+
+        private final Product fromProduct;
+
+        private final ProductCategory fromProductCategory;
+
+        private final BillingPeriod fromBillingPeriod;
+
+        private final PriceList fromPriceList;
+
+        private final Product toProduct;
+
+        private final ProductCategory toProductCategory;
+
+        private final BillingPeriod toBillingPeriod;
+
+        private final PriceList toPriceList;
+
+        public TestCaseChange(final PhaseType phaseType,
+                              final Product fromProduct,
+                              final ProductCategory fromProductCategory,
+                              final BillingPeriod fromBillingPeriod,
+                              final PriceList fromPriceList,
+                              final Product toProduct,
+                              final ProductCategory toProductCategory,
+                              final BillingPeriod toBillingPeriod,
+                              final PriceList toPriceList) {
+            this.phaseType = phaseType;
+            this.fromProduct = fromProduct;
+            this.fromProductCategory = fromProductCategory;
+            this.fromBillingPeriod = fromBillingPeriod;
+            this.fromPriceList = fromPriceList;
+            this.toProduct = toProduct;
+            this.toProductCategory = toProductCategory;
+            this.toBillingPeriod = toBillingPeriod;
+            this.toPriceList = toPriceList;
+        }
+
+        @Override
+        public PhaseType getPhaseType() {
+            return phaseType;
+        }
+
+        @Override
+        public Product getFromProduct() {
+            return fromProduct;
+        }
+
+        @Override
+        public ProductCategory getFromProductCategory() {
+            return fromProductCategory;
+        }
+
+        @Override
+        public BillingPeriod getFromBillingPeriod() {
+            return fromBillingPeriod;
+        }
+
+        @Override
+        public PriceList getFromPriceList() {
+            return fromPriceList;
+        }
+
+        @Override
+        public Product getToProduct() {
+            return toProduct;
+        }
+
+        @Override
+        public ProductCategory getToProductCategory() {
+            return toProductCategory;
+        }
+
+        @Override
+        public BillingPeriod getToBillingPeriod() {
+            return toBillingPeriod;
+        }
+
+        @Override
+        public PriceList getToPriceList() {
+            return toPriceList;
+        }
+    }
+
+}

catalog/pom.xml 4(+4 -0)

diff --git a/catalog/pom.xml b/catalog/pom.xml
index 0978f4a..f6dd499 100644
--- a/catalog/pom.xml
+++ b/catalog/pom.xml
@@ -68,6 +68,10 @@
         </dependency>
         <dependency>
             <groupId>org.kill-bill.billing</groupId>
+            <artifactId>killbill-platform-osgi-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.billing</groupId>
             <artifactId>killbill-platform-test</artifactId>
             <scope>test</scope>
         </dependency>
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheCatalogCache.java b/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheCatalogCache.java
index ef3806d..8c8ccdb 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheCatalogCache.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheCatalogCache.java
@@ -27,16 +27,23 @@ import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.VersionedCatalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.io.VersionedCatalogLoader;
+import org.killbill.billing.catalog.plugin.VersionedCatalogMapper;
+import org.killbill.billing.catalog.plugin.api.VersionedPluginCatalog;
+import org.killbill.billing.invoice.plugin.api.CatalogPluginApi;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.util.cache.Cachable.CacheType;
 import org.killbill.billing.util.cache.CacheController;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.cache.CacheLoaderArgument;
 import org.killbill.billing.util.cache.TenantCatalogCacheLoader.LoaderCallback;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.callcontext.TenantContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
 
 public class EhCacheCatalogCache implements CatalogCache {
 
@@ -45,13 +52,23 @@ public class EhCacheCatalogCache implements CatalogCache {
     private final CacheController cacheController;
     private final VersionedCatalogLoader loader;
     private final CacheLoaderArgument cacheLoaderArgument;
+    private final OSGIServiceRegistration<CatalogPluginApi> pluginRegistry;
+    private final VersionedCatalogMapper versionedCatalogMapper;
+    private final InternalCallContextFactory internalCallContextFactory;
 
     private VersionedCatalog defaultCatalog;
 
     @Inject
-    public EhCacheCatalogCache(final CacheControllerDispatcher cacheControllerDispatcher, final VersionedCatalogLoader loader) {
+    public EhCacheCatalogCache(final OSGIServiceRegistration<CatalogPluginApi> pluginRegistry,
+                               final VersionedCatalogMapper versionedCatalogMapper,
+                               final CacheControllerDispatcher cacheControllerDispatcher,
+                               final VersionedCatalogLoader loader,
+                               final InternalCallContextFactory internalCallContextFactory) {
+        this.pluginRegistry = pluginRegistry;
+        this.versionedCatalogMapper = versionedCatalogMapper;
         this.cacheController = cacheControllerDispatcher.getCacheController(CacheType.TENANT_CATALOG);
         this.loader = loader;
+        this.internalCallContextFactory = internalCallContextFactory;
         this.cacheLoaderArgument = initializeCacheLoaderArgument(this);
         setDefaultCatalog();
     }
@@ -65,6 +82,13 @@ public class EhCacheCatalogCache implements CatalogCache {
 
     @Override
     public VersionedCatalog getCatalog(final InternalTenantContext tenantContext) throws CatalogApiException {
+
+        // STEPH TODO what are the possibilities for caching here ?
+        final VersionedCatalog pluginVersionedCatalog = getCatalogFromPlugins(tenantContext);
+        if (pluginVersionedCatalog != null) {
+            return pluginVersionedCatalog;
+        }
+
         if (tenantContext.getTenantRecordId() == InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID) {
             return defaultCatalog;
         }
@@ -75,7 +99,7 @@ public class EhCacheCatalogCache implements CatalogCache {
             // It means we are using a default catalog in a multi-tenant deployment, that does not really match a real use case, but we want to support it
             // for test purpose.
             if (tenantCatalog == null) {
-                tenantCatalog = new VersionedCatalog(defaultCatalog, tenantContext);
+                tenantCatalog = new VersionedCatalog(defaultCatalog.getClock(), defaultCatalog.getCatalogName(), defaultCatalog.getRecurringBillingMode(), defaultCatalog.getVersions(), tenantContext);
                 cacheController.add(tenantContext.getTenantRecordId(), tenantCatalog);
             }
             return tenantCatalog;
@@ -91,6 +115,20 @@ public class EhCacheCatalogCache implements CatalogCache {
         }
     }
 
+    private VersionedCatalog getCatalogFromPlugins(final InternalTenantContext internalTenantContext) {
+        final TenantContext tenantContext = internalCallContextFactory.createTenantContext(internalTenantContext);
+        for (final String service : pluginRegistry.getAllServices()) {
+            final CatalogPluginApi plugin = pluginRegistry.getServiceForName(service);
+            final VersionedPluginCatalog pluginCatalog = plugin.getVersionedPluginCatalog(ImmutableList.<PluginProperty>of(), tenantContext);
+            // First plugin that gets something (for that tenant) returns it
+            if (pluginCatalog != null) {
+                logger.info("Returning catalog from plugin {} on tenant {} ", service, internalTenantContext.getTenantRecordId());
+                return versionedCatalogMapper.toVersionedCatalog(pluginCatalog, internalTenantContext);
+            }
+        }
+        return null;
+    }
+
     //
     // Build the LoaderCallback that is required to build the catalog from the xml from a module that knows
     // nothing about catalog.
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/glue/CatalogModule.java b/catalog/src/main/java/org/killbill/billing/catalog/glue/CatalogModule.java
index 4e93f86..f6100e9 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/glue/CatalogModule.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/glue/CatalogModule.java
@@ -33,12 +33,18 @@ import org.killbill.billing.catalog.io.CatalogLoader;
 import org.killbill.billing.catalog.io.VersionedCatalogLoader;
 import org.killbill.billing.catalog.override.DefaultPriceOverride;
 import org.killbill.billing.catalog.override.PriceOverride;
+import org.killbill.billing.catalog.plugin.StandaloneCatalogMapper;
+import org.killbill.billing.catalog.plugin.VersionedCatalogMapper;
+import org.killbill.billing.invoice.plugin.api.CatalogPluginApi;
+import org.killbill.billing.invoice.plugin.api.InvoicePluginApi;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
 import org.killbill.billing.util.config.CatalogConfig;
 import org.killbill.billing.util.glue.KillBillModule;
 import org.skife.config.ConfigurationObjectFactory;
 
+import com.google.inject.TypeLiteral;
 import com.google.inject.name.Names;
 
 public class CatalogModule extends KillBillModule {
@@ -75,6 +81,10 @@ public class CatalogModule extends KillBillModule {
         bind(OverriddenPlanCache.class).to(EhCacheOverriddenPlanCache.class).asEagerSingleton();
     }
 
+    protected void installCatalogPluginApi() {
+        bind(new TypeLiteral<OSGIServiceRegistration<CatalogPluginApi>>() {}).toProvider(DefaultCatalogProviderPluginRegistryProvider.class).asEagerSingleton();
+        bind(VersionedCatalogMapper.class).asEagerSingleton();
+    }
 
 
     @Override
@@ -84,5 +94,6 @@ public class CatalogModule extends KillBillModule {
         installCatalog();
         installCatalogUserApi();
         installCatalogConfigCache();
+        installCatalogPluginApi();
     }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/glue/DefaultCatalogProviderPluginRegistryProvider.java b/catalog/src/main/java/org/killbill/billing/catalog/glue/DefaultCatalogProviderPluginRegistryProvider.java
new file mode 100644
index 0000000..640ed2e
--- /dev/null
+++ b/catalog/src/main/java/org/killbill/billing/catalog/glue/DefaultCatalogProviderPluginRegistryProvider.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 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.catalog.glue;
+
+import org.killbill.billing.catalog.provider.DefaultCatalogProviderPluginRegistry;
+import org.killbill.billing.invoice.plugin.api.CatalogPluginApi;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+public class DefaultCatalogProviderPluginRegistryProvider implements Provider<OSGIServiceRegistration<CatalogPluginApi>> {
+
+    @Inject
+    public DefaultCatalogProviderPluginRegistryProvider() {
+    }
+
+    @Override
+    public OSGIServiceRegistration<CatalogPluginApi> get() {
+        final DefaultCatalogProviderPluginRegistry pluginRegistry = new DefaultCatalogProviderPluginRegistry();
+        return pluginRegistry;
+    }
+}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/plugin/StandaloneCatalogMapper.java b/catalog/src/main/java/org/killbill/billing/catalog/plugin/StandaloneCatalogMapper.java
index b046eb6..e70f9d1 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/plugin/StandaloneCatalogMapper.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/plugin/StandaloneCatalogMapper.java
@@ -494,7 +494,7 @@ public class StandaloneCatalogMapper {
     }
 
     private <I, C extends I> C[] toArrayWithTransform(final Iterable<I> input, final Function<I, C> transformer, boolean returnNullIfNothing) {
-        if (returnNullIfNothing && !input.iterator().hasNext()) {
+        if (returnNullIfNothing && (input == null || !input.iterator().hasNext())) {
             return null;
         }
         final Iterable<C> tmp = Iterables.transform(input, transformer);
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/plugin/VersionedCatalogMapper.java b/catalog/src/main/java/org/killbill/billing/catalog/plugin/VersionedCatalogMapper.java
new file mode 100644
index 0000000..39ec3fa
--- /dev/null
+++ b/catalog/src/main/java/org/killbill/billing/catalog/plugin/VersionedCatalogMapper.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 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.catalog.plugin;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.catalog.StandaloneCatalog;
+import org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride;
+import org.killbill.billing.catalog.VersionedCatalog;
+import org.killbill.billing.catalog.override.PriceOverride;
+import org.killbill.billing.catalog.plugin.api.StandalonePluginCatalog;
+import org.killbill.billing.catalog.plugin.api.VersionedPluginCatalog;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.clock.Clock;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class VersionedCatalogMapper {
+
+    private final Clock clock;
+
+    private final PriceOverride priceOverride;
+    private final InternalCallContextFactory internalCallContextFactory;
+
+    @Inject
+    public VersionedCatalogMapper(final Clock clock, final PriceOverride priceOverride, final InternalCallContextFactory internalCallContextFactory) {
+        this.clock = clock;
+        this.priceOverride = priceOverride;
+        this.internalCallContextFactory = internalCallContextFactory;
+    }
+
+    public VersionedCatalog toVersionedCatalog(final VersionedPluginCatalog pluginCatalog, final InternalTenantContext internalTenantContext) {
+        final VersionedCatalog result = new VersionedCatalog(clock, pluginCatalog.getCatalogName(), pluginCatalog.getRecurringBillingMode(), toStandaloneCatalogWithPriceOverrideList(pluginCatalog, internalTenantContext), internalTenantContext);
+        return result;
+    }
+
+    private List<StandaloneCatalogWithPriceOverride> toStandaloneCatalogWithPriceOverrideList(final VersionedPluginCatalog pluginCatalog, final InternalTenantContext internalTenantContext) {
+        return ImmutableList.copyOf(Iterables.transform(pluginCatalog.getStandalonePluginCatalogs(), new Function<StandalonePluginCatalog, StandaloneCatalogWithPriceOverride>() {
+            @Override
+            public StandaloneCatalogWithPriceOverride apply(final StandalonePluginCatalog input) {
+                return toStandaloneCatalogWithPriceOverride(pluginCatalog, input, internalTenantContext);
+            }
+        }));
+    }
+
+    private StandaloneCatalogWithPriceOverride toStandaloneCatalogWithPriceOverride(final VersionedPluginCatalog pluginCatalog, final StandalonePluginCatalog input, final InternalTenantContext internalTenantContext) {
+        final StandaloneCatalogMapper mapper = new StandaloneCatalogMapper(pluginCatalog.getCatalogName(), pluginCatalog.getRecurringBillingMode());
+        final StandaloneCatalog standaloneCatalog = mapper.toStandaloneCatalog(input, null);
+        final StandaloneCatalogWithPriceOverride result = new StandaloneCatalogWithPriceOverride(standaloneCatalog, priceOverride, internalTenantContext.getTenantRecordId(), internalCallContextFactory);
+        return result;
+    }
+}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/provider/DefaultCatalogProviderPluginRegistry.java b/catalog/src/main/java/org/killbill/billing/catalog/provider/DefaultCatalogProviderPluginRegistry.java
new file mode 100644
index 0000000..017c0d4
--- /dev/null
+++ b/catalog/src/main/java/org/killbill/billing/catalog/provider/DefaultCatalogProviderPluginRegistry.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 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.catalog.provider;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.inject.Inject;
+
+import org.killbill.billing.invoice.plugin.api.CatalogPluginApi;
+import org.killbill.billing.osgi.api.OSGIServiceDescriptor;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DefaultCatalogProviderPluginRegistry implements OSGIServiceRegistration<CatalogPluginApi> {
+
+    private final static Logger log = LoggerFactory.getLogger(DefaultCatalogProviderPluginRegistry.class);
+
+    private final Map<String, CatalogPluginApi> pluginsByName = new ConcurrentHashMap<String, CatalogPluginApi>();
+
+    @Inject
+    public DefaultCatalogProviderPluginRegistry() {
+    }
+
+    @Override
+    public void registerService(final OSGIServiceDescriptor desc, final CatalogPluginApi service) {
+        log.info("DefaultInvoiceProviderPluginRegistry registering service " + desc.getRegistrationName());
+        pluginsByName.put(desc.getRegistrationName(), service);
+    }
+
+    @Override
+    public void unregisterService(final String serviceName) {
+        log.info("DefaultInvoiceProviderPluginRegistry unregistering service " + serviceName);
+        pluginsByName.remove(serviceName);
+    }
+
+    @Override
+    public CatalogPluginApi getServiceForName(final String serviceName) {
+        if (serviceName == null) {
+            throw new IllegalArgumentException("Null catalog plugin API name");
+        }
+        final CatalogPluginApi plugin = pluginsByName.get(serviceName);
+        return plugin;
+    }
+
+    @Override
+    public Set<String> getAllServices() {
+        return pluginsByName.keySet();
+    }
+
+    @Override
+    public Class<CatalogPluginApi> getServiceType() {
+        return CatalogPluginApi.class;
+    }
+}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java b/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
index 38e9181..5c4a565 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
@@ -69,26 +69,42 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
     private BillingMode recurringBillingMode;
 
     @XmlElement(name = "catalogVersion", required = true)
-    private final List<StandaloneCatalogWithPriceOverride> versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
+    private List<StandaloneCatalogWithPriceOverride> versions;
 
     // Required for JAXB deserialization
     public VersionedCatalog() {
         this.clock = null;
+        versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
     }
 
     public VersionedCatalog(final Clock clock) {
         this.clock = clock;
+        versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
     }
 
+    public VersionedCatalog(final Clock clock, final String catalogName, final BillingMode recurringBillingMode, final List<StandaloneCatalogWithPriceOverride> versions, final InternalTenantContext tenantContext) {
+        this.clock = clock;
+        this.catalogName = catalogName;
+        this.recurringBillingMode = recurringBillingMode;
+        this.versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
+        for (final StandaloneCatalogWithPriceOverride cur : versions) {
+            final StandaloneCatalogWithPriceOverride catalogWithTenantInfo = new StandaloneCatalogWithPriceOverride(cur, tenantContext);
+            this.versions.add(catalogWithTenantInfo);
+        }
+    }
+
+    /*
     public VersionedCatalog(final VersionedCatalog inputCatalog, final InternalTenantContext tenantContext) {
         this.clock = inputCatalog.getClock();
         this.catalogName = inputCatalog.getCatalogName();
         this.recurringBillingMode = inputCatalog.getRecurringBillingMode();
+        versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
         for (final StandaloneCatalogWithPriceOverride cur : inputCatalog.getVersions()) {
             final StandaloneCatalogWithPriceOverride catalogWithTenantInfo = new StandaloneCatalogWithPriceOverride(cur, tenantContext);
             versions.add(catalogWithTenantInfo);
         }
     }
+    */
 
     //
     // Private methods
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/caching/TestEhCacheCatalogCache.java b/catalog/src/test/java/org/killbill/billing/catalog/caching/TestEhCacheCatalogCache.java
index d2e4e19..4e771ce 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/caching/TestEhCacheCatalogCache.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/caching/TestEhCacheCatalogCache.java
@@ -86,7 +86,7 @@ public class TestEhCacheCatalogCache extends CatalogTestSuiteNoDB {
         Assert.assertEquals(products.length, 3);
 
         // Verify the lookup with other contexts
-        final VersionedCatalog resultForMultiTenantContext = new VersionedCatalog(result, multiTenantContext);
+        final VersionedCatalog resultForMultiTenantContext = new VersionedCatalog(result.getClock(), result.getCatalogName(), result.getRecurringBillingMode(), result.getVersions(), multiTenantContext);
         Assert.assertEquals(catalogCache.getCatalog(multiTenantContext).getCatalogName(), resultForMultiTenantContext.getCatalogName());
         Assert.assertEquals(catalogCache.getCatalog(multiTenantContext).getVersions().size(), resultForMultiTenantContext.getVersions().size());
         for (int i = 0; i < catalogCache.getCatalog(multiTenantContext).getVersions().size(); i++) {