killbill-aplcache

Details

diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceList.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceList.java
index 273f12c..c915227 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceList.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceList.java
@@ -141,4 +141,10 @@ public class DefaultPriceList extends ValidatingConfig<StandaloneCatalog> implem
         result = 31 * result + (plans != null ? plans.hashCode() : 0);
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "DefaultPriceList{" +
+               "name='" + name + '}';
+    }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java b/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
index 6014008..82e8ec5 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
@@ -92,13 +92,13 @@ public class VersionedCatalogLoader implements CatalogLoader {
             return result;
         } catch (final ValidationException e) {
             logger.warn("Failed to load default catalog", e);
-            throw new CatalogApiException(ErrorCode.CAT_INVALID_DEFAULT);
+            throw new CatalogApiException(ErrorCode.CAT_INVALID_DEFAULT, uriString);
         } catch (final JAXBException e) {
             logger.warn("Failed to load default catalog", e);
-            throw new CatalogApiException(ErrorCode.CAT_INVALID_DEFAULT, e);
+            throw new CatalogApiException(ErrorCode.CAT_INVALID_DEFAULT, uriString);
         } catch(IllegalArgumentException e) {
             logger.warn("Failed to load default catalog", e);
-            throw new CatalogApiException(ErrorCode.CAT_INVALID_DEFAULT, e);
+            throw new CatalogApiException(ErrorCode.CAT_INVALID_DEFAULT, uriString);
         } catch (Exception e) {
             logger.warn("Failed to load default catalog", e);
             throw new IllegalStateException(e);
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseBillingAlignment.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseBillingAlignment.java
index fc2b8f5..31e56fc 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseBillingAlignment.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseBillingAlignment.java
@@ -74,4 +74,17 @@ public class DefaultCaseBillingAlignment extends DefaultCasePhase<BillingAlignme
         result = 31 * result + (alignment != null ? alignment.hashCode() : 0);
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "DefaultCaseBillingAlignment {" +
+               "alignment=" + alignment +
+               ", phaseType=" + getPhaseType() +
+               ", product=" + getProduct() +
+               ", productCategory=" + getProductCategory() +
+               ", billingPeriod=" + getBillingPeriod() +
+               ", priceList=" + getPriceList() +
+               '}';
+    }
+
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseCancelPolicy.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseCancelPolicy.java
index b1a061c..11a3399 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseCancelPolicy.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseCancelPolicy.java
@@ -74,4 +74,17 @@ public class DefaultCaseCancelPolicy extends DefaultCasePhase<BillingActionPolic
         result = 31 * result + (policy != null ? policy.hashCode() : 0);
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "DefaultCaseCancelPolicy{" +
+               "policy =" + policy +
+               ", phaseType =" + getPhaseType() +
+               ", product=" + getProduct() +
+               ", productCategory=" + getProductCategory() +
+               ", billingPeriod=" + getBillingPeriod() +
+               ", priceList=" + getPriceList() +
+               '}';
+    }
+
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChange.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChange.java
index 62a83cb..4ee86a2 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChange.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChange.java
@@ -303,4 +303,19 @@ public abstract class DefaultCaseChange<T> extends ValidatingConfig<StandaloneCa
     public PriceList getToPriceList() {
         return toPriceList;
     }
+
+    @Override
+    public String toString() {
+        return "DefaultCaseChange{" +
+               "phaseType=" + phaseType +
+               ", fromProduct=" + fromProduct +
+               ", fromProductCategory=" + fromProductCategory +
+               ", fromBillingPeriod=" + fromBillingPeriod +
+               ", fromPriceList=" + fromPriceList +
+               ", toProduct=" + toProduct +
+               ", toProductCategory=" + toProductCategory +
+               ", toBillingPeriod=" + toBillingPeriod +
+               ", toPriceList=" + toPriceList +
+               '}';
+    }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChangePlanAlignment.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChangePlanAlignment.java
index f01860c..d3a95b6 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChangePlanAlignment.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChangePlanAlignment.java
@@ -68,4 +68,21 @@ public class DefaultCaseChangePlanAlignment extends DefaultCaseChange<PlanAlignm
         result = 31 * result + (alignment != null ? alignment.hashCode() : 0);
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "DefaultCaseChangePlanAlignment {" +
+               "alignment=" + alignment +
+               ", phaseType=" + getPhaseType() +
+               ", fromProduct=" + getFromProduct() +
+               ", fromProductCategory=" + getFromProductCategory() +
+               ", fromBillingPeriod=" + getFromBillingPeriod() +
+               ", fromPriceList=" + getFromPriceList() +
+               ", toProduct=" + getToProduct() +
+               ", toProductCategory=" + getToProductCategory() +
+               ", toBillingPeriod=" + getToBillingPeriod() +
+               ", toPriceList=" + getToPriceList() +
+               '}';
+    }
+
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChangePlanPolicy.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChangePlanPolicy.java
index 1464b73..47ec574 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChangePlanPolicy.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChangePlanPolicy.java
@@ -16,16 +16,16 @@
 
 package org.killbill.billing.catalog.rules;
 
