killbill-memoizeit

Added a test for payment retries

2/29/2012 3:18:05 PM

Details

diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java b/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
index fd84927..57c76a0 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
@@ -39,7 +39,7 @@ public interface PaymentApi {
 
     List<Either<PaymentError, PaymentInfo>> createPayment(String accountKey, List<String> invoiceIds);
     List<Either<PaymentError, PaymentInfo>> createPayment(Account account, List<String> invoiceIds);
-    Either<PaymentError, PaymentInfo> createPayment(UUID paymentAttemptId);
+    Either<PaymentError, PaymentInfo> createPaymentForPaymentAttempt(UUID paymentAttemptId);
 
     List<Either<PaymentError, PaymentInfo>> createRefund(Account account, List<String> invoiceIds); //TODO
 
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
index d2f5c7c..7f40a5c 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
@@ -25,7 +25,6 @@ import javax.annotation.Nullable;
 
 import org.apache.commons.lang.StringUtils;
 import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -35,6 +34,7 @@ import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoicePaymentApi;
 import com.ning.billing.invoice.model.DefaultInvoicePayment;
+import com.ning.billing.payment.RetryService;
 import com.ning.billing.payment.dao.PaymentDao;
 import com.ning.billing.payment.provider.PaymentProviderPlugin;
 import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
@@ -44,6 +44,7 @@ public class DefaultPaymentApi implements PaymentApi {
     private final PaymentProviderPluginRegistry pluginRegistry;
     private final AccountUserApi accountUserApi;
     private final InvoicePaymentApi invoicePaymentApi;
+    private final RetryService retryService;
     private final PaymentDao paymentDao;
     private final PaymentConfig config;
 
@@ -53,11 +54,13 @@ public class DefaultPaymentApi implements PaymentApi {
     public DefaultPaymentApi(PaymentProviderPluginRegistry pluginRegistry,
                              AccountUserApi accountUserApi,
                              InvoicePaymentApi invoicePaymentApi,
+                             RetryService retryService,
                              PaymentDao paymentDao,
                              PaymentConfig config) {
         this.pluginRegistry = pluginRegistry;
         this.accountUserApi = accountUserApi;
         this.invoicePaymentApi = invoicePaymentApi;
+        this.retryService = retryService;
         this.paymentDao = paymentDao;
         this.config = config;
     }
@@ -134,7 +137,7 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     @Override
-    public Either<PaymentError, PaymentInfo> createPayment(UUID paymentAttemptId) {
+    public Either<PaymentError, PaymentInfo> createPaymentForPaymentAttempt(UUID paymentAttemptId) {
         PaymentAttempt paymentAttempt = paymentDao.getPaymentAttemptById(paymentAttemptId);
 
         if (paymentAttempt != null) {
@@ -235,7 +238,7 @@ public class DefaultPaymentApi implements PaymentApi {
 
         if (retryCount < retryDays.size()) {
             int retryInDays = 0;
-            DateTime nextRetryDate = new DateTime(DateTimeZone.UTC);
+            DateTime nextRetryDate = paymentAttempt.getPaymentAttemptDate();
 
             try {
                 retryInDays = retryDays.get(retryCount);
@@ -245,6 +248,7 @@ public class DefaultPaymentApi implements PaymentApi {
                 log.error("Could not get retry day for retry count {}", retryCount);
             }
 
+            retryService.scheduleRetry(paymentAttempt, nextRetryDate);
             paymentDao.updatePaymentAttemptWithRetryInfo(paymentAttempt.getPaymentAttemptId(), retryCount + 1, nextRetryDate);
         }
         else if (retryCount == retryDays.size()) {
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
index a86db13..aee9854 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
@@ -86,7 +86,7 @@ public class DefaultPaymentDao implements PaymentDao {
 
     @Override
     public void updatePaymentAttemptWithRetryInfo(UUID paymentAttemptId, int retryCount, DateTime nextRetryDate) {
-        sqlDao.updatePaymentAttemptWithRetryInfo(paymentAttemptId.toString(), retryCount, nextRetryDate);
+        sqlDao.updatePaymentAttemptWithRetryInfo(paymentAttemptId.toString(), retryCount, nextRetryDate == null ? null : nextRetryDate.toDate());
     }
 
     @Override
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
index 9919d34..a7feae3 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
@@ -76,7 +76,7 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, CloseMe, Tr
     @SqlUpdate
     void updatePaymentAttemptWithRetryInfo(@Bind("payment_attempt_id") String paymentAttemptId,
                                            @Bind("retry_count") int retryCount,
-                                           @Bind("next_retry_dt") DateTime nextRetryDate);
+                                           @Bind("next_retry_dt") Date nextRetryDate);
 
     @SqlUpdate
     void updatePaymentInfo(@Bind("payment_method") String paymentMethod,
diff --git a/payment/src/main/java/com/ning/billing/payment/RetryService.java b/payment/src/main/java/com/ning/billing/payment/RetryService.java
index bbe8a61..ee57397 100644
--- a/payment/src/main/java/com/ning/billing/payment/RetryService.java
+++ b/payment/src/main/java/com/ning/billing/payment/RetryService.java
@@ -19,7 +19,6 @@ package com.ning.billing.payment;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 
 import com.google.inject.Inject;
 import com.ning.billing.lifecycle.KillbillService;
@@ -82,7 +81,7 @@ public class RetryService implements KillbillService {
          }
     }
 
-    public void scheduleRetry(Transmogrifier transactionalDao, PaymentAttempt paymentAttempt, DateTime timeOfRetry) {
+    public void scheduleRetry(PaymentAttempt paymentAttempt, DateTime timeOfRetry) {
         final String id = paymentAttempt.getPaymentAttemptId().toString();
 
         NotificationKey key = new NotificationKey() {
@@ -91,14 +90,14 @@ public class RetryService implements KillbillService {
                 return id;
             }
         };
-        retryQueue.recordFutureNotificationFromTransaction(transactionalDao, timeOfRetry, key);
+        retryQueue.recordFutureNotification(timeOfRetry, key);
     }
 
     private void retry(String paymentAttemptId) {
         PaymentInfo paymentInfo = paymentApi.getPaymentInfoForPaymentAttemptId(paymentAttemptId);
 
-        if (paymentInfo != null && !PaymentStatus.Processed.equals(PaymentStatus.valueOf(paymentInfo.getStatus()))) {
-            paymentApi.createPayment(UUID.fromString(paymentAttemptId));
+        if (paymentInfo == null || !PaymentStatus.Processed.equals(PaymentStatus.valueOf(paymentInfo.getStatus()))) {
+            paymentApi.createPaymentForPaymentAttempt(UUID.fromString(paymentAttemptId));
         }
     }
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/setup/PaymentModule.java b/payment/src/main/java/com/ning/billing/payment/setup/PaymentModule.java
index 85641ed..d3d9320 100644
--- a/payment/src/main/java/com/ning/billing/payment/setup/PaymentModule.java
+++ b/payment/src/main/java/com/ning/billing/payment/setup/PaymentModule.java
@@ -64,5 +64,6 @@ public class PaymentModule extends AbstractModule {
         bind(PaymentService.class).to(DefaultPaymentService.class).asEagerSingleton();
         installPaymentProviderPlugins(paymentConfig);
         installPaymentDao();
+        installRetryEngine();
     }
 }
diff --git a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
index 375bcfd..7155d63 100644
--- a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
+++ b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
@@ -22,6 +22,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.commons.lang.RandomStringUtils;
 import org.joda.time.DateTime;
@@ -39,24 +40,34 @@ import com.ning.billing.payment.api.PaymentProviderAccount;
 import com.ning.billing.payment.api.PaypalPaymentMethodInfo;
 
 public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
+    private final AtomicBoolean makeNextInvoiceFail = new AtomicBoolean(false);
     private final Map<String, PaymentInfo> payments = new ConcurrentHashMap<String, PaymentInfo>();
     private final Map<String, PaymentProviderAccount> accounts = new ConcurrentHashMap<String, PaymentProviderAccount>();
     private final Map<String, PaymentMethodInfo> paymentMethods = new ConcurrentHashMap<String, PaymentMethodInfo>();
 
+    public void makeNextInvoiceFail() {
+        makeNextInvoiceFail.set(true);
+    }
+
     @Override
     public Either<PaymentError, PaymentInfo> processInvoice(Account account, Invoice invoice) {
-        PaymentInfo payment = new PaymentInfo.Builder().setPaymentId(UUID.randomUUID().toString())
-                                             .setAmount(invoice.getBalance())
-                                             .setStatus("Processed")
-                                             .setBankIdentificationNumber("1234")
-                                             .setCreatedDate(new DateTime())
-                                             .setEffectiveDate(new DateTime())
-                                             .setPaymentNumber("12345")
-                                             .setReferenceId("12345")
-                                             .setType("Electronic")
-                                             .build();
-        payments.put(payment.getPaymentId(), payment);
-        return Either.right(payment);
+        if (makeNextInvoiceFail.getAndSet(false)) {
+            return Either.left(new PaymentError("unknown", "test error"));
+        }
+        else {
+            PaymentInfo payment = new PaymentInfo.Builder().setPaymentId(UUID.randomUUID().toString())
+                                                 .setAmount(invoice.getBalance())
+                                                 .setStatus("Processed")
+                                                 .setBankIdentificationNumber("1234")
+                                                 .setCreatedDate(new DateTime())
+                                                 .setEffectiveDate(new DateTime())
+                                                 .setPaymentNumber("12345")
+                                                 .setReferenceId("12345")
+                                                 .setType("Electronic")
+                                                 .build();
+            payments.put(payment.getPaymentId(), payment);
+            return Either.right(payment);
+        }
     }
 
     @Override
diff --git a/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithMocks.java b/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithMocks.java
index 3dfc637..c8f79bc 100644
--- a/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithMocks.java
+++ b/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithMocks.java
@@ -31,6 +31,8 @@ import com.ning.billing.payment.dao.PaymentDao;
 import com.ning.billing.payment.provider.MockPaymentProviderPluginModule;
 import com.ning.billing.util.bus.Bus;
 import com.ning.billing.util.bus.InMemoryBus;
+import com.ning.billing.util.notificationq.MockNotificationQueueService;
+import com.ning.billing.util.notificationq.NotificationQueueService;
 
 public class PaymentTestModuleWithMocks extends PaymentModule {
 	public static class MockProvider implements Provider<EntitlementBillingApi> {
@@ -38,12 +40,13 @@ public class PaymentTestModuleWithMocks extends PaymentModule {
 		public EntitlementBillingApi get() {
 			return BrainDeadProxyFactory.createBrainDeadProxyFor(EntitlementBillingApi.class);
 		}
-		
+
 	}
-	
-	
+
+
     public PaymentTestModuleWithMocks() {
-        super(MapUtils.toProperties(ImmutableMap.of("killbill.payment.provider.default", "my-mock")));
+        super(MapUtils.toProperties(ImmutableMap.of("killbill.payment.provider.default", "my-mock",
+                                                    "killbill.payment.engine.events.off", "false")));
     }
 
     @Override
@@ -65,5 +68,6 @@ public class PaymentTestModuleWithMocks extends PaymentModule {
         bind(MockInvoiceDao.class).asEagerSingleton();
         bind(InvoiceDao.class).to(MockInvoiceDao.class);
         bind(EntitlementBillingApi.class).toProvider( MockProvider.class );
+        bind(NotificationQueueService.class).to(MockNotificationQueueService.class).asEagerSingleton();
     }
 }
diff --git a/payment/src/test/java/com/ning/billing/payment/TestRetryService.java b/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
new file mode 100644
index 0000000..9930a3f
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
@@ -0,0 +1,177 @@
+package com.ning.billing.payment;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import com.google.inject.Inject;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.glue.AccountModuleWithMocks;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.glue.InvoiceModuleWithMocks;
+import com.ning.billing.invoice.model.RecurringInvoiceItem;
+import com.ning.billing.payment.api.Either;
+import com.ning.billing.payment.api.PaymentApi;
+import com.ning.billing.payment.api.PaymentAttempt;
+import com.ning.billing.payment.api.PaymentError;
+import com.ning.billing.payment.api.PaymentInfo;
+import com.ning.billing.payment.api.PaymentStatus;
+import com.ning.billing.payment.dao.PaymentDao;
+import com.ning.billing.payment.provider.MockPaymentProviderPlugin;
+import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
+import com.ning.billing.payment.setup.PaymentConfig;
+import com.ning.billing.payment.setup.PaymentTestModuleWithMocks;
+import com.ning.billing.util.bus.Bus;
+import com.ning.billing.util.notificationq.MockNotificationQueue;
+import com.ning.billing.util.notificationq.Notification;
+import com.ning.billing.util.notificationq.NotificationQueueService;
+
+@Guice(modules = { PaymentTestModuleWithMocks.class, AccountModuleWithMocks.class, InvoiceModuleWithMocks.class })
+@Test(groups = "fast")
+public class TestRetryService {
+    @Inject
+    private PaymentConfig paymentConfig;
+    @Inject
+    private Bus eventBus;
+    @Inject
+    private PaymentApi paymentApi;
+    @Inject
+    private TestHelper testHelper;
+    @Inject
+    private PaymentProviderPluginRegistry registry;
+    @Inject
+    private PaymentDao paymentDao;
+    @Inject
+    private RetryService retryService;
+    @Inject
+    private NotificationQueueService notificationQueueService;
+
+    private MockPaymentProviderPlugin mockPaymentProviderPlugin;
+    private MockNotificationQueue mockNotificationQueue;
+
+    @BeforeClass(alwaysRun = true)
+    public void initialize() throws Exception {
+        retryService.initialize();
+    }
+
+    @BeforeMethod(alwaysRun = true)
+    public void setUp() throws Exception {
+        eventBus.start();
+        retryService.start();
+
+        mockPaymentProviderPlugin = (MockPaymentProviderPlugin)registry.getPlugin(null);
+        mockNotificationQueue = (MockNotificationQueue)notificationQueueService.getNotificationQueue(RetryService.SERVICE_NAME, RetryService.QUEUE_NAME);
+    }
+
+    @AfterMethod(alwaysRun = true)
+    public void tearDown() throws Exception {
+        retryService.stop();
+        eventBus.stop();
+    }
+
+    @Test
+    public void testSchedulesRetry() throws Exception {
+        final DateTime now = new DateTime(DateTimeZone.UTC);
+        final Account account = testHelper.createTestCreditCardAccount();
+        final Invoice invoice = testHelper.createTestInvoice(account, now, Currency.USD);
+        final BigDecimal amount = new BigDecimal("10.00");
+        final UUID subscriptionId = UUID.randomUUID();
+
+        invoice.addInvoiceItem(new RecurringInvoiceItem(invoice.getId(),
+                                                       subscriptionId,
+                                                       "test plan", "test phase",
+                                                       now,
+                                                       now.plusMonths(1),
+                                                       amount,
+                                                       new BigDecimal("1.0"),
+                                                       Currency.USD));
+
+        mockPaymentProviderPlugin.makeNextInvoiceFail();
+
+        List<Either<PaymentError, PaymentInfo>> results = paymentApi.createPayment(account.getExternalKey(), Arrays.asList(invoice.getId().toString()));
+
+        assertEquals(results.size(), 1);
+        assertTrue(results.get(0).isLeft());
+
+        List<Notification> pendingNotifications = mockNotificationQueue.getPendingEvents();
+
+        assertEquals(pendingNotifications.size(), 1);
+
+        Notification notification = pendingNotifications.get(0);
+        PaymentAttempt paymentAttempt = paymentApi.getPaymentAttemptForInvoiceId(invoice.getId().toString());
+
+        assertNotNull(paymentAttempt);
+        assertEquals(notification.getNotificationKey(), paymentAttempt.getPaymentAttemptId().toString());
+        assertEquals(paymentAttempt.getRetryCount(), new Integer(1));
+
+        DateTime expectedRetryDate = paymentAttempt.getPaymentAttemptDate().plusDays(paymentConfig.getPaymentRetryDays().get(0));
+
+        assertEquals(notification.getEffectiveDate(), expectedRetryDate);
+        assertEquals(paymentAttempt.getNextRetryDate(), expectedRetryDate);
+    }
+
+    @Test
+    public void testRetries() throws Exception {
+        final DateTime now = new DateTime(DateTimeZone.UTC);
+        final Account account = testHelper.createTestCreditCardAccount();
+        final Invoice invoice = testHelper.createTestInvoice(account, now, Currency.USD);
+        final BigDecimal amount = new BigDecimal("10.00");
+        final UUID subscriptionId = UUID.randomUUID();
+
+        invoice.addInvoiceItem(new RecurringInvoiceItem(invoice.getId(),
+                                                       subscriptionId,
+                                                       "test plan", "test phase",
+                                                       now,
+                                                       now.plusMonths(1),
+                                                       amount,
+                                                       new BigDecimal("1.0"),
+                                                       Currency.USD));
+
+        DateTime nextRetryDate = new DateTime(DateTimeZone.UTC).minusDays(1);
+        DateTime paymentAttemptDate = nextRetryDate.minusDays(paymentConfig.getPaymentRetryDays().get(0));
+        PaymentAttempt paymentAttempt = new PaymentAttempt(UUID.randomUUID(), invoice).cloner()
+                                                                                      .setRetryCount(1)
+                                                                                      .setPaymentAttemptDate(paymentAttemptDate)
+                                                                                      .setNextRetryDate(nextRetryDate)
+                                                                                      .build();
+
+        paymentDao.createPaymentAttempt(paymentAttempt);
+        retryService.scheduleRetry(paymentAttempt, nextRetryDate);
+
+        // wait a little to give the queue time to process
+        Thread.sleep(paymentConfig.getNotificationSleepTimeMs() * 2);
+
+        List<Notification> pendingNotifications = mockNotificationQueue.getPendingEvents();
+
+        assertEquals(pendingNotifications.size(), 0);
+
+        List<PaymentInfo> paymentInfos = paymentApi.getPaymentInfo(Arrays.asList(invoice.getId().toString()));
+
+        assertEquals(paymentInfos.size(), 1);
+
+        PaymentInfo paymentInfo = paymentInfos.get(0);
+
+        assertEquals(paymentInfo.getStatus(), PaymentStatus.Processed.toString());
+
+        PaymentAttempt updatedAttempt = paymentApi.getPaymentAttemptForInvoiceId(invoice.getId().toString());
+
+        assertEquals(updatedAttempt.getPaymentAttemptId(), paymentAttempt.getPaymentAttemptId());
+        assertEquals(paymentInfo.getPaymentId(), updatedAttempt.getPaymentId());
+
+        // TODO: what else to assert ?
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotificationQueue.java b/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotificationQueue.java
index 8e2aaf8..c0c88fe 100644
--- a/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotificationQueue.java
+++ b/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotificationQueue.java
@@ -19,6 +19,7 @@ package com.ning.billing.util.notificationq;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
+
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
@@ -64,6 +65,12 @@ public class DefaultNotificationQueue extends NotificationQueueBase {
     }
 
     @Override
+    public void recordFutureNotification(DateTime futureNotificationTime, NotificationKey notificationKey) {
+        Notification notification = new DefaultNotification(getFullQName(), notificationKey.toString(), futureNotificationTime);
+        dao.insertNotification(notification);
+    }
+
+    @Override
     public void recordFutureNotificationFromTransaction(final Transmogrifier transactionalDao,
             final DateTime futureNotificationTime, final NotificationKey notificationKey) {
         NotificationSqlDao transactionalNotificationDao =  transactionalDao.become(NotificationSqlDao.class);
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueue.java b/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueue.java
index e1dcdbf..fb88d4c 100644
--- a/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueue.java
+++ b/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueue.java
@@ -23,9 +23,18 @@ import com.ning.billing.util.notificationq.NotificationQueueService.Notification
 
 public interface NotificationQueue {
 
-    /**
+   /**
+    *
+    * Record the need to be called back when the notification is ready
+    *
+    * @param futureNotificationTime the time at which the notification is ready
+    * @param notificationKey the key for that notification
+    */
+   public void recordFutureNotification(final DateTime futureNotificationTime, final NotificationKey notificationKey);
+
+   /**
     *
-    *  Record from within a transaction the need to be called back when the notification is ready
+    * Record from within a transaction the need to be called back when the notification is ready
     *
     * @param transactionalDao the transactionalDao
     * @param futureNotificationTime the time at which the notification is ready
diff --git a/util/src/test/java/com/ning/billing/util/notificationq/MockNotificationQueue.java b/util/src/test/java/com/ning/billing/util/notificationq/MockNotificationQueue.java
index e96d2cf..b76a8ad 100644
--- a/util/src/test/java/com/ning/billing/util/notificationq/MockNotificationQueue.java
+++ b/util/src/test/java/com/ning/billing/util/notificationq/MockNotificationQueue.java
@@ -49,9 +49,7 @@ public class MockNotificationQueue extends NotificationQueueBase implements Noti
     }
 
     @Override
-    public void recordFutureNotificationFromTransaction(
-            Transmogrifier transactionalDao, DateTime futureNotificationTime,
-            NotificationKey notificationKey) {
+    public void recordFutureNotification(DateTime futureNotificationTime, NotificationKey notificationKey) {
         Notification notification = new DefaultNotification("MockQueue", notificationKey.toString(), futureNotificationTime);
         synchronized(notifications) {
             notifications.add(notification);
@@ -59,6 +57,24 @@ public class MockNotificationQueue extends NotificationQueueBase implements Noti
     }
 
     @Override
+    public void recordFutureNotificationFromTransaction(
+            Transmogrifier transactionalDao, DateTime futureNotificationTime,
+            NotificationKey notificationKey) {
+        recordFutureNotification(futureNotificationTime, notificationKey);
+    }
+
+    public List<Notification> getPendingEvents() {
+        List<Notification> result = new ArrayList<Notification>();
+
+        for (Notification notification : notifications) {
+            if (notification.getProcessingState() == NotificationLifecycleState.AVAILABLE) {
+                result.add(notification);
+            }
+        }
+        return result;
+    }
+
+    @Override
     protected int doProcessEvents(int sequenceId) {
 
         int result = 0;