killbill-aplcache

Merge remote-tracking branch 'origin/test-fixes' into test-fixes-doc Signed-off-by:

5/2/2018 8:09:53 AM

Details

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 ce7474f..5a996c0 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
@@ -356,6 +356,10 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
 
     @AfterMethod(groups = "slow")
     public void afterMethod() throws Exception {
+        if (hasFailed()) {
+            return;
+        }
+
         lifecycle.fireShutdownSequencePriorEventUnRegistration();
         busService.getBus().unregister(busHandler);
         lifecycle.fireShutdownSequencePostEventUnRegistration();
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/CallbackServlet.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/CallbackServlet.java
index 9f47378..eedba91 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/CallbackServlet.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/CallbackServlet.java
@@ -22,6 +22,8 @@ import java.io.InputStreamReader;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.Stack;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
@@ -56,8 +58,18 @@ public class CallbackServlet extends HttpServlet {
     private String listenerFailedMsg;
     private boolean completed = true;
 
+    final AtomicInteger receivedCalls = new AtomicInteger(0);
+    final AtomicBoolean forceToFail = new AtomicBoolean(false);
+
     @Override
     protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
+        final int current = receivedCalls.incrementAndGet();
+        if (forceToFail.get()) {
+            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            log.info("CallmebackServlet is forced to fail for testing purposes");
+            return;
+        }
+
         final String body = CharStreams.toString(new InputStreamReader(request.getInputStream(), "UTF-8"));
         response.setStatus(HttpServletResponse.SC_OK);
 
@@ -87,6 +99,8 @@ public class CallbackServlet extends HttpServlet {
     }
 
     public synchronized void reset() {
+        receivedCalls.set(0);
+        forceToFail.set(false);
         nextExpectedEvent.clear();
         completed = true;
 
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/KillbillClient.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/KillbillClient.java
index 1b11937..ce48bda 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/KillbillClient.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/KillbillClient.java
@@ -203,14 +203,18 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
 
     protected Account createAccount(final UUID parentAccountId) throws Exception {
         callbackServlet.pushExpectedEvent(ExtBusEventType.ACCOUNT_CREATION);
-        final Account input = getAccount(parentAccountId);
-        final Account account = accountApi.createAccount(input, requestOptions);
+        final Account account = createAccountNoEvent(parentAccountId);
         callbackServlet.assertListenerStatus();
         return account;
     }
 
+    protected Account createAccountNoEvent(final UUID parentAccountId) throws KillBillClientException {
+        final Account input = getAccount(parentAccountId);
+        return accountApi.createAccount(input, requestOptions);
+    }
+
     protected Subscription createSubscription(final UUID accountId, final String bundleExternalKey, final String productName,
-                                             final ProductCategory productCategory, final BillingPeriod billingPeriod, final boolean waitCompletion) throws Exception {
+                                              final ProductCategory productCategory, final BillingPeriod billingPeriod, final boolean waitCompletion) throws Exception {
         final Account account = accountApi.getAccount(accountId, requestOptions);
         if (account.getBillCycleDayLocal() == null || account.getBillCycleDayLocal() == 0) {
             callbackServlet.pushExpectedEvent(ExtBusEventType.ACCOUNT_CHANGE);
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 9e752f6..d7d1d89 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
@@ -18,132 +18,31 @@
 
 package org.killbill.billing.jaxrs;
 
-import java.io.IOException;
-import java.io.InputStreamReader;
 import java.util.UUID;
 import java.util.concurrent.Callable;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
 import org.awaitility.Awaitility;
 import org.joda.time.DateTime;
 import org.killbill.CreatorName;
-import org.killbill.billing.api.FlakyRetryAnalyzer;
-import org.killbill.billing.client.KillBillClientException;
-import org.killbill.billing.client.model.gen.TenantKeyValue;
-import org.killbill.billing.jaxrs.json.NotificationJson;
 import org.killbill.billing.notification.plugin.api.ExtBusEventType;
 import org.killbill.billing.server.DefaultServerService;
 import org.killbill.billing.server.notifications.PushNotificationKey;
-import org.killbill.billing.server.notifications.PushNotificationListener;
-import org.killbill.billing.tenant.api.TenantKV;
 import org.killbill.notificationq.NotificationQueueDispatcher;
 import org.killbill.notificationq.api.NotificationEvent;
 import org.killbill.notificationq.api.NotificationQueue;
 import org.killbill.notificationq.api.NotificationQueueService.NotificationQueueHandler;
 import org.killbill.notificationq.dao.NotificationEventModelDao;
 import org.killbill.queue.QueueObjectMapper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.testng.Assert;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.io.CharStreams;
 
 public class TestPushNotification extends TestJaxrsBase {
 
-    private CallbackServer callbackServer;
-
-    private volatile boolean callbackCompleted;
-    private volatile boolean callbackCompletedWithError;
-    private volatile int expectedNbCalls = 1;
-    private volatile boolean forceToFail = false;
-    private volatile int failedResponseStatus = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
-
-    @Override
-    @BeforeMethod(groups = "slow")
-    public void beforeMethod() throws Exception {
-        if (hasFailed()) {
-            return;
-        }
-
-        super.beforeMethod();
-        callbackServer = new CallbackServer(new CallmebackServlet(this));
-        resetCallbackStatusProperties();
-        callbackServer.startServer();
-        this.expectedNbCalls = 1;
-    }
-
-    @AfterMethod(groups = "slow")
-    public void afterMethod() throws Exception {
-        if (hasFailed()) {
-            return;
-        }
-
-        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 = DEFAULT_REQUEST_TIMEOUT_SEC * 1000;
-        do {
-            if (callbackCompleted) {
-                break;
-            }
-            Thread.sleep(100);
-            remainingMs -= 100;
-        } while (remainingMs > 0);
-        return (remainingMs > 0);
-    }
-
-    public void retrieveAccountWithAsserts(final UUID accountId) {
-        try {
-            // Just check we can retrieve the account with the id from the callback
-            accountApi.getAccount(accountId, requestOptions);
-        } catch (final Exception e) {
-            Assert.fail(e.getMessage());
-        }
-    }
-
-    @Test(groups = "slow")
-    public void testPushNotification() throws Exception {
-        final String callback = registerTenantForCallback();
-
-        // set expected number of calls
-        // 1st: was "eventType":"TENANT_CONFIG_CHANGE"
-        // 2nd: is "eventType":"ACCOUNT_CREATION"
-        this.expectedNbCalls = 2;
-
-        // Create account to trigger a push notification
-        createAccount();
-
-        assertAllCallbacksCompleted();
-
-        if (callbackCompletedWithError) {
-            Assert.fail("Assertion during callback failed...");
-        }
-
-        unregisterTenantForCallback(callback);
-    }
-
     @Test(groups = "slow", description = "https://github.com/killbill/killbill/issues/726")
     public void testVerify726Backport() throws Exception {
         // Record an event without the metadata field
@@ -210,233 +109,170 @@ public class TestPushNotification extends TestJaxrsBase {
         Assert.assertNull(retrievedKey.getMetaData());
     }
 
-    private void unregisterTenantForCallback(final String callback) throws KillBillClientException {
-        final TenantKeyValue result = tenantApi.registerPushNotificationCallback(callback, requestOptions);
-        Assert.assertEquals(result.getKey(), TenantKV.TenantKey.PUSH_NOTIFICATION_CB.toString());
-        Assert.assertEquals(result.getValues().size(), 1);
-        Assert.assertEquals(result.getValues().get(0), callback);
-
-        tenantApi.deletePushNotificationCallbacks(requestOptions);
-        final TenantKeyValue result2 = tenantApi.getPushNotificationCallbacks(requestOptions);
-        Assert.assertEquals(result2.getKey(), TenantKV.TenantKey.PUSH_NOTIFICATION_CB.toString());
-        Assert.assertEquals(result2.getValues().size(), 0);
-    }
-
-    private String registerTenantForCallback() throws KillBillClientException, InterruptedException {// Register tenant for callback
-        final String callback = callbackServer.getServletEndpoint();
-        final TenantKeyValue result0 = tenantApi.registerPushNotificationCallback(callback, requestOptions);
-
-        Assert.assertTrue(waitForCallbacksToComplete());
-
-        Assert.assertEquals(result0.getKey(), TenantKV.TenantKey.PUSH_NOTIFICATION_CB.toString());
-        Assert.assertEquals(result0.getValues().size(), 1);
-        Assert.assertEquals(result0.getValues().get(0), callback);
-
-        // reset values
-        resetCallbackStatusProperties();
-
-        return callback;
-    }
-
-    // Flaky, see https://github.com/killbill/killbill/issues/860
-    @Test(groups = "slow", retryAnalyzer = FlakyRetryAnalyzer.class)
+    @Test(groups = "slow")
     public void testPushNotificationRetries() throws Exception {
-        final String callback = registerTenantForCallback();
+        Assert.assertEquals(callbackServlet.receivedCalls.get(), 1);
 
         // force server to fail
         // Notifications retries are set to:
         // org.killbill.billing.server.notifications.retries=15m,1h,1d,2d
-        this.forceToFail = true;
+        callbackServlet.forceToFail.set(true);
 
-        // set expected number of calls
         // 1st: was "eventType":"TENANT_CONFIG_CHANGE"
         // 2nd: is original "eventType":"ACCOUNT_CREATION" call [force error]
         // 3rd: is 1st notification retry (+ 15m) [force error]
         // 4th: is 1st notification retry (+ 1h) [force error]
         // 5th: is 1st notification retry (+ 1d) [success]
-        this.expectedNbCalls = 5;
 
         // Create account to trigger a push notification
-        createAccount();
-
-        assertAllCallbacksCompleted();
-        Assert.assertTrue(callbackCompletedWithError);
-
-        resetCallbackStatusProperties();
+        createAccountNoEvent(null);
+        Awaitility.await()
+                  .atMost(10, TimeUnit.SECONDS)
+                  .until(new Callable<Boolean>() {
+                      @Override
+                      public Boolean call() throws Exception {
+                          return callbackServlet.receivedCalls.get() == 2;
+                      }
+                  });
+        callbackServlet.assertListenerStatus();
+        Assert.assertEquals(callbackServlet.receivedCalls.get(), 2);
 
         // move clock 15 minutes and get 1st retry
         clock.addDeltaFromReality(900000);
 
-        assertAllCallbacksCompleted();
-        Assert.assertTrue(callbackCompletedWithError);
-
-        resetCallbackStatusProperties();
+        Awaitility.await()
+                  .atMost(10, TimeUnit.SECONDS)
+                  .until(new Callable<Boolean>() {
+                      @Override
+                      public Boolean call() throws Exception {
+                          return callbackServlet.receivedCalls.get() == 3;
+                      }
+                  });
+        callbackServlet.assertListenerStatus();
+        Assert.assertEquals(callbackServlet.receivedCalls.get(), 3);
 
         // move clock an hour and get 2nd retry
         clock.addDeltaFromReality(3600000);
 
-        assertAllCallbacksCompleted();
-        Assert.assertTrue(callbackCompletedWithError);
-
-        resetCallbackStatusProperties();
+        Awaitility.await()
+                  .atMost(10, TimeUnit.SECONDS)
+                  .until(new Callable<Boolean>() {
+                      @Override
+                      public Boolean call() throws Exception {
+                          return callbackServlet.receivedCalls.get() == 4;
+                      }
+                  });
+        callbackServlet.assertListenerStatus();
+        Assert.assertEquals(callbackServlet.receivedCalls.get(), 4);
 
         // make call success
-        this.forceToFail = false;
+        callbackServlet.pushExpectedEvents(ExtBusEventType.ACCOUNT_CREATION);
+        callbackServlet.forceToFail.set(false);
 
         // move clock a day, get 3rd retry and wait for a success push notification
         clock.addDays(1);
 
-        assertAllCallbacksCompleted();
-        Assert.assertFalse(callbackCompletedWithError);
-
-        unregisterTenantForCallback(callback);
+        Awaitility.await()
+                  .atMost(10, TimeUnit.SECONDS)
+                  .until(new Callable<Boolean>() {
+                      @Override
+                      public Boolean call() throws Exception {
+                          return callbackServlet.receivedCalls.get() == 5;
+                      }
+                  });
+        callbackServlet.assertListenerStatus();
+        Assert.assertEquals(callbackServlet.receivedCalls.get(), 5);
     }
 
-    // Flaky, see https://github.com/killbill/killbill/issues/860
-    @Test(groups = "slow", retryAnalyzer = FlakyRetryAnalyzer.class)
+    @Test(groups = "slow")
     public void testPushNotificationRetriesMaxAttemptNumber() throws Exception {
-        final String callback = registerTenantForCallback();
+        Assert.assertEquals(callbackServlet.receivedCalls.get(), 1);
 
         // force server to fail
         // Notifications retries are set to:
         // org.killbill.billing.server.notifications.retries=15m,1h,1d,2d
-        this.forceToFail = true;
+        callbackServlet.forceToFail.set(true);
 
-        // set expected number of calls
         // 1st: was "eventType":"TENANT_CONFIG_CHANGE"
         // 2nd: is original "eventType":"ACCOUNT_CREATION" call [force error]
         // 3rd: is 1st notification retry (+ 15m) [force error]
         // 4th: is 2nd notification retry (+ 1h) [force error]
         // 5th: is 3rd notification retry (+ 1d) [force error]
         // 6th: is 4th notification retry (+ 2d) [force error]
-        this.expectedNbCalls = 6;
 
         // Create account to trigger a push notification
-        createAccount();
-
-        assertAllCallbacksCompleted();
-        Assert.assertTrue(callbackCompletedWithError);
-
-        resetCallbackStatusProperties();
+        createAccountNoEvent(null);
+        Awaitility.await()
+                  .atMost(10, TimeUnit.SECONDS)
+                  .until(new Callable<Boolean>() {
+                      @Override
+                      public Boolean call() throws Exception {
+                          return callbackServlet.receivedCalls.get() == 2;
+                      }
+                  });
+        callbackServlet.assertListenerStatus();
+        Assert.assertEquals(callbackServlet.receivedCalls.get(), 2);
 
         // move clock 15 minutes (+10s for flakiness) and get 1st retry
         clock.addDeltaFromReality(910000);
 
-        assertAllCallbacksCompleted();
-        Assert.assertTrue(callbackCompletedWithError);
-
-        resetCallbackStatusProperties();
+        Awaitility.await()
+                  .atMost(10, TimeUnit.SECONDS)
+                  .until(new Callable<Boolean>() {
+                      @Override
+                      public Boolean call() throws Exception {
+                          return callbackServlet.receivedCalls.get() == 3;
+                      }
+                  });
+        callbackServlet.assertListenerStatus();
+        Assert.assertEquals(callbackServlet.receivedCalls.get(), 3);
 
         // move clock an hour (+10s for flakiness) and get 2nd retry
         clock.addDeltaFromReality(3610000);
 
-        assertAllCallbacksCompleted();
-        Assert.assertTrue(callbackCompletedWithError);
-
-        resetCallbackStatusProperties();
+        Awaitility.await()
+                  .atMost(10, TimeUnit.SECONDS)
+                  .until(new Callable<Boolean>() {
+                      @Override
+                      public Boolean call() throws Exception {
+                          return callbackServlet.receivedCalls.get() == 4;
+                      }
+                  });
+        callbackServlet.assertListenerStatus();
+        Assert.assertEquals(callbackServlet.receivedCalls.get(), 4);
 
         // move clock a day and get 3rd retry
         clock.addDays(1);
 
-        assertAllCallbacksCompleted();
-        Assert.assertTrue(callbackCompletedWithError);
-
-        resetCallbackStatusProperties();
-
-        // move clock a day and get 4rd retry
+        Awaitility.await()
+                  .atMost(10, TimeUnit.SECONDS)
+                  .until(new Callable<Boolean>() {
+                      @Override
+                      public Boolean call() throws Exception {
+                          return callbackServlet.receivedCalls.get() == 5;
+                      }
+                  });
+        callbackServlet.assertListenerStatus();
+        Assert.assertEquals(callbackServlet.receivedCalls.get(), 5);
+
+        // move clock a day and get 4th retry
         clock.addDays(2);
 
-        assertAllCallbacksCompleted();
-        Assert.assertTrue(callbackCompletedWithError);
-        resetCallbackStatusProperties();
+        Awaitility.await()
+                  .atMost(10, TimeUnit.SECONDS)
+                  .until(new Callable<Boolean>() {
+                      @Override
+                      public Boolean call() throws Exception {
+                          return callbackServlet.receivedCalls.get() == 6;
+                      }
+                  });
+        callbackServlet.assertListenerStatus();
+        Assert.assertEquals(callbackServlet.receivedCalls.get(), 6);
 
         clock.addDays(4);
 
-        Assert.assertFalse(waitForCallbacksToComplete());
-        Assert.assertFalse(callbackCompletedWithError);
-
-        unregisterTenantForCallback(callback);
-    }
-
-    private void resetCallbackStatusProperties() {
-        // reset values
-        this.callbackCompleted = false;
-        this.callbackCompletedWithError = false;
-    }
-
-    public void setCompleted(final boolean withError) {
-        callbackCompleted = true;
-        callbackCompletedWithError = withError;
-    }
-
-    public static class CallmebackServlet extends HttpServlet {
-
-        private static final long serialVersionUID = -5181211514918217301L;
-
-        private static final Logger log = LoggerFactory.getLogger(CallmebackServlet.class);
-
-        private final AtomicInteger receivedCalls;
-        private final TestPushNotification test;
-        private final ObjectMapper objectMapper = new ObjectMapper();
-
-        private boolean withError;
-
-        public CallmebackServlet(final TestPushNotification test) {
-            this.test = test;
-            this.receivedCalls = new AtomicInteger(0);
-        }
-
-        @Override
-        protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
-            final int current = receivedCalls.incrementAndGet();
-            final String body = CharStreams.toString(new InputStreamReader(request.getInputStream(), "UTF-8"));
-
-            log.info("CallmebackServlet received {} calls , current = {} at {}", current, body, getClock().getUTCNow());
-
-            if (test.forceToFail) {
-                response.setStatus(test.failedResponseStatus);
-                log.info("CallmebackServlet is force to fail for testing purposes");
-                test.setCompleted(true);
-                return;
-            }
-
-            response.setStatus(HttpServletResponse.SC_OK);
-
-            log.info("Got body {}", body);
-
-            final NotificationJson notification = objectMapper.readValue(body, NotificationJson.class);
-
-            final ExtBusEventType type = ExtBusEventType.valueOf(notification.getEventType());
-            switch (type) {
-                case TENANT_CONFIG_CHANGE:
-                    Assert.assertEquals(notification.getEventType(), "TENANT_CONFIG_CHANGE");
-                    Assert.assertEquals(notification.getObjectType(), "TENANT_KVS");
-                    Assert.assertNotNull(notification.getObjectId());
-                    Assert.assertNull(notification.getAccountId());
-                    Assert.assertNotNull(notification.getMetaData());
-                    Assert.assertEquals(notification.getMetaData(), "PUSH_NOTIFICATION_CB");
-                    break;
-                case ACCOUNT_CREATION:
-                    Assert.assertEquals(notification.getEventType(), "ACCOUNT_CREATION");
-                    Assert.assertEquals(notification.getObjectType(), "ACCOUNT");
-                    Assert.assertNotNull(notification.getObjectId());
-                    Assert.assertNotNull(notification.getAccountId());
-                    Assert.assertEquals(notification.getObjectId(), notification.getAccountId());
-                    break;
-            }
-
-            test.retrieveAccountWithAsserts(notification.getObjectId());
-
-            Assert.assertEquals(request.getHeader(PushNotificationListener.HTTP_HEADER_CONTENT_TYPE), PushNotificationListener.CONTENT_TYPE_JSON);
-            stopServerWhenComplete(current, false);
-        }
-
-        private void stopServerWhenComplete(final int current, final boolean withError) {
-            if (current == test.expectedNbCalls) {
-                log.info("Excellent, we are done!");
-                test.setCompleted(withError);
-            }
-        }
+        callbackServlet.assertListenerStatus();
+        Assert.assertEquals(callbackServlet.receivedCalls.get(), 6);
     }
 
     public static final class PushNotificationKeyPre726 implements NotificationEvent {
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 a2acf84..90f54ba 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
@@ -50,6 +50,10 @@ public class TestDefaultCustomFieldUserApi extends UtilTestSuiteWithEmbeddedDB {
     @Override
     @BeforeMethod(groups = "slow")
     public void beforeMethod() throws Exception {
+        if (hasFailed()) {
+            return;
+        }
+
         super.beforeMethod();
 
         final ImmutableAccountData immutableAccountData = Mockito.mock(ImmutableAccountData.class);