+import java.net.URI;
+
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlSeeAlso;
 
+import org.killbill.billing.catalog.CatalogSafetyInitializer;
+import org.killbill.billing.catalog.StandaloneCatalog;
 import org.killbill.billing.catalog.api.BillingActionPolicy;
-import org.killbill.billing.catalog.api.BillingPeriod;
-import org.killbill.billing.catalog.api.PhaseType;
-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.rules.CaseChangePlanPolicy;
+import org.killbill.xmlloader.ValidationErrors;
 
 @XmlSeeAlso(DefaultCaseChange.class)
 public class DefaultCaseChangePlanPolicy extends DefaultCaseChange<BillingActionPolicy> implements CaseChangePlanPolicy {
@@ -49,6 +49,17 @@ public class DefaultCaseChangePlanPolicy extends DefaultCaseChange<BillingAction
     }
 
     @Override
+    public ValidationErrors validate(final StandaloneCatalog catalog, final ValidationErrors errors) {
+        return errors;
+    }
+
+    @Override
+    public void initialize(final StandaloneCatalog catalog, final URI sourceURI) {
+        super.initialize(catalog, sourceURI);
+        CatalogSafetyInitializer.initializeNonRequiredArrayFields(this);
+    }
+
+    @Override
     public boolean equals(final Object o) {
         if (this == o) {
             return true;
@@ -75,4 +86,20 @@ public class DefaultCaseChangePlanPolicy extends DefaultCaseChange<BillingAction
         result = 31 * result + (policy != null ? policy.hashCode() : 0);
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "DefaultCaseChangePlanPolicy {" +
+               "policy=" + policy +
+               ", phaseType=" + phaseType +
+               ", fromProduct=" + getFromProduct() +
+               ", fromProductCategory=" + getFromProductCategory() +
+               ", fromBillingPeriod=" + getFromBillingPeriod() +
+               ", fromPriceList=" + getFromPriceList() +
+               ", toProduct=" + getToProduct() +
+               ", toProductCategory=" + getToProductCategory() +
+               ", toBillingPeriod=" + getToBillingPeriod() +
+               ", toPriceList=" + getToPriceList() +
+               '}';
+    }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseCreateAlignment.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseCreateAlignment.java
index 5d7862d..5577db3 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseCreateAlignment.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseCreateAlignment.java
@@ -68,4 +68,16 @@ public class DefaultCaseCreateAlignment extends DefaultCaseStandardNaming<PlanAl
         result = 31 * result + (alignment != null ? alignment.hashCode() : 0);
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "DefaultCaseCreateAlignment {" +
+               "alignment =" + alignment +
+               ", product=" + getProduct() +
+               ", productCategory=" + getProductCategory() +
+               ", billingPeriod=" + getBillingPeriod() +
+               ", priceList=" + getPriceList() +
+               '}';
+    }
+
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCasePriceList.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCasePriceList.java
index 7958365..4b9cf33 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCasePriceList.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCasePriceList.java
@@ -143,4 +143,16 @@ public class DefaultCasePriceList extends DefaultCaseStandardNaming<DefaultPrice
         result = 31 * result + (toPriceList != null ? toPriceList.hashCode() : 0);
         return result;
     }
+
+
+    @Override
+    public String toString() {
+        return "DefaultCasePriceList {" +
+               "fromProduct=" + fromProduct +
+               ", fromProductCategory=" + fromProductCategory +
+               ", fromBillingPeriod=" + fromBillingPeriod +
+               ", fromPriceList=" + fromPriceList +
+               ", toPriceList=" + toPriceList +
+               '}';
+    }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseStandardNaming.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseStandardNaming.java
index 79c73aa..d23c55f 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseStandardNaming.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseStandardNaming.java
@@ -115,4 +115,5 @@ public abstract class DefaultCaseStandardNaming<T> extends DefaultCase<T> implem
         result = 31 * result + (priceList != null ? priceList.hashCode() : 0);
         return result;
     }
+
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultPlanRules.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultPlanRules.java
index 20c4e9c..f9d3238 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultPlanRules.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultPlanRules.java
@@ -18,6 +18,7 @@ package org.killbill.billing.catalog.rules;
 
 import java.net.URI;
 import java.util.Arrays;
