killbill-memoizeit

Changes

.circleci/config.yml 219(+219 -0)

pom.xml 2(+1 -1)

Details

.circleci/config.yml 219(+219 -0)

diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 0000000..ca7b9fc
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,219 @@
+defaults: &defaults
+  working_directory: ~/repo
+  environment:
+    MAVEN_OPTS: -server -showversion -XX:+PrintCommandLineFlags -XX:+UseCodeCacheFlushing -Xms1024M -Xmx2048M -XX:+CMSClassUnloadingEnabled -XX:-OmitStackTraceInFastThrow -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSConcurrentMTEnabled -XX:+CMSParallelRemarkEnabled -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 -XX:+ScavengeBeforeFullGC -XX:+CMSScavengeBeforeRemark -XX:NewSize=600m -XX:MaxNewSize=900m -XX:SurvivorRatio=10 -XX:+DisableExplicitGC -Djava.security.egd=file:/dev/./urandom
+
+version: 2
+jobs:
+  build:
+    <<: *defaults
+    docker:
+      - image: killbill/kbbuild:0.1.0
+    steps:
+      - checkout
+      - restore_cache:
+          key: v1-dependencies-{{ .Branch }}-{{ checksum "pom.xml" }}
+      - run:
+          name: Setup dependencies
+          command: |
+            if [ "${CIRCLE_BRANCH}" != "master" ]; then
+              for i in killbill-oss-parent killbill-api killbill-plugin-api killbill-commons killbill-platform; do
+                if [ -n "$(git ls-remote --heads https://github.com/killbill/$i.git ${CIRCLE_BRANCH})" ]; then
+                  echo "*** Setting up $i"
+                  mkdir -p /home/killbill/$i
+                  git clone https://github.com/killbill/$i.git /home/killbill/$i
+                  pushd /home/killbill/$i
+                  git checkout -b ${CIRCLE_BRANCH} origin/${CIRCLE_BRANCH}
+                  mvn clean install -DskipTests=true
+                  popd
+                fi
+              done
+            fi
+      - run: mvn -DskipTests=true clean install dependency:go-offline
+      - save_cache:
+          paths:
+            - ~/.m2
+          key: v1-dependencies-{{ .Branch }}-{{ checksum "pom.xml" }}
+
+  test-h2:
+    <<: *defaults
+    docker:
+      - image: killbill/kbbuild:0.1.0
+    steps:
+      - checkout
+      - restore_cache:
+          key: v1-dependencies-{{ .Branch }}-{{ checksum "pom.xml" }}
+      - run: mvn clean install -Ptravis
+      - run:
+          name: Save test results
+          command: |
+            mkdir -p ~/junit/
+            find . -type f -regex ".*/target/surefire-reports/.*xml" -exec cp {} ~/junit/ \;
+          when: always
+      - store_test_results:
+          path: ~/junit
+      - store_artifacts:
+          path: ~/junit
+  test-mysql:
+    <<: *defaults
+    docker:
+      - image: killbill/kbbuild:0.1.0
+      - image: killbill/mariadb:0.18
+        environment:
+        - MYSQL_ROOT_PASSWORD=root
+    steps:
+      - checkout
+      - restore_cache:
+          key: v1-dependencies-{{ .Branch }}-{{ checksum "pom.xml" }}
+      - run:
+          name: Setup latest DDL
+          command: |
+            set +e
+            count=0
+            until mysqladmin ping -h 127.0.0.1 -u root --password=root --silent; do
+              if [[ "$count" == "25" ]]; then
+                exit 1
+              fi
+              (( count++ ))
+
+              printf '.'
+              sleep 5
+            done
+
+            set -e
+            ./bin/db-helper -a create --driver mysql -u root -p root -t yes -h 127.0.0.1
+      - run: mvn clean install -Plocaltest-mysql
+      - run:
+          name: Save test results
+          command: |
+            mkdir -p ~/junit/
+            find . -type f -regex ".*/target/surefire-reports/.*xml" -exec cp {} ~/junit/ \;
+          when: always
+      - store_test_results:
+          path: ~/junit
+      - store_artifacts:
+          path: ~/junit
+  test-postgresql:
+    <<: *defaults
+    docker:
+      - image: killbill/kbbuild:0.1.0
+      - image: killbill/postgresql:0.18
+        environment:
+        - POSTGRES_PASSWORD=postgres
+    steps:
+      - checkout
+      - restore_cache:
+          key: v1-dependencies-{{ .Branch }}-{{ checksum "pom.xml" }}
+      - run:
+          name: Setup latest DDL
+          command: ./bin/db-helper -a create --driver postgres -u postgres -p postgres -t yes
+      - run: mvn clean install -Plocaltest-postgresql
+      - run:
+          name: Save test results
+          command: |
+            mkdir -p ~/junit/
+            find . -type f -regex ".*/target/surefire-reports/.*xml" -exec cp {} ~/junit/ \;
+          when: always
+      - store_test_results:
+          path: ~/junit
+      - store_artifacts:
+          path: ~/junit
+
+  integration-tests:
+    <<: *defaults
+    docker:
+      - image: killbill/kbbuild:0.1.0
+      - image: killbill/mariadb:0.18
+        environment:
+        - MYSQL_ROOT_PASSWORD=root
+    steps:
+      - checkout
+      - restore_cache:
+          key: v1-dependencies-{{ .Branch }}-{{ checksum "pom.xml" }}
+      - run:
+          name: Setup latest DDL
+          command: ./bin/db-helper -a create --driver mysql -u root -p root -t yes -h 127.0.0.1
+      - run:
+          name: Run integration tests
+          command: |
+            set +e
+
+            mvn clean install -DskipTests=true
+            nohup ./bin/start-server -s &
+
+            mkdir -p /home/killbill/killbill-integration-tests
+            git clone https://github.com/killbill/killbill-integration-tests.git /home/killbill/killbill-integration-tests
+            pushd /home/killbill/killbill-integration-tests
+            if [ "${CIRCLE_BRANCH}" != "master" ]; then
+              if [ -n "$(git ls-remote --heads https://github.com/killbill/killbill-integration-tests.git ${CIRCLE_BRANCH})" ]; then
+                git checkout -b ${CIRCLE_BRANCH} origin/${CIRCLE_BRANCH}
+              fi
+            fi
+            bundle install
+
+            count=0
+            until $(curl --output /dev/null --silent --fail http://127.0.0.1:8080/1.0/healthcheck); do
+              if [[ "$count" == "25" ]]; then
+                exit 1
+              fi
+              (( count++ ))
+
+              printf '.'
+              sleep 5
+            done
+
+            set -e
+            mkdir -p /tmp/test-results
+            bundle exec rake test:core | tee /tmp/test-results/test.txt 2>&1
+      - store_test_results:
+          path: /tmp/test-results
+      - store_artifacts:
+          path: /tmp/test-results
+
+workflows:
+  version: 2
+  build-and-test:
+    jobs:
+      - build:
+          filters:
+            branches:
+              only:
+                - master
+                - work-for-release-0.19.x
+                - circle-ci-experiment
+      - test-h2:
+          requires:
+            - build
+          filters:
+            branches:
+              only:
+                - master
+                - circle-ci-experiment
+      - test-mysql:
+          requires:
+            - build
+          filters:
+            branches:
+              only:
+                - master
+                - work-for-release-0.19.x
+                - circle-ci-experiment
+      - test-postgresql:
+          requires:
+            - build
+          filters:
+            branches:
+              only:
+                - master
+                - circle-ci-experiment
+      - integration-tests:
+          requires:
+            - test-h2
+            - test-mysql
+            - test-postgresql
+          filters:
+            branches:
+              only:
+                - master
+                - work-for-release-0.19.x
+                - circle-ci-experiment
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java
index 7dd3961..6769325 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java
@@ -29,6 +29,7 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.killbill.billing.ErrorCode;
+import org.killbill.billing.api.FlakyRetryAnalyzer;
 import org.killbill.billing.api.TestApiListener.NextEvent;
 import org.killbill.billing.beatrix.integration.BeatrixIntegrationModule;
 import org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
@@ -953,7 +954,8 @@ public class TestOverdueIntegration extends TestOverdueBase {
         checkODState(OverdueWrapper.CLEAR_STATE_NAME);
     }
 
-    @Test(groups = "slow", description = "Test overdue state with number of unpaid invoices condition")
+    // Flaky, see https://github.com/killbill/killbill/issues/782
+    @Test(groups = "slow", description = "Test overdue state with number of unpaid invoices condition", retryAnalyzer = FlakyRetryAnalyzer.class)
     public void testOverdueStateWithNumberOfUnpaidInvoicesCondition() throws Exception {
         // 2012-05-01T00:03:42.000Z
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
@@ -1025,7 +1027,8 @@ public class TestOverdueIntegration extends TestOverdueBase {
         checkODState(OverdueWrapper.CLEAR_STATE_NAME);
     }
 
-    @Test(groups = "slow", description = "Test overdue state with total unpaid invoice balance condition")
+    // Flaky, see https://github.com/killbill/killbill/issues/782
+    @Test(groups = "slow", description = "Test overdue state with total unpaid invoice balance condition", retryAnalyzer = FlakyRetryAnalyzer.class)
     public void testOverdueStateWithTotalUnpaidInvoiceBalanceCondition() throws Exception {
         // 2012-05-01T00:03:42.000Z
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java
index 89507b5..5536865 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java
@@ -421,49 +421,6 @@ public class TestIntegration extends TestIntegrationBase {
         checkNoMoreInvoiceToGenerate(account);
     }
 
-    @Test(groups = {"stress"}, enabled = false)
-    public void stressTest() throws Exception {
-        final int maxIterations = 100;
-        for (int curIteration = 0; curIteration < maxIterations; curIteration++) {
-            if (curIteration != 0) {
-                beforeMethod();
-            }
-
-            log.info("################################  ITERATION " + curIteration + "  #########################");
-            afterMethod();
-            beforeMethod();
-            testBasePlanCompleteWithBillingDayInPast();
-            Thread.sleep(1000);
-            afterMethod();
-            beforeMethod();
-            testBasePlanCompleteWithBillingDayAlignedWithTrial();
-            Thread.sleep(1000);
-            afterMethod();
-            beforeMethod();
-            testBasePlanCompleteWithBillingDayInFuture();
-            if (curIteration < maxIterations - 1) {
-                afterMethod();
-                Thread.sleep(1000);
-            }
-        }
-    }
-
-    @Test(groups = {"stress"}, enabled = false)
-    public void stressTestDebug() throws Exception {
-        final int maxIterations = 100;
-        for (int curIteration = 0; curIteration < maxIterations; curIteration++) {
-            log.info("################################  ITERATION " + curIteration + "  #########################");
-            if (curIteration != 0) {
-                beforeMethod();
-            }
-            testAddonsWithMultipleAlignments();
-            if (curIteration < maxIterations - 1) {
-                afterMethod();
-                Thread.sleep(1000);
-            }
-        }
-    }
-
     @Test(groups = "slow")
     public void testAddonsWithMultipleAlignments() throws Exception {
         final DateTime initialDate = new DateTime(2012, 4, 25, 0, 13, 42, 0, testTimeZone);
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
index 2969715..6c98b7a 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
@@ -18,15 +18,18 @@
 
 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 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 javax.annotation.Nullable;
+import javax.inject.Inject;
+import javax.inject.Named;
+
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
@@ -122,30 +125,31 @@ import org.skife.config.TimeSpan;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
+import org.testng.IHookCallBack;
+import org.testng.IHookable;
+import org.testng.ITestResult;
 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 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 static org.awaitility.Awaitility.await;
 import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.awaitility.Awaitility.await;
 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 {
+public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implements IHookable {
 
     protected static final DateTimeZone testTimeZone = DateTimeZone.UTC;
     protected static final Logger log = LoggerFactory.getLogger(TestIntegrationBase.class);
@@ -295,6 +299,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
 
     protected ConfigurableInvoiceConfig invoiceConfig;
 
+    @Override
     protected void assertListenerStatus() {
         busHandler.assertListenerStatus();
     }
@@ -329,16 +334,10 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
         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();
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java
index 18f2da2..a755f9c 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java
@@ -64,12 +64,6 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
     @Inject
     protected InvoiceDao invoiceDao;
 
-
-    @AfterMethod(groups = "slow")
-    public void afterMethod() throws Exception {
-        super.afterMethod();
-    }
-
     @Test(groups = "slow")
     public void testSimplePartialRepairWithItemAdjustment() throws Exception {
         // We take april as it has 30 days (easier to play with BCD)
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentRefund.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentRefund.java
index 86912e4..15d05da 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentRefund.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentRefund.java
@@ -152,7 +152,7 @@ public class TestPaymentRefund extends TestIntegrationBase {
         // No end date for the trial item (fixed price of zero), and CTD should be today (i.e. when the trial started)
         invoiceChecker.checkChargedThroughDate(bpEntitlement.getId(), clock.getUTCToday(), callContext);
 
-        setDateAndCheckForCompletion(new DateTime(2012, 3, 2, 23, 59, 59, 0, testTimeZone), NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        setDateAndCheckForCompletion(new DateTime(2012, 3, 2, 23, 59, 0, 0, testTimeZone), NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         invoice = invoiceChecker.checkInvoice(account.getId(), ++invoiceItemCount, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 3, 2),
                                                                                                                              new LocalDate(2012, 3, 31), InvoiceItemType.RECURRING, new BigDecimal("233.82")));
         payment = paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 3, 2), new BigDecimal("233.82"), TransactionStatus.SUCCESS, invoice.getId(), Currency.USD));
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPublicBus.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPublicBus.java
index 353ae0d..288c217 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPublicBus.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPublicBus.java
@@ -24,13 +24,10 @@ import java.util.concurrent.Callable;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.joda.time.DateTime;
-import org.killbill.billing.DBTestingHelper;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.api.TestApiListener.NextEvent;
-import org.killbill.billing.beatrix.extbus.DefaultBusExternalEvent;
 import org.killbill.billing.callcontext.DefaultCallContext;
 import org.killbill.billing.catalog.api.BillingPeriod;
-import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.entitlement.api.DefaultEntitlement;
 import org.killbill.billing.notification.plugin.api.ExtBusEvent;
@@ -46,11 +43,6 @@ import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.CallOrigin;
 import org.killbill.billing.util.callcontext.UserType;
 import org.killbill.billing.util.jackson.ObjectMapper;
-import org.killbill.billing.util.nodes.NodeCommand;
-import org.killbill.billing.util.nodes.NodeCommandMetadata;
-import org.killbill.billing.util.nodes.NodeCommandProperty;
-import org.killbill.billing.util.nodes.PluginNodeCommandMetadata;
-import org.killbill.billing.util.nodes.SystemNodeCommandType;
 import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
@@ -58,12 +50,11 @@ import org.testng.annotations.Test;
 
 import com.fasterxml.jackson.core.JsonParseException;
 import com.fasterxml.jackson.databind.JsonMappingException;
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.eventbus.Subscribe;
 
-import static org.awaitility.Awaitility.await;
 import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.awaitility.Awaitility.await;
 import static org.testng.Assert.assertNotNull;
 
 public class TestPublicBus extends TestIntegrationBase {
@@ -123,10 +114,7 @@ public class TestPublicBus extends TestIntegrationBase {
         TODO modify sequence to allow optional registration of publicListener
          */
 
-        try {
-            DBTestingHelper.get().getInstance().cleanupAllTables();
-        } catch (final Exception ignored) {
-        }
+        cleanupAllTables();
 
         log.debug("RESET TEST FRAMEWORK");
 
@@ -149,9 +137,6 @@ public class TestPublicBus extends TestIntegrationBase {
         paymentPlugin.clear();
 
         this.externalBusCount = new AtomicInteger(0);
-
-        // Make sure we start with a clean state
-        assertListenerStatus();
     }
 
     @AfterMethod(groups = "slow")
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithFakeKPMPlugin.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithFakeKPMPlugin.java
index b239488..60e3176 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithFakeKPMPlugin.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithFakeKPMPlugin.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * 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
@@ -21,7 +21,6 @@ import java.io.File;
 import java.io.IOException;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -38,12 +37,10 @@ import org.killbill.billing.notification.plugin.api.ExtBusEventType;
 import org.killbill.billing.osgi.BundleRegistry;
 import org.killbill.billing.osgi.BundleWithConfig;
 import org.killbill.billing.osgi.FileInstall;
-import org.killbill.billing.osgi.PureOSGIBundleFinder;
 import org.killbill.billing.osgi.api.PluginInfo;
 import org.killbill.billing.osgi.api.PluginStateChange;
 import org.killbill.billing.osgi.api.PluginsInfoApi;
 import org.killbill.billing.osgi.api.config.PluginConfig;
-import org.killbill.billing.osgi.api.config.PluginConfigServiceApi;
 import org.killbill.billing.osgi.api.config.PluginLanguage;
 import org.killbill.billing.osgi.api.config.PluginType;
 import org.killbill.billing.osgi.config.OSGIConfig;
@@ -51,7 +48,6 @@ import org.killbill.billing.osgi.pluginconf.PluginConfigException;
 import org.killbill.billing.osgi.pluginconf.PluginFinder;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.util.jackson.ObjectMapper;
-import org.killbill.billing.util.nodes.KillbillNodesApi;
 import org.killbill.billing.util.nodes.NodeCommand;
 import org.killbill.billing.util.nodes.NodeCommandMetadata;
 import org.killbill.billing.util.nodes.NodeCommandProperty;
@@ -61,8 +57,10 @@ import org.killbill.billing.util.nodes.PluginNodeCommandMetadata;
 import org.killbill.billing.util.nodes.SystemNodeCommandType;
 import org.mockito.Mockito;
 import org.osgi.framework.Bundle;
+import org.osgi.framework.launch.Framework;
 import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import com.fasterxml.jackson.databind.SerializationFeature;
@@ -79,8 +77,10 @@ import com.google.inject.Module;
 import com.google.inject.Stage;
 import com.google.inject.util.Modules;
 
-import static org.awaitility.Awaitility.await;
 import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.awaitility.Awaitility.await;
+import static org.mockito.Mockito.RETURNS_MOCKS;
+import static org.mockito.Mockito.withSettings;
 
 public class TestWithFakeKPMPlugin extends TestIntegrationBase {
 
@@ -226,13 +226,16 @@ public class TestWithFakeKPMPlugin extends TestIntegrationBase {
 
         @Inject
         public FakeBundleRegistry() {
-            super(null);
+            super(Mockito.mock(FileInstall.class, withSettings().defaultAnswer(RETURNS_MOCKS)));
             bundles = new ArrayList<BundleWithMetadata>();
         }
 
-        public void installNewBundle(final String pluginName, @Nullable final String version, final PluginLanguage pluginLanguage) {
+        @Override
+        public void installBundles(final Framework framework) {
+            super.installBundles(framework);
+
             final Bundle bundle = Mockito.mock(Bundle.class);
-            Mockito.when(bundle.getSymbolicName()).thenReturn(pluginName);
+            Mockito.when(bundle.getSymbolicName()).thenReturn(NEW_PLUGIN_NAME);
 
             final BundleWithConfig config = new BundleWithConfig(bundle, new PluginConfig() {
                 @Override
@@ -247,7 +250,7 @@ public class TestWithFakeKPMPlugin extends TestIntegrationBase {
 
                 @Override
                 public String getPluginName() {
-                    return pluginName;
+                    return NEW_PLUGIN_NAME;
                 }
 
                 @Override
@@ -257,7 +260,7 @@ public class TestWithFakeKPMPlugin extends TestIntegrationBase {
 
                 @Override
                 public String getVersion() {
-                    return version;
+                    return NEW_PLUGIN_VERSION;
                 }
 
                 @Override
@@ -272,7 +275,7 @@ public class TestWithFakeKPMPlugin extends TestIntegrationBase {
 
                 @Override
                 public PluginLanguage getPluginLanguage() {
-                    return pluginLanguage;
+                    return PluginLanguage.JAVA;
                 }
 
                 @Override
@@ -296,10 +299,6 @@ public class TestWithFakeKPMPlugin extends TestIntegrationBase {
                 }
             }).orNull();
         }
-
-        public Collection<BundleWithMetadata> getBundles() {
-            return bundles;
-        }
     }
 
     public static class OverrideModuleForOSGI implements Module {
@@ -317,16 +316,12 @@ public class TestWithFakeKPMPlugin extends TestIntegrationBase {
         g.injectMembers(this);
     }
 
-    @BeforeClass(groups = "slow")
+    @BeforeMethod(groups = "slow")
     public void beforeMethod() throws Exception {
-
-        try {
-            DBTestingHelper.get().getInstance().cleanupAllTables();
-        } catch (final Exception ignored) {
-        }
-
         log.debug("RESET TEST FRAMEWORK");
 
+        cleanupAllTables();
+
         clock.resetDeltaFromReality();
         busHandler.reset();
 
@@ -335,9 +330,6 @@ public class TestWithFakeKPMPlugin extends TestIntegrationBase {
         externalBus.register(new FakeKPMPlugin());
 
         lifecycle.fireStartupSequencePostEventRegistration();
-
-        // Make sure we start with a clean state
-        assertListenerStatus();
     }
 
     @Test(groups = "slow")
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java
index 9aa8eb1..52f5921 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java
@@ -54,11 +54,6 @@ import com.google.common.collect.ImmutableList;
 
 public class TestConsumableInArrear extends TestIntegrationBase {
 
-    @BeforeMethod(groups = "slow")
-    public void beforeMethod() throws Exception {
-        super.beforeMethod();
-    }
-
     @Test(groups = "slow")
     public void testWithNoUsageInPeriodAndOldUsage() throws Exception {
         // We take april as it has 30 days (easier to play with BCD)
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/util/PaymentChecker.java b/beatrix/src/test/java/org/killbill/billing/beatrix/util/PaymentChecker.java
index 03440c5..9925dde 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/util/PaymentChecker.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/util/PaymentChecker.java
@@ -93,7 +93,7 @@ public class PaymentChecker {
         final PaymentTransaction transaction = getPurchaseTransaction(payment);
         Assert.assertTrue(transaction.getAmount().compareTo(expected.getAmount()) == 0, "Actual amount " + transaction.getAmount() + ", expected amount " + expected.getAmount());
         Assert.assertEquals(transaction.getTransactionStatus(), expected.getStatus());
-        Assert.assertEquals(transaction.getEffectiveDate().toLocalDate().compareTo(expected.getPaymentDate()), 0);
+        Assert.assertEquals(transaction.getEffectiveDate().toLocalDate().compareTo(expected.getPaymentDate()), 0, "Actual date " + transaction.getEffectiveDate() + ", expected date " + expected.getPaymentDate());
 
         auditChecker.checkPaymentCreated(payment, context);
     }
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java
index c991055..7c84dac 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java
@@ -91,6 +91,7 @@ public class DefaultEventsStream implements EventsStream {
                                final List<SubscriptionBase> allSubscriptionsForBundle,
                                final int defaultBillCycleDayLocal,
                                final InternalTenantContext contextWithValidAccountRecordId, final DateTime utcNow) {
+        sanityChecks(account, bundle, baseSubscription, subscription);
         this.account = account;
         this.bundle = bundle;
         this.blockingStates = blockingStates;
@@ -106,6 +107,20 @@ public class DefaultEventsStream implements EventsStream {
         setup();
     }
 
+    private void sanityChecks(@Nullable final ImmutableAccountData account,
+                              @Nullable final SubscriptionBaseBundle bundle,
+                              @Nullable final SubscriptionBase baseSubscription,
+                              @Nullable final SubscriptionBase subscription) {
+        for (final Object object : new Object[]{account, bundle, baseSubscription, subscription}) {
+            Preconditions.checkNotNull(object,
+                                       "accountId='%s', bundleId='%s', baseSubscriptionId='%s', subscriptionId='%s'",
+                                       account != null ? account.getId() : null,
+                                       bundle != null ? bundle.getId() : null,
+                                       baseSubscription != null ? baseSubscription.getId() : null,
+                                       subscription != null ? subscription.getId() : null);
+        }
+    }
+
     @Override
     public UUID getAccountId() {
         return account.getId();
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java b/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
index 9f6f218..120548c 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
@@ -140,9 +140,6 @@ public class EntitlementTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWi
         startTestFamework(testListener, clock, busService, subscriptionBaseService, entitlementService);
         this.catalog = initCatalog(catalogService);
 
-        // Make sure we start with a clean state
-        assertListenerStatus();
-
         configureShiro();
         login("EntitlementUser");
     }
@@ -174,12 +171,8 @@ public class EntitlementTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWi
 
     @AfterMethod(groups = "slow")
     public void afterMethod() throws Exception {
-
         securityApi.logout();
 
-        // Make sure we finish in a clean state
-        assertListenerStatus();
-
         stopTestFramework(testListener, busService, subscriptionBaseService, entitlementService);
     }
 
@@ -289,6 +282,7 @@ public class EntitlementTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWi
         return account;
     }
 
+    @Override
     protected void assertListenerStatus() {
         testListener.assertListenerStatus();
     }
diff --git a/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteWithEmbeddedDB.java b/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
index 4eced64..859d024 100644
--- a/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
+++ b/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
@@ -108,16 +108,10 @@ public abstract class JunctionTestSuiteWithEmbeddedDB extends GuicyKillbillTestS
         super.beforeMethod();
         startTestFamework();
         this.catalog = initCatalog(catalogService);
-
-        // 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();
-
         stopTestFramework();
     }
 
@@ -219,6 +213,7 @@ public abstract class JunctionTestSuiteWithEmbeddedDB extends GuicyKillbillTestS
         return account;
     }
 
+    @Override
     protected void assertListenerStatus() {
         testListener.assertListenerStatus();
     }
diff --git a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
index c8c99f1..880e6c2 100644
--- a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
+++ b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
@@ -223,6 +223,7 @@ public class TestDefaultInternalBillingApi extends JunctionTestSuiteWithEmbedded
                                                                      true,
                                                                      block1Date);
         blockingInternalApi.setBlockingState(state1, internalCallContext);
+        assertListenerStatus();
 
         clock.addDays(1);
 
diff --git a/overdue/src/test/java/org/killbill/billing/overdue/notification/TestOverdueCheckNotifier.java b/overdue/src/test/java/org/killbill/billing/overdue/notification/TestOverdueCheckNotifier.java
index 162d756..4747620 100644
--- a/overdue/src/test/java/org/killbill/billing/overdue/notification/TestOverdueCheckNotifier.java
+++ b/overdue/src/test/java/org/killbill/billing/overdue/notification/TestOverdueCheckNotifier.java
@@ -69,9 +69,10 @@ public class TestOverdueCheckNotifier extends OverdueTestSuiteWithEmbeddedDB {
     @Override
     @BeforeMethod(groups = "slow")
     public void beforeMethod() throws Exception {
-        //super.beforeMethod();
         // We override the parent method on purpose, because we want to register a different OverdueCheckNotifier
 
+        cleanupAllTables();
+
         mockDispatcher = new OverdueDispatcherMock(internalCallContextFactory);
         notifierForMock = new OverdueCheckNotifier(notificationQueueService, overdueProperties, internalCallContextFactory, mockDispatcher);
 
diff --git a/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java b/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
index 73d4bbd..cc247ca 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
@@ -115,6 +115,11 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
                               );
     }
 
+    @Override
+    protected void assertListenerStatus() {
+        testListener.assertListenerStatus();
+    }
+
     @BeforeClass(groups = "slow")
     protected void beforeClass() throws Exception {
         super.beforeClass();
@@ -133,16 +138,12 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
         eventBus.register(testListener);
         mockPaymentProviderPlugin.clear();
         account = testHelper.createTestAccount("bobo@gmail.com", true);
-
-        testListener.assertListenerStatus();
     }
 
     @AfterMethod(groups = "slow")
     public void afterMethod() throws Exception {
         retryService.stop();
 
-        testListener.assertListenerStatus();
-
         eventBus.unregister(handler);
         eventBus.unregister(testListener);
         super.afterMethod();

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index 6937aef..c9092c4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.140.40</version>
+        <version>0.140.42</version>
     </parent>
     <artifactId>killbill</artifactId>
     <version>0.18.14-SNAPSHOT</version>
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestEntitlement.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestEntitlement.java
index 1378fd1..582bd90 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestEntitlement.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestEntitlement.java
@@ -22,6 +22,7 @@ import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
+import java.util.regex.Pattern;
 
 import org.joda.time.DateTime;
 import org.joda.time.Interval;
@@ -258,20 +259,20 @@ public class TestEntitlement extends TestJaxrsBase {
 
         Assert.assertEquals(subscription.getEvents().size(), 3);
         Assert.assertEquals(subscription.getEvents().get(0).getEventType(), SubscriptionEventType.START_ENTITLEMENT.name());
-        Assert.assertEquals(subscription.getEvents().get(0).getPlan(), "shotgun-monthly-1");
-        Assert.assertEquals(subscription.getEvents().get(0).getPhase(), "shotgun-monthly-1-trial");
+        assertMatches(subscription.getEvents().get(0).getPlan(), "shotgun-monthly-[1-9]+");
+        assertMatches(subscription.getEvents().get(0).getPhase(), "shotgun-monthly-[1-9]+-trial");
         Assert.assertEquals(subscription.getEvents().get(0).getPriceList(), PriceListSet.DEFAULT_PRICELIST_NAME.toString());
         Assert.assertEquals(subscription.getEvents().get(0).getProduct(), "Shotgun");
 
         Assert.assertEquals(subscription.getEvents().get(1).getEventType(), SubscriptionEventType.START_BILLING.name());
-        Assert.assertEquals(subscription.getEvents().get(1).getPlan(), "shotgun-monthly-1");
-        Assert.assertEquals(subscription.getEvents().get(1).getPhase(), "shotgun-monthly-1-trial");
+        assertMatches(subscription.getEvents().get(1).getPlan(), "shotgun-monthly-[1-9]+");
+        assertMatches(subscription.getEvents().get(1).getPhase(), "shotgun-monthly-[1-9]+-trial");
         Assert.assertEquals(subscription.getEvents().get(1).getPriceList(), PriceListSet.DEFAULT_PRICELIST_NAME.toString());
         Assert.assertEquals(subscription.getEvents().get(1).getProduct(), "Shotgun");
 
         Assert.assertEquals(subscription.getEvents().get(2).getEventType(), SubscriptionEventType.PHASE.name());
-        Assert.assertEquals(subscription.getEvents().get(2).getPlan(), "shotgun-monthly-1");
-        Assert.assertEquals(subscription.getEvents().get(2).getPhase(), "shotgun-monthly-1-evergreen");
+        assertMatches(subscription.getEvents().get(2).getPlan(), "shotgun-monthly-[1-9]+");
+        assertMatches(subscription.getEvents().get(2).getPhase(), "shotgun-monthly-[1-9]+-evergreen");
         Assert.assertEquals(subscription.getEvents().get(2).getPriceList(), PriceListSet.DEFAULT_PRICELIST_NAME.toString());
         Assert.assertEquals(subscription.getEvents().get(2).getProduct(), "Shotgun");
 
@@ -297,20 +298,20 @@ public class TestEntitlement extends TestJaxrsBase {
 
         Assert.assertEquals(subscription3.getEvents().size(), 4);
         Assert.assertEquals(subscription3.getEvents().get(0).getEventType(), SubscriptionEventType.START_ENTITLEMENT.name());
-        Assert.assertEquals(subscription3.getEvents().get(0).getPlan(), "shotgun-monthly-1");
-        Assert.assertEquals(subscription3.getEvents().get(0).getPhase(), "shotgun-monthly-1-trial");
+        assertMatches(subscription3.getEvents().get(0).getPlan(), "shotgun-monthly-[1-9]+");
+        assertMatches(subscription3.getEvents().get(0).getPhase(), "shotgun-monthly-[1-9]+-trial");
         Assert.assertEquals(subscription3.getEvents().get(0).getPriceList(), PriceListSet.DEFAULT_PRICELIST_NAME.toString());
         Assert.assertEquals(subscription3.getEvents().get(0).getProduct(), "Shotgun");
 
         Assert.assertEquals(subscription3.getEvents().get(1).getEventType(), SubscriptionEventType.START_BILLING.name());
-        Assert.assertEquals(subscription3.getEvents().get(1).getPlan(), "shotgun-monthly-1");
-        Assert.assertEquals(subscription3.getEvents().get(1).getPhase(), "shotgun-monthly-1-trial");
+        assertMatches(subscription3.getEvents().get(1).getPlan(), "shotgun-monthly-[1-9]+");
+        assertMatches(subscription3.getEvents().get(1).getPhase(), "shotgun-monthly-[1-9]+-trial");
         Assert.assertEquals(subscription3.getEvents().get(1).getPriceList(), PriceListSet.DEFAULT_PRICELIST_NAME.toString());
         Assert.assertEquals(subscription3.getEvents().get(1).getProduct(), "Shotgun");
 
         Assert.assertEquals(subscription3.getEvents().get(2).getEventType(), SubscriptionEventType.PHASE.name());
-        Assert.assertEquals(subscription3.getEvents().get(2).getPlan(), "shotgun-monthly-1");
-        Assert.assertEquals(subscription3.getEvents().get(2).getPhase(), "shotgun-monthly-1-evergreen");
+        assertMatches(subscription3.getEvents().get(2).getPlan(), "shotgun-monthly-[1-9]+");
+        assertMatches(subscription3.getEvents().get(2).getPhase(), "shotgun-monthly-[1-9]+-evergreen");
         Assert.assertEquals(subscription3.getEvents().get(2).getPriceList(), PriceListSet.DEFAULT_PRICELIST_NAME.toString());
         Assert.assertEquals(subscription3.getEvents().get(2).getProduct(), "Shotgun");
 
@@ -321,6 +322,10 @@ public class TestEntitlement extends TestJaxrsBase {
         Assert.assertEquals(subscription3.getEvents().get(3).getProduct(), "Pistol");
     }
 
+    private void assertMatches(final String actual, final String regexp) {
+        Assert.assertTrue(Pattern.compile(regexp).matcher(actual).matches(), String.format("%s doesn't match pattern %s", actual, regexp));
+    }
+
     @Test(groups = "slow", description = "Create a base entitlement and also addOns entitlements under the same bundle")
     public void testEntitlementWithAddOnsWithWRITTEN_OFF() throws Exception {
         final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
index 389156a..d94d4d1 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
@@ -27,6 +27,7 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
+import org.killbill.billing.api.FlakyRetryAnalyzer;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.client.KillBillClientException;
@@ -492,7 +493,8 @@ public class TestInvoice extends TestJaxrsBase {
         assertEquals(killBillClient.getInvoicesForAccount(accountJson.getAccountId()).size(), 3);
     }
 
-    @Test(groups = "slow", description = "Can create multiple external charges with same invoice and external keys")
+    // Flaky, see https://github.com/killbill/killbill/issues/801
+    @Test(groups = "slow", description = "Can create multiple external charges with same invoice and external keys", retryAnalyzer = FlakyRetryAnalyzer.class)
     public void testExternalChargesWithSameInvoiceAndExternalKeys() throws Exception {
         final Account accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
 
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestJaxrsBase.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestJaxrsBase.java
index 66007ef..333cb3e 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestJaxrsBase.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestJaxrsBase.java
@@ -18,6 +18,9 @@
 
 package org.killbill.billing.jaxrs;
 
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadInfo;
+import java.lang.management.ThreadMXBean;
 import java.util.EventListener;
 import java.util.Iterator;
 import java.util.List;
@@ -37,7 +40,6 @@ import org.killbill.billing.GuicyKillbillTestWithEmbeddedDBModule;
 import org.killbill.billing.api.TestApiListener;
 import org.killbill.billing.client.KillBillClient;
 import org.killbill.billing.client.KillBillHttpClient;
-import org.killbill.billing.client.RequestOptions;
 import org.killbill.billing.client.model.Payment;
 import org.killbill.billing.client.model.PaymentTransaction;
 import org.killbill.billing.client.model.Tenant;
@@ -61,6 +63,8 @@ import org.killbill.billing.util.config.definition.SecurityConfig;
 import org.killbill.bus.api.PersistentBus;
 import org.killbill.commons.jdbi.guice.DaoConfig;
 import org.skife.config.ConfigurationObjectFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.AfterSuite;
 import org.testng.annotations.BeforeClass;
@@ -77,6 +81,12 @@ import com.google.inject.util.Modules;
 
 public class TestJaxrsBase extends KillbillClient {
 
+    private static final Logger log = LoggerFactory.getLogger(TestJaxrsBase.class);
+
+    protected final int DEFAULT_CONNECT_TIMEOUT_SEC = 10;
+    protected final int DEFAULT_READ_TIMEOUT_SEC = 60;
+    protected final int DEFAULT_REQUEST_TIMEOUT_SEC = DEFAULT_READ_TIMEOUT_SEC;
+
     protected static final String PLUGIN_NAME = "noop";
 
     @Inject
@@ -168,7 +178,12 @@ public class TestJaxrsBase extends KillbillClient {
                                                     username,
                                                     password,
                                                     apiKey,
-                                                    apiSecret);
+                                                    apiSecret,
+                                                    null,
+                                                    null,
+                                                    DEFAULT_CONNECT_TIMEOUT_SEC * 1000,
+                                                    DEFAULT_READ_TIMEOUT_SEC * 1000,
+                                                    DEFAULT_REQUEST_TIMEOUT_SEC * 1000);
         killBillClient = new KillBillClient(killBillHttpClient);
     }
 
@@ -294,4 +309,24 @@ public class TestJaxrsBase extends KillbillClient {
         })));
     }
 
+    protected void printThreadDump() {
+        final StringBuilder dump = new StringBuilder("Thread dump:\n");
+        final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
+        final ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(threadMXBean.getAllThreadIds(), 100);
+        for (final ThreadInfo threadInfo : threadInfos) {
+            dump.append('"');
+            dump.append(threadInfo.getThreadName());
+            dump.append("\" ");
+            final Thread.State state = threadInfo.getThreadState();
+            dump.append("\n   java.lang.Thread.State: ");
+            dump.append(state);
+            final StackTraceElement[] stackTraceElements = threadInfo.getStackTrace();
+            for (final StackTraceElement stackTraceElement : stackTraceElements) {
+                dump.append("\n        at ");
+                dump.append(stackTraceElement);
+            }
+            dump.append("\n\n");
+        }
+        log.warn(dump.toString());
+    }
 }
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPushNotification.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPushNotification.java
index 1c6b542..28eb1ee 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPushNotification.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPushNotification.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * 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
@@ -74,8 +74,16 @@ public class TestPushNotification extends TestJaxrsBase {
         callbackServer.stopServer();
     }
 
+    private void assertAllCallbacksCompleted() throws InterruptedException {
+        final boolean waitForCallbacksToComplete = waitForCallbacksToComplete();
+        if (!waitForCallbacksToComplete) {
+            printThreadDump();
+        }
+        Assert.assertTrue(waitForCallbacksToComplete, "Fail to see push notification callbacks");
+    }
+
     private boolean waitForCallbacksToComplete() throws InterruptedException {
-        long remainingMs = 30000;
+        long remainingMs = DEFAULT_REQUEST_TIMEOUT_SEC * 1000;
         do {
             if (callbackCompleted) {
                 break;
@@ -107,10 +115,7 @@ public class TestPushNotification extends TestJaxrsBase {
         // Create account to trigger a push notification
         createAccount();
 
-        final boolean success = waitForCallbacksToComplete();
-        if (!success) {
-            Assert.fail("Fail to see push notification callbacks after 5 sec");
-        }
+        assertAllCallbacksCompleted();
 
         if (callbackCompletedWithError) {
             Assert.fail("Assertion during callback failed...");
@@ -135,7 +140,7 @@ public class TestPushNotification extends TestJaxrsBase {
         final String callback = "http://127.0.0.1:" + SERVER_PORT + CALLBACK_ENDPOINT;
         final TenantKey result0 = killBillClient.registerCallbackNotificationForTenant(callback, requestOptions);
 
-        Assert.assertTrue(waitForCallbacksToComplete());
+        assertAllCallbacksCompleted();
         Assert.assertTrue(callbackCompletedWithError); // expected true because is not an ACCOUNT_CREATION event
 
         Assert.assertEquals(result0.getKey(), TenantKV.TenantKey.PUSH_NOTIFICATION_CB.toString());
@@ -167,7 +172,7 @@ public class TestPushNotification extends TestJaxrsBase {
         // Create account to trigger a push notification
         createAccount();
 
-        Assert.assertTrue(waitForCallbacksToComplete());
+        assertAllCallbacksCompleted();
         Assert.assertTrue(callbackCompletedWithError);
 
         resetCallbackStatusProperties();
@@ -175,7 +180,7 @@ public class TestPushNotification extends TestJaxrsBase {
         // move clock 15 minutes and get 1st retry
         clock.addDeltaFromReality(900000);
 
-        Assert.assertTrue(waitForCallbacksToComplete());
+        assertAllCallbacksCompleted();
         Assert.assertTrue(callbackCompletedWithError);
 
         resetCallbackStatusProperties();
@@ -183,7 +188,7 @@ public class TestPushNotification extends TestJaxrsBase {
         // move clock an hour and get 2nd retry
         clock.addDeltaFromReality(3600000);
 
-        Assert.assertTrue(waitForCallbacksToComplete());
+        assertAllCallbacksCompleted();
         Assert.assertTrue(callbackCompletedWithError);
 
         resetCallbackStatusProperties();
@@ -194,7 +199,7 @@ public class TestPushNotification extends TestJaxrsBase {
         // move clock a day, get 3rd retry and wait for a success push notification
         clock.addDays(1);
 
-        Assert.assertTrue(waitForCallbacksToComplete());
+        assertAllCallbacksCompleted();
         Assert.assertFalse(callbackCompletedWithError);
 
         unregisterTenantForCallback(callback);
@@ -221,7 +226,7 @@ public class TestPushNotification extends TestJaxrsBase {
         // Create account to trigger a push notification
         createAccount();
 
-        Assert.assertTrue(waitForCallbacksToComplete());
+        assertAllCallbacksCompleted();
         Assert.assertTrue(callbackCompletedWithError);
 
         resetCallbackStatusProperties();
@@ -229,7 +234,7 @@ public class TestPushNotification extends TestJaxrsBase {
         // move clock 15 minutes and get 1st retry
         clock.addDeltaFromReality(900000);
 
-        Assert.assertTrue(waitForCallbacksToComplete());
+        assertAllCallbacksCompleted();
         Assert.assertTrue(callbackCompletedWithError);
 
         resetCallbackStatusProperties();
@@ -237,7 +242,7 @@ public class TestPushNotification extends TestJaxrsBase {
         // move clock an hour and get 2nd retry
         clock.addDeltaFromReality(3600000);
 
-        Assert.assertTrue(waitForCallbacksToComplete());
+        assertAllCallbacksCompleted();
         Assert.assertTrue(callbackCompletedWithError);
 
         resetCallbackStatusProperties();
@@ -245,7 +250,7 @@ public class TestPushNotification extends TestJaxrsBase {
         // move clock a day and get 3rd retry
         clock.addDays(1);
 
-        Assert.assertTrue(waitForCallbacksToComplete());
+        assertAllCallbacksCompleted();
         Assert.assertTrue(callbackCompletedWithError);
 
         resetCallbackStatusProperties();
@@ -253,7 +258,7 @@ public class TestPushNotification extends TestJaxrsBase {
         // move clock a day and get 4rd retry
         clock.addDays(2);
 
-        Assert.assertTrue(waitForCallbacksToComplete());
+        assertAllCallbacksCompleted();
         Assert.assertTrue(callbackCompletedWithError);
         resetCallbackStatusProperties();
 
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java
index b90587c..e42bc0d 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java
@@ -151,6 +151,7 @@ public class SubscriptionTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
         subscriptionTestInitializer.stopTestFramework(testListener, busService, subscriptionBaseService);
     }
 
+    @Override
     protected void assertListenerStatus() {
         testListener.assertListenerStatus();
     }
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java
index e97f3e0..02ce1be 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java
@@ -115,16 +115,10 @@ public class SubscriptionTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteW
         this.accountData = subscriptionTestInitializer.initAccountData();
         final Account account = createAccount(accountData);
         this.bundle = subscriptionTestInitializer.initBundle(account.getId(), subscriptionInternalApi, internalCallContext);
-
-        // 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();
-
         subscriptionTestInitializer.stopTestFramework(testListener, busService, subscriptionBaseService);
     }
 
@@ -136,6 +130,7 @@ public class SubscriptionTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteW
         return account;
     }
 
+    @Override
     protected void assertListenerStatus() {
         testListener.assertListenerStatus();
     }
diff --git a/tenant/src/test/java/org/killbill/billing/tenant/TenantTestSuiteWithEmbeddedDb.java b/tenant/src/test/java/org/killbill/billing/tenant/TenantTestSuiteWithEmbeddedDb.java
index 060a087..45486dc 100644
--- a/tenant/src/test/java/org/killbill/billing/tenant/TenantTestSuiteWithEmbeddedDb.java
+++ b/tenant/src/test/java/org/killbill/billing/tenant/TenantTestSuiteWithEmbeddedDb.java
@@ -58,13 +58,4 @@ public class TenantTestSuiteWithEmbeddedDb extends GuicyKillbillTestSuiteWithEmb
         final Injector injector = Guice.createInjector(new TestTenantModuleWithEmbeddedDB(configSource));
         injector.injectMembers(this);
     }
-
-    @BeforeMethod(groups = "slow")
-    public void beforeMethod() throws Exception {
-        super.beforeMethod();
-    }
-
-    @AfterMethod(groups = "slow")
-    public void afterMethod() {
-    }
 }
diff --git a/util/src/main/java/org/killbill/billing/util/cache/CacheControllerDispatcherProvider.java b/util/src/main/java/org/killbill/billing/util/cache/CacheControllerDispatcherProvider.java
index 7d9ce76..2ce2b6f 100644
--- a/util/src/main/java/org/killbill/billing/util/cache/CacheControllerDispatcherProvider.java
+++ b/util/src/main/java/org/killbill/billing/util/cache/CacheControllerDispatcherProvider.java
@@ -31,6 +31,8 @@ import org.killbill.billing.util.cache.Cachable.CacheType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Preconditions;
+
 // Build the abstraction layer between EhCache and Kill Bill
 public class CacheControllerDispatcherProvider implements Provider<CacheControllerDispatcher> {
 
@@ -57,6 +59,7 @@ public class CacheControllerDispatcherProvider implements Provider<CacheControll
                 logger.warn("Cache for cacheName='{}' not configured - check your ehcache.xml", cacheLoader.getCacheType().getCacheName());
                 continue;
             }
+            Preconditions.checkState(!cache.isClosed(), "Cache '%s' should not be closed", cacheType.getCacheName());
 
             final CacheController<Object, Object> ehCacheBasedCacheController = new EhCacheBasedCacheController<Object, Object>(cache, cacheLoader);
             cacheControllers.put(cacheType, ehCacheBasedCacheController);
diff --git a/util/src/main/java/org/killbill/billing/util/glue/CacheProviderBase.java b/util/src/main/java/org/killbill/billing/util/glue/CacheProviderBase.java
index 24f2dbf..222dc7f 100644
--- a/util/src/main/java/org/killbill/billing/util/glue/CacheProviderBase.java
+++ b/util/src/main/java/org/killbill/billing/util/glue/CacheProviderBase.java
@@ -32,6 +32,7 @@ import com.codahale.metrics.Metric;
 import com.codahale.metrics.MetricFilter;
 import com.codahale.metrics.MetricRegistry;
 import com.codahale.metrics.jcache.JCacheGaugeSet;
+import com.google.common.base.Preconditions;
 
 abstract class CacheProviderBase {
 
@@ -61,6 +62,7 @@ abstract class CacheProviderBase {
         final MutableConfiguration<K, V> configuration = new MutableConfiguration<K, V>().setTypes(keyType, valueType)
                                                                                          .setStoreByValue(false); // Store by reference to avoid copying large objects (e.g. catalog)
         final Cache<K, V> cache = cacheManager.createCache(cacheName, configuration);
+        Preconditions.checkState(!cache.isClosed(), "Cache '%s' should not be closed", cacheName);
 
         // Re-create the metrics to support dynamically created caches (e.g. for Shiro)
         metricRegistry.removeMatching(new MetricFilter() {
diff --git a/util/src/main/java/org/killbill/billing/util/glue/Eh107CacheManagerProvider.java b/util/src/main/java/org/killbill/billing/util/glue/Eh107CacheManagerProvider.java
index 005afa3..d5de5cc 100644
--- a/util/src/main/java/org/killbill/billing/util/glue/Eh107CacheManagerProvider.java
+++ b/util/src/main/java/org/killbill/billing/util/glue/Eh107CacheManagerProvider.java
@@ -27,6 +27,7 @@ import javax.cache.spi.CachingProvider;
 import javax.inject.Inject;
 import javax.inject.Provider;
 
+import org.ehcache.core.spi.store.InternalCacheManager;
 import org.killbill.billing.util.cache.BaseCacheLoader;
 import org.killbill.billing.util.config.definition.EhCacheConfig;
 import org.slf4j.Logger;
@@ -38,6 +39,7 @@ import com.codahale.metrics.MetricRegistry;
 public class Eh107CacheManagerProvider extends CacheProviderBase implements Provider<CacheManager> {
 
     private static final Logger logger = LoggerFactory.getLogger(Eh107CacheManagerProvider.class);
+    private static final EhcacheLoggingListener ehcacheLoggingListener = new EhcacheLoggingListener();
 
     private final Set<BaseCacheLoader> cacheLoaders;
 
@@ -65,6 +67,10 @@ public class Eh107CacheManagerProvider extends CacheProviderBase implements Prov
             cacheManager = cachingProvider.getCacheManager();
         }
 
+        // Make sure we start from a clean state - this is mainly useful for tests
+        cacheManager.unwrap(InternalCacheManager.class).deregisterListener(ehcacheLoggingListener);
+        cacheManager.unwrap(InternalCacheManager.class).registerListener(ehcacheLoggingListener);
+
         for (final BaseCacheLoader<?, ?> cacheLoader : cacheLoaders) {
             createCache(cacheManager,
                         cacheLoader.getCacheType().getCacheName(),
diff --git a/util/src/main/java/org/killbill/billing/util/glue/EhcacheLoggingListener.java b/util/src/main/java/org/killbill/billing/util/glue/EhcacheLoggingListener.java
new file mode 100644
index 0000000..721072e
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/glue/EhcacheLoggingListener.java
@@ -0,0 +1,44 @@
+/*
+ * 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.util.glue;
+
+import org.ehcache.Cache;
+import org.ehcache.Status;
+import org.ehcache.core.events.CacheManagerListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class EhcacheLoggingListener implements CacheManagerListener {
+
+    private static final Logger logger = LoggerFactory.getLogger(EhcacheLoggingListener.class);
+
+    @Override
+    public void cacheAdded(final String alias, final Cache<?, ?> cache) {
+        logger.info("Added Ehcache '{}'", alias);
+    }
+
+    @Override
+    public void cacheRemoved(final String alias, final Cache<?, ?> cache) {
+        logger.info("Removed Ehcache '{}'", alias);
+    }
+
+    @Override
+    public void stateTransition(final Status from, final Status to) {
+        logger.info("Transitioning Ehcache from '{}' to '{}'", from, to);
+    }
+}
diff --git a/util/src/test/java/org/killbill/billing/api/FlakyInvokedMethodListener.java b/util/src/test/java/org/killbill/billing/api/FlakyInvokedMethodListener.java
new file mode 100644
index 0000000..80ad169
--- /dev/null
+++ b/util/src/test/java/org/killbill/billing/api/FlakyInvokedMethodListener.java
@@ -0,0 +1,47 @@
+/*
+ * 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.api;
+
+import org.testng.IInvokedMethod;
+import org.testng.IInvokedMethodListener;
+import org.testng.IRetryAnalyzer;
+import org.testng.ITestResult;
+import org.testng.Reporter;
+
+public class FlakyInvokedMethodListener implements IInvokedMethodListener {
+
+    @Override
+    public void beforeInvocation(final IInvokedMethod method, final ITestResult testResult) {
+    }
+
+    @Override
+    public void afterInvocation(final IInvokedMethod method, final ITestResult testResult) {
+        if (testResult.getStatus() != ITestResult.FAILURE) {
+            return;
+        }
+
+        final IRetryAnalyzer retryAnalyzer = testResult.getMethod().getRetryAnalyzer();
+        if (retryAnalyzer != null &&
+            retryAnalyzer instanceof FlakyRetryAnalyzer &&
+            !((FlakyRetryAnalyzer) retryAnalyzer).shouldRetry()) {
+            // Don't fail the build (flaky test), mark it as SKIPPED
+            testResult.setStatus(ITestResult.SKIP);
+            Reporter.setCurrentTestResult(testResult);
+        }
+    }
+}
diff --git a/util/src/test/java/org/killbill/billing/api/FlakyRetryAnalyzer.java b/util/src/test/java/org/killbill/billing/api/FlakyRetryAnalyzer.java
new file mode 100644
index 0000000..9cacc19
--- /dev/null
+++ b/util/src/test/java/org/killbill/billing/api/FlakyRetryAnalyzer.java
@@ -0,0 +1,46 @@
+/*
+ * 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.api;
+
+import org.testng.IRetryAnalyzer;
+import org.testng.ITestResult;
+
+public class FlakyRetryAnalyzer implements IRetryAnalyzer {
+
+    private static final int MAX_RETRIES = 3;
+
+    private int count = 0;
+
+    @Override
+    public boolean retry(final ITestResult iTestResult) {
+        if (iTestResult.isSuccess()) {
+            return false;
+        }
+
+        if (shouldRetry()) {
+            count++;
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public boolean shouldRetry() {
+        return count < MAX_RETRIES;
+    }
+}
diff --git a/util/src/test/java/org/killbill/billing/api/TestApiListener.java b/util/src/test/java/org/killbill/billing/api/TestApiListener.java
index 886dd99..fd0bc8c 100644
--- a/util/src/test/java/org/killbill/billing/api/TestApiListener.java
+++ b/util/src/test/java/org/killbill/billing/api/TestApiListener.java
@@ -63,7 +63,7 @@ public class TestApiListener {
 
     private static final Joiner SPACE_JOINER = Joiner.on(" ");
 
-    private static final long DELAY = 25000;
+    private static final long DELAY = 60000;
 
     private final List<NextEvent> nextExpectedEvent;
     private final IDBI idbi;
diff --git a/util/src/test/java/org/killbill/billing/DBTestingHelper.java b/util/src/test/java/org/killbill/billing/DBTestingHelper.java
index 7046879..de0640b 100644
--- a/util/src/test/java/org/killbill/billing/DBTestingHelper.java
+++ b/util/src/test/java/org/killbill/billing/DBTestingHelper.java
@@ -79,14 +79,16 @@ public class DBTestingHelper extends PlatformDBTestingHelper {
                                "CREATE TABLE accounts (\n" +
                                "    record_id serial unique,\n" +
                                "    id varchar(36) NOT NULL,\n" +
-                               "    external_key varchar(128) NULL,\n" +
-                               "    email varchar(128) NOT NULL,\n" +
-                               "    name varchar(100) NOT NULL,\n" +
-                               "    first_name_length int NOT NULL,\n" +
+                               "    external_key varchar(255) NULL,\n" +
+                               "    email varchar(128) DEFAULT NULL,\n" +
+                               "    name varchar(100) DEFAULT NULL,\n" +
+                               "    first_name_length int DEFAULT NULL,\n" +
                                "    currency varchar(3) DEFAULT NULL,\n" +
                                "    billing_cycle_day_local int DEFAULT NULL,\n" +
+                               "    parent_account_id varchar(36) DEFAULT NULL,\n" +
+                               "    is_payment_delegated_to_parent boolean DEFAULT FALSE,\n" +
                                "    payment_method_id varchar(36) DEFAULT NULL,\n" +
-                               "    time_zone varchar(50) DEFAULT NULL,\n" +
+                               "    time_zone varchar(50) NOT NULL,\n" +
                                "    locale varchar(5) DEFAULT NULL,\n" +
                                "    address1 varchar(100) DEFAULT NULL,\n" +
                                "    address2 varchar(100) DEFAULT NULL,\n" +
@@ -96,6 +98,7 @@ public class DBTestingHelper extends PlatformDBTestingHelper {
                                "    country varchar(50) DEFAULT NULL,\n" +
                                "    postal_code varchar(16) DEFAULT NULL,\n" +
                                "    phone varchar(25) DEFAULT NULL,\n" +
+                               "    notes varchar(4096) DEFAULT NULL,\n" +
                                "    migrated boolean default false,\n" +
                                "    is_notified_for_invoices boolean NOT NULL,\n" +
                                "    created_date datetime NOT NULL,\n" +
@@ -109,7 +112,7 @@ public class DBTestingHelper extends PlatformDBTestingHelper {
                                "CREATE TABLE tenants (\n" +
                                "    record_id serial unique,\n" +
                                "    id varchar(36) NOT NULL,\n" +
-                               "    external_key varchar(128) NULL,\n" +
+                               "    external_key varchar(255) NULL,\n" +
                                "    api_key varchar(128) NULL,\n" +
                                "    api_secret varchar(128) NULL,\n" +
                                "    api_salt varchar(128) NULL,\n" +
@@ -125,9 +128,10 @@ public class DBTestingHelper extends PlatformDBTestingHelper {
                                "CREATE TABLE bundles (\n" +
                                "    record_id serial unique,\n" +
                                "    id varchar(36) NOT NULL,\n" +
-                               "    external_key varchar(64) NOT NULL,\n" +
+                               "    external_key varchar(255) NOT NULL,\n" +
                                "    account_id varchar(36) NOT NULL,\n" +
                                "    last_sys_update_date datetime,\n" +
+                               "    original_created_date datetime NOT NULL,\n" +
                                "    created_by varchar(50) NOT NULL,\n" +
                                "    created_date datetime NOT NULL,\n" +
                                "    updated_by varchar(50) NOT NULL,\n" +
@@ -144,9 +148,8 @@ public class DBTestingHelper extends PlatformDBTestingHelper {
                                "    category varchar(32) NOT NULL,\n" +
                                "    start_date datetime NOT NULL,\n" +
                                "    bundle_start_date datetime NOT NULL,\n" +
-                               "    active_version int DEFAULT 1,\n" +
                                "    charged_through_date datetime DEFAULT NULL,\n" +
-                               "    paid_through_date datetime DEFAULT NULL,\n" +
+                               "    migrated bool NOT NULL default FALSE,\n" +
                                "    created_by varchar(50) NOT NULL,\n" +
                                "    created_date datetime NOT NULL,\n" +
                                "    updated_by varchar(50) NOT NULL,\n" +
@@ -162,12 +165,10 @@ public class DBTestingHelper extends PlatformDBTestingHelper {
                                "    record_id serial unique,\n" +
                                "    id varchar(36) NOT NULL,\n" +
                                "    account_id varchar(36) NOT NULL,\n" +
-                               "    invoice_id varchar(36) NOT NULL,\n" +
                                "    payment_method_id varchar(36) NOT NULL,\n" +
-                               "    amount numeric(15,9),\n" +
-                               "    currency varchar(3),\n" +
-                               "    effective_date datetime,\n" +
-                               "    payment_status varchar(50),\n" +
+                               "    external_key varchar(255) NOT NULL,\n" +
+                               "    state_name varchar(64) DEFAULT NULL,\n" +
+                               "    last_success_state_name varchar(64) DEFAULT NULL,\n" +
                                "    created_by varchar(50) NOT NULL,\n" +
                                "    created_date datetime NOT NULL,\n" +
                                "    updated_by varchar(50) NOT NULL,\n" +
diff --git a/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuite.java b/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuite.java
index c7ba40e..79008a6 100644
--- a/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuite.java
+++ b/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuite.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * 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
@@ -23,6 +23,7 @@ import java.util.UUID;
 
 import javax.inject.Inject;
 
+import org.killbill.billing.api.FlakyInvokedMethodListener;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.callcontext.MutableInternalCallContext;
 import org.killbill.billing.platform.api.KillbillConfigSource;
@@ -35,13 +36,17 @@ import org.killbill.clock.ClockMock;
 import org.skife.config.ConfigSource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.testng.IHookCallBack;
+import org.testng.IHookable;
 import org.testng.ITestResult;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Listeners;
 
 import com.google.common.collect.ImmutableMap;
 
-public class GuicyKillbillTestSuite {
+@Listeners(FlakyInvokedMethodListener.class)
+public class GuicyKillbillTestSuite implements IHookable {
 
     // Use the simple name here to save screen real estate
     protected static final Logger log = LoggerFactory.getLogger(KillbillTestSuite.class.getSimpleName());
@@ -143,6 +148,24 @@ public class GuicyKillbillTestSuite {
         }
     }
 
+    // Note: assertions should not be run in before / after hooks, as the associated test result won't be correctly updated.
+    // Use this wrapper instead.
+    @Override
+    public void run(final IHookCallBack callBack, final ITestResult testResult) {
+        // Make sure we start with a clean state
+        assertListenerStatus();
+
+        // Run the actual test
+        callBack.runTestMethod(testResult);
+
+        // Make sure we finish in a clean state
+        assertListenerStatus();
+    }
+
+    protected void assertListenerStatus() {
+        // No-op
+    }
+
     public boolean hasFailed() {
         return hasFailed;
     }
diff --git a/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuiteWithEmbeddedDB.java b/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuiteWithEmbeddedDB.java
index 565027e..757b8d4 100644
--- a/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuiteWithEmbeddedDB.java
+++ b/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuiteWithEmbeddedDB.java
@@ -24,6 +24,7 @@ import org.killbill.commons.embeddeddb.EmbeddedDB;
 import org.skife.jdbi.v2.IDBI;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.testng.Assert;
 import org.testng.annotations.AfterSuite;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.BeforeSuite;
@@ -52,11 +53,16 @@ public class GuicyKillbillTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuite
 
     @BeforeMethod(groups = "slow")
     public void beforeMethod() throws Exception {
+        cleanupAllTables();
+        controlCacheDispatcher.clearAll();
+    }
+
+    protected void cleanupAllTables() {
         try {
             DBTestingHelper.get().getInstance().cleanupAllTables();
-        } catch (final Exception ignored) {
+        } catch (final Exception e) {
+            Assert.fail("Unable to clean database", e);
         }
-        controlCacheDispatcher.clearAll();
     }
 
     @AfterSuite(groups = "slow")
diff --git a/util/src/test/java/org/killbill/billing/util/callcontext/TestInternalCallContextFactory.java b/util/src/test/java/org/killbill/billing/util/callcontext/TestInternalCallContextFactory.java
index 40e940e..c13db83 100644
--- a/util/src/test/java/org/killbill/billing/util/callcontext/TestInternalCallContextFactory.java
+++ b/util/src/test/java/org/killbill/billing/util/callcontext/TestInternalCallContextFactory.java
@@ -53,9 +53,11 @@ public class TestInternalCallContextFactory extends UtilTestSuiteWithEmbeddedDB 
                                "    id varchar(36) NOT NULL,\n" +
                                "    account_id varchar(36) NOT NULL,\n" +
                                "    invoice_date date NOT NULL,\n" +
-                               "    target_date date NOT NULL,\n" +
+                               "    target_date date,\n" +
                                "    currency varchar(3) NOT NULL,\n" +
+                               "    status varchar(15) NOT NULL DEFAULT 'COMMITTED',\n" +
                                "    migrated bool NOT NULL,\n" +
+                               "    parent_invoice bool NOT NULL DEFAULT FALSE,\n" +
                                "    created_by varchar(50) NOT NULL,\n" +
                                "    created_date datetime NOT NULL,\n" +
                                "    account_record_id bigint /*! unsigned */ not null,\n" +
@@ -86,8 +88,8 @@ public class TestInternalCallContextFactory extends UtilTestSuiteWithEmbeddedDB 
             @Override
             public Void withHandle(final Handle handle) throws Exception {
                 // Note: we always create an accounts table, see MysqlTestingHelper
-                handle.execute("insert into accounts (record_id, id, email, name, first_name_length, is_notified_for_invoices, created_date, created_by, updated_date, updated_by) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
-                               accountRecordId, accountId.toString(), "yo@t.com", "toto", 4, false, new Date(), "i", new Date(), "j");
+                handle.execute("insert into accounts (record_id, id, email, name, first_name_length, time_zone, is_notified_for_invoices, created_date, created_by, updated_date, updated_by) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+                               accountRecordId, accountId.toString(), "yo@t.com", "toto", 4, "UTC", false, new Date(), "i", new Date(), "j");
                 return null;
             }
         });
diff --git a/util/src/test/java/org/killbill/billing/util/customfield/api/TestDefaultCustomFieldUserApi.java b/util/src/test/java/org/killbill/billing/util/customfield/api/TestDefaultCustomFieldUserApi.java
index ffe76e3..fcddf41 100644
--- a/util/src/test/java/org/killbill/billing/util/customfield/api/TestDefaultCustomFieldUserApi.java
+++ b/util/src/test/java/org/killbill/billing/util/customfield/api/TestDefaultCustomFieldUserApi.java
@@ -53,8 +53,8 @@ public class TestDefaultCustomFieldUserApi extends UtilTestSuiteWithEmbeddedDB {
             @Override
             public Void withHandle(final Handle handle) throws Exception {
                 // Note: we always create an accounts table, see MysqlTestingHelper
-                handle.execute("insert into accounts (record_id, id, email, name, first_name_length, is_notified_for_invoices, created_date, created_by, updated_date, updated_by) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
-                               accountRecordId, accountId.toString(), "yo@t.com", "toto", 4, false, new Date(), "i", new Date(), "j");
+                handle.execute("insert into accounts (record_id, id, email, name, first_name_length, time_zone, is_notified_for_invoices, created_date, created_by, updated_date, updated_by) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+                               accountRecordId, accountId.toString(), "yo@t.com", "toto", 4, "UTC", false, new Date(), "i", new Date(), "j");
 
                 return null;
             }
diff --git a/util/src/test/java/org/killbill/billing/util/dao/TestNonEntityDao.java b/util/src/test/java/org/killbill/billing/util/dao/TestNonEntityDao.java
index 8b9c231..2319573 100644
--- a/util/src/test/java/org/killbill/billing/util/dao/TestNonEntityDao.java
+++ b/util/src/test/java/org/killbill/billing/util/dao/TestNonEntityDao.java
@@ -96,8 +96,8 @@ public class TestNonEntityDao extends UtilTestSuiteWithEmbeddedDB {
             @Override
             public Void withHandle(final Handle handle) throws Exception {
                 // Note: we always create an accounts table, see MysqlTestingHelper
-                handle.execute("insert into accounts (record_id, id, email, name, first_name_length, is_notified_for_invoices, created_date, created_by, updated_date, updated_by, tenant_record_id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
-                               accountRecordId, accountId.toString(), "zozo@tt.com", "zozo", 4, false, new Date(), "i", new Date(), "j", tenantRecordId);
+                handle.execute("insert into accounts (record_id, id, email, name, first_name_length, time_zone, is_notified_for_invoices, created_date, created_by, updated_date, updated_by, tenant_record_id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+                               accountRecordId, accountId.toString(), "zozo@tt.com", "zozo", 4, "UTC", false, new Date(), "i", new Date(), "j", tenantRecordId);
                 return null;
             }
         });
@@ -108,8 +108,8 @@ public class TestNonEntityDao extends UtilTestSuiteWithEmbeddedDB {
             @Override
             public Void withHandle(final Handle handle) throws Exception {
                 // Note: we always create an accounts table, see MysqlTestingHelper
-                handle.execute("insert into account_history (record_id, id, email, name, first_name_length, is_notified_for_invoices, created_date, created_by, updated_date, updated_by, tenant_record_id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
-                               accountRecordId, accountId.toString(), "zozo@tt.com", "zozo", 4, false, new Date(), "i", new Date(), "j", tenantRecordId);
+                handle.execute("insert into account_history (record_id, id, email, name, first_name_length, time_zone, is_notified_for_invoices, created_date, created_by, updated_date, updated_by, tenant_record_id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+                               accountRecordId, accountId.toString(), "zozo@tt.com", "zozo", 4, "UTC", false, new Date(), "i", new Date(), "j", tenantRecordId);
                 return null;
             }
         });
diff --git a/util/src/test/java/org/killbill/billing/util/export/dao/TestDatabaseExportDao.java b/util/src/test/java/org/killbill/billing/util/export/dao/TestDatabaseExportDao.java
index 01fe087..8c26cbe 100644
--- a/util/src/test/java/org/killbill/billing/util/export/dao/TestDatabaseExportDao.java
+++ b/util/src/test/java/org/killbill/billing/util/export/dao/TestDatabaseExportDao.java
@@ -46,6 +46,7 @@ public class TestDatabaseExportDao extends UtilTestSuiteWithEmbeddedDB {
         final String accountEmail = UUID.randomUUID().toString().substring(0, 4) + '@' + UUID.randomUUID().toString().substring(0, 4);
         final String accountName = UUID.randomUUID().toString().substring(0, 4);
         final int firstNameLength = 4;
+        final String timeZone = "UTC";
         final boolean isNotifiedForInvoices = false;
         final Date createdDate = new Date(12421982000L);
         final String createdBy = UUID.randomUUID().toString().substring(0, 4);
@@ -77,17 +78,17 @@ public class TestDatabaseExportDao extends UtilTestSuiteWithEmbeddedDB {
                                internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId());
 
                 // Add row in accounts table
-                handle.execute("insert into accounts (record_id, id, email, name, first_name_length, is_notified_for_invoices, created_date, created_by, updated_date, updated_by, tenant_record_id) " +
-                               "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
-                               internalCallContext.getAccountRecordId(), accountId, accountEmail, accountName, firstNameLength, isNotifiedForInvoices, createdDate, createdBy, updatedDate, updatedBy, internalCallContext.getTenantRecordId());
+                handle.execute("insert into accounts (record_id, id, email, name, first_name_length, time_zone, is_notified_for_invoices, created_date, created_by, updated_date, updated_by, tenant_record_id) " +
+                               "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+                               internalCallContext.getAccountRecordId(), accountId, accountEmail, accountName, firstNameLength, timeZone, isNotifiedForInvoices, createdDate, createdBy, updatedDate, updatedBy, internalCallContext.getTenantRecordId());
                 return null;
             }
         });
 
         // Verify new dump
         final String newDump = getDump();
-        Assert.assertEquals(newDump, "-- accounts record_id|id|external_key|email|name|first_name_length|currency|billing_cycle_day_local|payment_method_id|time_zone|locale|address1|address2|company_name|city|state_or_province|country|postal_code|phone|migrated|is_notified_for_invoices|created_date|created_by|updated_date|updated_by|tenant_record_id\n" +
-                                     String.format("%s|%s||%s|%s|%s||||||||||||||false|%s|%s|%s|%s|%s|%s", internalCallContext.getAccountRecordId(), accountId, accountEmail, accountName, firstNameLength,
+        Assert.assertEquals(newDump, "-- accounts record_id|id|external_key|email|name|first_name_length|currency|billing_cycle_day_local|parent_account_id|is_payment_delegated_to_parent|payment_method_id|time_zone|locale|address1|address2|company_name|city|state_or_province|country|postal_code|phone|notes|migrated|is_notified_for_invoices|created_date|created_by|updated_date|updated_by|tenant_record_id\n" +
+                                     String.format("%s|%s||%s|%s|%s||||false||%s|||||||||||false|%s|%s|%s|%s|%s|%s", internalCallContext.getAccountRecordId(), accountId, accountEmail, accountName, firstNameLength, timeZone,
                                                    isNotifiedForInvoices, "1970-05-24T18:33:02.000+0000", createdBy, "1982-02-18T20:03:42.000+0000", updatedBy, internalCallContext.getTenantRecordId()) + "\n" +
                                      "-- " + tableNameA + " record_id|a_column|blob_column|account_record_id|tenant_record_id\n" +
                                      "1|a|WlYAAARjYWZl|" + internalCallContext.getAccountRecordId() + "|" + internalCallContext.getTenantRecordId() + "\n" +
diff --git a/util/src/test/java/org/killbill/billing/util/tag/api/TestDefaultTagUserApi.java b/util/src/test/java/org/killbill/billing/util/tag/api/TestDefaultTagUserApi.java
index bbdfb0f..290a472 100644
--- a/util/src/test/java/org/killbill/billing/util/tag/api/TestDefaultTagUserApi.java
+++ b/util/src/test/java/org/killbill/billing/util/tag/api/TestDefaultTagUserApi.java
@@ -52,8 +52,8 @@ public class TestDefaultTagUserApi extends UtilTestSuiteWithEmbeddedDB {
             @Override
             public Void withHandle(final Handle handle) throws Exception {
                 // Note: we always create an accounts table, see MysqlTestingHelper
-                handle.execute("insert into accounts (record_id, id, email, name, first_name_length, is_notified_for_invoices, created_date, created_by, updated_date, updated_by) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
-                               accountRecordId, accountId.toString(), "yo@t.com", "toto", 4, false, new Date(), "i", new Date(), "j");
+                handle.execute("insert into accounts (record_id, id, email, name, first_name_length, time_zone, is_notified_for_invoices, created_date, created_by, updated_date, updated_by) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+                               accountRecordId, accountId.toString(), "yo@t.com", "toto", 4, "UTC", false, new Date(), "i", new Date(), "j");
 
                 return null;
             }
diff --git a/util/src/test/java/org/killbill/billing/util/UtilTestSuiteWithEmbeddedDB.java b/util/src/test/java/org/killbill/billing/util/UtilTestSuiteWithEmbeddedDB.java
index 503a229..0569958 100644
--- a/util/src/test/java/org/killbill/billing/util/UtilTestSuiteWithEmbeddedDB.java
+++ b/util/src/test/java/org/killbill/billing/util/UtilTestSuiteWithEmbeddedDB.java
@@ -123,20 +123,15 @@ public abstract class UtilTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuite
 
         eventBus.start();
         eventBus.register(eventsListener);
-
-        // 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();
-
         eventBus.unregister(eventsListener);
         eventBus.stop();
     }
 
+    @Override
     protected void assertListenerStatus() {
         eventsListener.assertListenerStatus();
     }