+import java.util.HashSet;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
@@ -45,6 +46,7 @@ import org.killbill.billing.catalog.api.rules.CaseCreateAlignment;
 import org.killbill.billing.catalog.api.rules.CasePriceList;
 import org.killbill.billing.catalog.api.rules.PlanRules;
 import org.killbill.xmlloader.ValidatingConfig;
+import org.killbill.xmlloader.ValidationError;
 import org.killbill.xmlloader.ValidationErrors;
 
 import com.google.common.collect.ImmutableList;
@@ -157,7 +159,7 @@ public class DefaultPlanRules extends ValidatingConfig<StandaloneCatalog> implem
     }
 
     private DefaultPriceList findPriceList(final PlanSpecifier specifier, final StaticCatalog catalog) throws CatalogApiException {
-        DefaultPriceList result = DefaultCase.getResult(priceListCase, specifier, catalog);
+        DefaultPriceList result = DefaultCasePriceList.getResult(priceListCase, specifier, catalog);
         if (result == null) {
             final String priceListName = specifier.getPlanName() != null ? catalog.findCurrentPlan(specifier.getPlanName()).getPriceListName() : specifier.getPriceListName();
             result = (DefaultPriceList) catalog.findCurrentPricelist(priceListName);
@@ -165,13 +167,92 @@ public class DefaultPlanRules extends ValidatingConfig<StandaloneCatalog> implem
         return result;
     }
 
+
     @Override
     public ValidationErrors validate(final StandaloneCatalog catalog, final ValidationErrors errors) {
-        //TODO: MDW - Validation: check that the plan change special case pairs are unique!
-        //TODO: MDW - Validation: check that the each product appears in at most one tier.
-        //TODO: MDW - Unit tests for rules
-        //TODO: MDW - validate that there is a default policy for change AND cancel
+        //
+        // Validate that there is a default policy for change AND cancel rules and check unicity of rules
+        //
+        final HashSet<DefaultCaseChangePlanPolicy> caseChangePlanPoliciesSet = new HashSet<DefaultCaseChangePlanPolicy>();
+        boolean foundDefaultCase = false;
+        for (final DefaultCaseChangePlanPolicy cur : changeCase) {
+            if (caseChangePlanPoliciesSet.contains(cur)) {
+                errors.add(new ValidationError(String.format("Duplicate rule for change plan %s", cur.toString()), catalog.getCatalogURI(), DefaultPlanRules.class, ""));
+            } else {
+                caseChangePlanPoliciesSet.add(cur);
+            }
+            if (cur.getPhaseType() == null &&
+                cur.getFromProduct() == null &&
+                cur.getFromProductCategory() == null &&
+                cur.getFromBillingPeriod() == null &&
+                cur.getFromPriceList() == null &&
+                cur.getToProduct() == null &&
+                cur.getToProductCategory() == null &&
+                cur.getToBillingPeriod() == null &&
+                cur.getToPriceList() == null) {
+                foundDefaultCase = true;
+            }
+        }
+        if (!foundDefaultCase) {
+            errors.add(new ValidationError("Missing default rule case for plan change", catalog.getCatalogURI(), DefaultPlanRules.class, ""));
+        }
+
+        final HashSet<DefaultCaseCancelPolicy> defaultCaseCancelPoliciesSet = new HashSet<DefaultCaseCancelPolicy>();
+        foundDefaultCase = false;
+        for (final DefaultCaseCancelPolicy cur : cancelCase) {
+            if (defaultCaseCancelPoliciesSet.contains(cur)) {
+                errors.add(new ValidationError(String.format("Duplicate rule for plan cancellation %s", cur.toString()), catalog.getCatalogURI(), DefaultPlanRules.class, ""));
+            } else {
+                defaultCaseCancelPoliciesSet.add(cur);
+            }
+            if (cur.getPhaseType() == null &&
+                cur.getProduct() == null &&
+                cur.getProductCategory() == null &&
+                cur.getBillingPeriod() == null &&
+                cur.getPriceList() == null) {
+                foundDefaultCase = true;
+            }
+        }
+        if (!foundDefaultCase) {
+            errors.add(new ValidationError("Missing default rule case for plan cancellation", catalog.getCatalogURI(), DefaultPlanRules.class, ""));
+        }
+
+
+        final HashSet<DefaultCaseChangePlanAlignment> caseChangePlanAlignmentsSet = new HashSet<DefaultCaseChangePlanAlignment>();
+        for (final DefaultCaseChangePlanAlignment cur : changeAlignmentCase) {
+            if (caseChangePlanAlignmentsSet.contains(cur)) {
+                errors.add(new ValidationError(String.format("Duplicate rule for plan change alignment %s", cur.toString()), catalog.getCatalogURI(), DefaultPlanRules.class, ""));
+            } else {
+                caseChangePlanAlignmentsSet.add(cur);
+            }
+        }
+
+        final HashSet<DefaultCaseCreateAlignment> caseCreateAlignmentsSet = new HashSet<DefaultCaseCreateAlignment>();
+        for (final DefaultCaseCreateAlignment cur : createAlignmentCase) {
+            if (caseCreateAlignmentsSet.contains(cur)) {
+                errors.add(new ValidationError(String.format("Duplicate rule for create plan alignment %s", cur.toString()), catalog.getCatalogURI(), DefaultPlanRules.class, ""));
+            } else {
+                caseCreateAlignmentsSet.add(cur);
+            }
+        }
 
+        final HashSet<DefaultCaseBillingAlignment> caseBillingAlignmentsSet = new HashSet<DefaultCaseBillingAlignment>();
+        for (final DefaultCaseBillingAlignment cur : billingAlignmentCase) {
+            if (caseBillingAlignmentsSet.contains(cur)) {
+                errors.add(new ValidationError(String.format("Duplicate rule for billing alignment %s", cur.toString()), catalog.getCatalogURI(), DefaultPlanRules.class, ""));
+            } else {
+                caseBillingAlignmentsSet.add(cur);
+            }
+        }
+
+        final HashSet<DefaultCasePriceList> casePriceListsSet = new HashSet<DefaultCasePriceList>();
+        for (final DefaultCasePriceList cur : priceListCase) {
+            if (casePriceListsSet.contains(cur)) {
+                errors.add(new ValidationError(String.format("Duplicate rule for price list transition %s", cur.toString()), catalog.getCatalogURI(), DefaultPlanRules.class, ""));
+            } else {
+                casePriceListsSet.add(cur);
+            }
+        }
         return errors;
     }
 
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestStandaloneCatalog.java b/catalog/src/test/java/org/killbill/billing/catalog/TestStandaloneCatalog.java
index 32d839a..c7c411a 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/TestStandaloneCatalog.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestStandaloneCatalog.java
@@ -30,17 +30,26 @@ import com.google.common.io.Resources;
 public class TestStandaloneCatalog extends CatalogTestSuiteNoDB {
 
     @Test(groups = "fast")
-    public void testLoadCatalogWithPlanInvalidProduct() throws Exception {
+    public void testLoadCatalogWithValidationIssues() throws Exception {
         try {
             XMLLoader.getObjectFromString(Resources.getResource("CatalogWithValidationErrors.xml").toExternalForm(), StandaloneCatalog.class);
             Assert.fail();
         } catch (final ValidationException e) {
-            Assert.assertEquals(e.getErrors().size(), 5);
+            Assert.assertEquals(e.getErrors().size(), 13);
             Assert.assertEquals(e.getErrors().get(0).getDescription(), "Invalid product for plan 'standard'");
             Assert.assertEquals(e.getErrors().get(1).getDescription(), "Duration can only have 'UNLIMITED' unit if the number is omitted");
             Assert.assertEquals(e.getErrors().get(2).getDescription(), "Finite Duration must have a well defined length");
             Assert.assertEquals(e.getErrors().get(3).getDescription(), "Initial Phase standard-trial-evergreen of plan standard-trial cannot be of type EVERGREEN");
             Assert.assertEquals(e.getErrors().get(4).getDescription(), "Final Phase standard-trial-trial of plan standard-trial cannot be of type TRIAL");
+            Assert.assertEquals(e.getErrors().get(5).getDescription(), "Duplicate rule for change plan DefaultCaseChangePlanPolicy {policy=IMMEDIATE, phaseType=null, fromProduct=DefaultProduct{name='Standard', category=BASE, included=org.killbill.billing.catalog.CatalogEntityCollection@0, available=org.killbill.billing.catalog.CatalogEntityCollection@0, limits=[], catalogName='CatalogWithValidationErrors'}, fromProductCategory=null, fromBillingPeriod=null, fromPriceList=null, toProduct=null, toProductCategory=null, toBillingPeriod=null, toPriceList=null}");
+            Assert.assertEquals(e.getErrors().get(6).getDescription(), "Missing default rule case for plan change");
+            Assert.assertEquals(e.getErrors().get(7).getDescription(), "Duplicate rule for plan cancellation DefaultCaseCancelPolicy{policy =IMMEDIATE, phaseType =null, product=DefaultProduct{name='Standard', category=BASE, included=org.killbill.billing.catalog.CatalogEntityCollection@0, available=org.killbill.billing.catalog.CatalogEntityCollection@0, limits=[], catalogName='CatalogWithValidationErrors'}, productCategory=null, billingPeriod=null, priceList=null}");
+            Assert.assertEquals(e.getErrors().get(8).getDescription(), "Missing default rule case for plan cancellation");
+            Assert.assertEquals(e.getErrors().get(9).getDescription(), "Duplicate rule for plan change alignment DefaultCaseChangePlanAlignment {alignment=START_OF_BUNDLE, phaseType=null, fromProduct=null, fromProductCategory=null, fromBillingPeriod=null, fromPriceList=null, toProduct=null, toProductCategory=null, toBillingPeriod=null, toPriceList=null}");
+            Assert.assertEquals(e.getErrors().get(10).getDescription(), "Duplicate rule for create plan alignment DefaultCaseCreateAlignment {alignment =START_OF_BUNDLE, product=null, productCategory=null, billingPeriod=null, priceList=null}");
+            Assert.assertEquals(e.getErrors().get(11).getDescription(), "Duplicate rule for billing alignment DefaultCaseBillingAlignment {alignment=ACCOUNT, phaseType=null, product=null, productCategory=null, billingPeriod=null, priceList=null}");
+            Assert.assertEquals(e.getErrors().get(12).getDescription(), "Duplicate rule for price list transition DefaultCasePriceList {fromProduct=null, fromProductCategory=null, fromBillingPeriod=null, fromPriceList=null, toPriceList=DefaultPriceList{name='DEFAULT}}");
+
         }
     }
 
diff --git a/catalog/src/test/resources/CatalogWithValidationErrors.xml b/catalog/src/test/resources/CatalogWithValidationErrors.xml
index 97cfb4d..26758f8 100644
--- a/catalog/src/test/resources/CatalogWithValidationErrors.xml
+++ b/catalog/src/test/resources/CatalogWithValidationErrors.xml
@@ -20,7 +20,7 @@
          xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
 
     <effectiveDate>2013-02-08T00:00:00+00:00</effectiveDate>
-    <catalogName>CatalogWithPlanInvalidProduct</catalogName>
+    <catalogName>CatalogWithValidationErrors</catalogName>
 
     <recurringBillingMode>IN_ADVANCE</recurringBillingMode>
 
@@ -34,7 +34,64 @@
         </product>
     </products>
 
-    <rules></rules>
+
+
+    <!-- Note we defined twice the same rule for each case and also we miss default rules for plan cancellation and change of plan -->
+    <rules>
+        <changePolicy>
+            <changePolicyCase>
+                <fromProduct>Standard</fromProduct>
+                <policy>IMMEDIATE</policy>
+            </changePolicyCase>
+            <changePolicyCase>
+                <fromProduct>Standard</fromProduct>
+                <policy>IMMEDIATE</policy>
+            </changePolicyCase>
+        </changePolicy>
+        <changeAlignment>
+            <changeAlignmentCase>
+                <alignment>START_OF_BUNDLE</alignment>
+            </changeAlignmentCase>
+            <changeAlignmentCase>
+                <alignment>START_OF_BUNDLE</alignment>
+            </changeAlignmentCase>
+        </changeAlignment>
+        <cancelPolicy>
+            <cancelPolicyCase>
+                <product>Standard</product>
+                <policy>IMMEDIATE</policy>
+            </cancelPolicyCase>
+            <cancelPolicyCase>
+                <product>Standard</product>
+                <policy>IMMEDIATE</policy>
+            </cancelPolicyCase>
+        </cancelPolicy>
+        <createAlignment>
+            <createAlignmentCase>
+                <alignment>START_OF_BUNDLE</alignment>
+            </createAlignmentCase>
+            <createAlignmentCase>
+                <alignment>START_OF_BUNDLE</alignment>
+            </createAlignmentCase>
+        </createAlignment>
+        <billingAlignment>
+            <billingAlignmentCase>
+                <alignment>ACCOUNT</alignment>
+            </billingAlignmentCase>
+            <billingAlignmentCase>
+                <alignment>ACCOUNT</alignment>
+            </billingAlignmentCase>
+        </billingAlignment>
+        <priceList>
+            <priceListCase>
+                <toPriceList>DEFAULT</toPriceList>
+            </priceListCase>
+            <priceListCase>
+                <toPriceList>DEFAULT</toPriceList>
+            </priceListCase>
+        </priceList>
+    </rules>
+
 
     <plans>
         <plan name="standard">