killbill-uncached

Changes

beatrix/pom.xml 25(+24 -1)

Details

diff --git a/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java b/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
index 8e5c0b3..2b916bf 100644
--- a/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
+++ b/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
@@ -19,16 +19,15 @@ package com.ning.billing.account.api;
 import java.util.List;
 import java.util.UUID;
 
-import com.google.inject.Inject;
-import com.ning.billing.util.clock.Clock;
 import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.util.customfield.CustomizableEntityBase;
 import com.ning.billing.util.tag.DefaultTagStore;
 import com.ning.billing.util.tag.DescriptiveTag;
 import com.ning.billing.util.tag.Tag;
 import com.ning.billing.util.tag.TagDefinition;
-import org.joda.time.DateTimeZone;
  
 public class DefaultAccount extends CustomizableEntityBase implements Account {
 	//public final static String OBJECT_TYPE = "Account";
@@ -68,13 +67,19 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
 				data.getPostalCode(), data.getPhone(), createdDate, createdDate);
 	}
 
-	/**
-	 * This call is used to migrate an account
-	 * @param data
-	 * @param createdDate
-	 */
-	public DefaultAccount(final AccountData data, DateTime createdDate,  DateTime updatedDate) {
-		this(UUID.randomUUID(), data.getExternalKey(), data.getEmail(), data.getName(), data.getFirstNameLength(),
+	//intended for creation
+	public DefaultAccount(final AccountData data) {
+		this(UUID.randomUUID(), data, null, null);
+	}
+	
+	// Intended for migration
+	public DefaultAccount(final AccountData data, DateTime createdDate, DateTime updatedDate) {
+		this(UUID.randomUUID(), data, createdDate, updatedDate);
+	}
+
+	//intended for update
+	public DefaultAccount(final UUID id, final AccountData data, DateTime createdDate, DateTime updatedDate) {
+		this(id, data.getExternalKey(), data.getEmail(), data.getName(), data.getFirstNameLength(),
 				data.getCurrency(), data.getBillCycleDay(), data.getPaymentProviderName(),
 				data.getTimeZone(), data.getLocale(),
 				data.getAddress1(), data.getAddress2(), data.getCompanyName(),
diff --git a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
index a6e2b7e..fd17c86 100644
--- a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
+++ b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
@@ -20,10 +20,12 @@ import java.util.List;
 import java.util.UUID;
 
 import com.google.inject.Inject;
+import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountData;
 import com.ning.billing.account.api.DefaultAccount;
+import com.ning.billing.account.api.MigrationAccountData;
 import com.ning.billing.account.dao.AccountDao;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.customfield.CustomField;
@@ -74,8 +76,31 @@ public class DefaultAccountUserApi implements com.ning.billing.account.api.Accou
         dao.update(account);
     }
 
+    @Override
+    public void updateAccount(final String externalKey, final AccountData accountData) throws AccountApiException {
+    	UUID accountId = getIdFromKey(externalKey);
+    	if(accountId == null) {
+    		throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_KEY, externalKey);
+    	}
+    	Account account = new DefaultAccount(accountId, accountData);
+        dao.update(account);
+    }
+
 	@Override
 	public void deleteAccountByKey(String externalKey) throws AccountApiException {
 		dao.deleteByKey(externalKey);
 	}
+
+	@Override
+	public Account migrateAccount(MigrationAccountData data,
+			List<CustomField> fields, List<Tag> tags)
+			throws AccountApiException {
+		
+		Account account = new DefaultAccount(data);
+        account.addFields(fields);
+        account.addTags(tags);
+
+        dao.create(account);
+        return account;
+	}
 }
diff --git a/account/src/test/java/com/ning/billing/account/api/MockAccountUserApi.java b/account/src/test/java/com/ning/billing/account/api/MockAccountUserApi.java
index 28f0107..ac74b68 100644
--- a/account/src/test/java/com/ning/billing/account/api/MockAccountUserApi.java
+++ b/account/src/test/java/com/ning/billing/account/api/MockAccountUserApi.java
@@ -117,4 +117,19 @@ public class MockAccountUserApi implements AccountUserApi {
         }	
 		
 	}
+
+	@Override
+	public Account migrateAccount(MigrationAccountData data,
+			List<CustomField> fields, List<Tag> tags)
+			throws AccountApiException {
+		Account result = new DefaultAccount(data, data.getCreatedDate(), data.getUpdatedDate());
+        accounts.add(result);
+        return result;
+	}
+
+	@Override
+	public void updateAccount(String key, AccountData accountData)
+			throws AccountApiException {
+		throw new UnsupportedOperationException();
+	}
 }
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java b/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java
index 0722494..c6d2369 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java
@@ -20,9 +20,11 @@ import java.util.List;
 import java.util.UUID;
 
 import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountData;
 import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.account.api.DefaultAccount;
+import com.ning.billing.account.api.MigrationAccountData;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.customfield.CustomField;
@@ -78,4 +80,17 @@ public class MockIAccountUserApi implements AccountUserApi
 	public void deleteAccountByKey(String externalKey) {
 		throw new UnsupportedOperationException();
 	}
+
+	@Override
+	public Account migrateAccount(MigrationAccountData data,
+			List<CustomField> fields, List<Tag> tags)
+			throws AccountApiException {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public void updateAccount(String key, AccountData accountData)
+			throws AccountApiException {
+		throw new UnsupportedOperationException();
+	}
 }
diff --git a/api/src/main/java/com/ning/billing/account/api/AccountApiException.java b/api/src/main/java/com/ning/billing/account/api/AccountApiException.java
index d9761b6..a6b1460 100644
--- a/api/src/main/java/com/ning/billing/account/api/AccountApiException.java
+++ b/api/src/main/java/com/ning/billing/account/api/AccountApiException.java
@@ -20,7 +20,9 @@ import com.ning.billing.BillingExceptionBase;
 import com.ning.billing.ErrorCode;
 
 public class AccountApiException extends BillingExceptionBase {
-    public AccountApiException(Throwable cause, int code, final String msg) {
+	private static final long serialVersionUID = 1L;
+
+	public AccountApiException(Throwable cause, int code, final String msg) {
         super(cause, code, msg);
     }
 
@@ -31,4 +33,5 @@ public class AccountApiException extends BillingExceptionBase {
     public AccountApiException(ErrorCode code, final Object... args) {
         super(code, args);
     }
+
 }
diff --git a/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java b/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java
index e208c83..05d8660 100644
--- a/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java
+++ b/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java
@@ -25,6 +25,8 @@ public interface AccountUserApi {
 
     public Account createAccount(AccountData data, List<CustomField> fields, List<Tag> tags) throws AccountApiException;
 
+    public Account migrateAccount(MigrationAccountData data, List<CustomField> fields, List<Tag> tags) throws AccountApiException;
+
     /***
      *
      * Note: does not update the external key
@@ -32,6 +34,8 @@ public interface AccountUserApi {
      */
     public void updateAccount(Account account) throws AccountApiException;
 
+    public void updateAccount(String key, AccountData accountData) throws AccountApiException;
+
     public Account getAccountByKey(String key);
 
     public Account getAccountById(UUID accountId);
diff --git a/api/src/main/java/com/ning/billing/account/api/MigrationAccountData.java b/api/src/main/java/com/ning/billing/account/api/MigrationAccountData.java
new file mode 100644
index 0000000..34f7023
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/account/api/MigrationAccountData.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.account.api;
+
+import org.joda.time.DateTime;
+
+public interface MigrationAccountData extends AccountData {
+
+	public DateTime getCreatedDate();
+	
+	public DateTime getUpdatedDate();
+}
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 189e87c..58e9898 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
@@ -45,7 +45,7 @@ public interface PaymentApi {
 
     Either<PaymentError, String> createPaymentProviderAccount(Account account);
 
-    Either<PaymentError, Void> updatePaymentProviderAccountContact(Account account);
+    Either<PaymentError, Void> updatePaymentProviderAccountContact(String accountKey);
 
     PaymentAttempt getPaymentAttemptForPaymentId(String id);
 

beatrix/pom.xml 25(+24 -1)

diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index c964ba4..8fe8863 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -8,7 +8,8 @@
     OR CONDITIONS OF ANY KIND, either express or implied. See the ~ License for 
     the specific language governing permissions and limitations ~ under the License. -->
 
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>com.ning.billing</groupId>
@@ -98,11 +99,33 @@
             <type>test-jar</type>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>com.mysql</groupId>
+            <artifactId>management</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.mysql</groupId>
+            <artifactId>management-dbfiles</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <scope>runtime</scope>
+        </dependency>
     </dependencies>
     <build>
         <plugins>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <groups>fast,slow</groups>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-jar-plugin</artifactId>
                 <executions>
                     <execution>
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/MockModule.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/MockModule.java
index 54f6dae..0cb1b01 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/MockModule.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/MockModule.java
@@ -36,6 +36,7 @@ import com.ning.billing.catalog.api.CatalogService;
 import com.ning.billing.catalog.glue.CatalogModule;
 import com.ning.billing.dbi.DBIProvider;
 import com.ning.billing.dbi.DbiConfig;
+import com.ning.billing.dbi.MysqlTestingHelper;
 import com.ning.billing.entitlement.api.EntitlementService;
 import com.ning.billing.entitlement.glue.EntitlementModule;
 import com.ning.billing.invoice.api.InvoiceService;
@@ -66,9 +67,19 @@ public class MockModule extends AbstractModule {
         bind(Clock.class).to(ClockMock.class).asEagerSingleton();
         bind(ClockMock.class).asEagerSingleton();
         bind(Lifecycle.class).to(SubsetDefaultLifecycle.class).asEagerSingleton();
-        bind(IDBI.class).toProvider(DBIProvider.class).asEagerSingleton();
-        final DbiConfig config = new ConfigurationObjectFactory(System.getProperties()).build(DbiConfig.class);
-        bind(DbiConfig.class).toInstance(config);
+
+
+        final MysqlTestingHelper helper = new MysqlTestingHelper();
+        bind(MysqlTestingHelper.class).toInstance(helper);
+        if (helper.isUsingLocalInstance()) {
+            bind(IDBI.class).toProvider(DBIProvider.class).asEagerSingleton();
+            final DbiConfig config = new ConfigurationObjectFactory(System.getProperties()).build(DbiConfig.class);
+            bind(DbiConfig.class).toInstance(config);
+        } else {
+            final IDBI dbi = helper.getDBI();
+            bind(IDBI.class).toInstance(dbi);
+        }
+
         install(new GlobalLockerModule());
         install(new BusModule());
         install(new NotificationQueueModule());
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBasic.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBasic.java
index bc9b6f2..ad0738f 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBasic.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBasic.java
@@ -20,6 +20,7 @@ import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
+import java.io.IOException;
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -27,11 +28,12 @@ import java.util.List;
 import java.util.UUID;
 
 import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.dbi.MysqlTestingHelper;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 
+import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceItem;
-import com.ning.billing.util.clock.MockClockModule;
-import com.sun.org.apache.bcel.internal.generic.NEW;
+import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang.RandomStringUtils;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
@@ -101,6 +103,9 @@ public class TestBasic {
     @Inject
     private AccountService accountService;
 
+    @Inject
+    private MysqlTestingHelper helper;
+
     private EntitlementUserApi entitlementUserApi;
 
     private InvoiceUserApi invoiceUserApi;
@@ -111,9 +116,30 @@ public class TestBasic {
 
 
 
+    private void setupMySQL() throws IOException
+    {
+
+
+        final String accountDdl = IOUtils.toString(TestBasic.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
+        final String entitlementDdl = IOUtils.toString(TestBasic.class.getResourceAsStream("/com/ning/billing/entitlement/ddl.sql"));
+        final String invoiceDdl = IOUtils.toString(TestBasic.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
+        final String paymentDdl = IOUtils.toString(TestBasic.class.getResourceAsStream("/com/ning/billing/payment/ddl.sql"));
+        final String utilDdl = IOUtils.toString(TestBasic.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
+
+        helper.startMysql();
+
+        helper.initDb(accountDdl);
+        helper.initDb(entitlementDdl);
+        helper.initDb(invoiceDdl);
+        helper.initDb(paymentDdl);
+        helper.initDb(utilDdl);
+    }
+
     @BeforeSuite(alwaysRun = true)
     public void setup() throws Exception{
 
+        setupMySQL();
+
         /**
          * Initialize lifecyle for subset of services
          */
@@ -122,6 +148,8 @@ public class TestBasic {
         busService.getBus().register(busHandler);
         lifecycle.fireStartupSequencePostEventRegistration();
 
+
+
         /**
          * Retrieve APIs
          */
@@ -178,66 +206,62 @@ public class TestBasic {
         });
     }
 
-    private void verifyTestResult(UUID accountId, UUID subscriptionId, InvoiceTestResult expectedResult) {
+    private void verifyTestResult(UUID accountId, UUID subscriptionId,
+                                  DateTime startDate, DateTime endDate,
+                                  BigDecimal amount, DateTime chargeThroughDate) {
         SubscriptionData subscription = (SubscriptionData) entitlementUserApi.getSubscriptionFromId(subscriptionId);
 
         List<InvoiceItem> invoiceItems = invoiceUserApi.getInvoiceItemsByAccount(accountId);
+        boolean wasFound = false;
+
         Iterator<InvoiceItem> invoiceItemIterator = invoiceItems.iterator();
         while (invoiceItemIterator.hasNext()) {
             InvoiceItem item = invoiceItemIterator.next();
-
-            for (InvoiceItemData data : expectedResult.getItems()) {
-                if (item.getStartDate().compareTo(data.getStartDate()) == 0) {
-                    if (item.getEndDate().compareTo(data.getEndDate()) == 0) {
-                        if (item.getAmount().compareTo(data.getAmount()) == 0) {
-                            invoiceItemIterator.remove();
-                        }
+            if (item.getStartDate().compareTo(removeMillis(startDate)) == 0) {
+                if (item.getEndDate().compareTo(removeMillis(endDate)) == 0) {
+                    if (item.getAmount().compareTo(amount) == 0) {
+                        wasFound = true;
+                        break;
                     }
                 }
             }
         }
 
-        assertEquals(invoiceItems.size(), 0);
+        assertTrue(wasFound);
 
         DateTime ctd = subscription.getChargedThroughDate();
         assertNotNull(ctd);
         log.info("Checking CTD: " + ctd.toString() + "; clock is " + clock.getUTCNow().toString());
         assertTrue(clock.getUTCNow().isBefore(ctd));
-        assertTrue(ctd.compareTo(expectedResult.getChargeThroughDate()) == 0);
+        assertTrue(ctd.compareTo(removeMillis(chargeThroughDate)) == 0);
+    }
+
+    private DateTime removeMillis(DateTime input) {
+        return input.toMutableDateTime().millisOfSecond().set(0).toDateTime();
     }
 
     @Test(groups = "fast", enabled = false)
     public void testBasePlanCompleteWithBillingDayInPast() throws Exception {
-        List<InvoiceTestResult> expectedTestResults = new ArrayList<InvoiceTestResult>();
         DateTime startDate = new DateTime(2012, 2, 1, 0, 3, 42, 0);
-        testBasePlanComplete(startDate, 31, expectedTestResults);
+        testBasePlanComplete(startDate, 31, false);
     }
 
     @Test(groups = "fast", enabled = false)
     public void testBasePlanCompleteWithBillingDayPresent() throws Exception {
-        List<InvoiceTestResult> expectedTestResults = new ArrayList<InvoiceTestResult>();
         DateTime startDate = new DateTime(2012, 2, 1, 0, 3, 42, 0);
-        testBasePlanComplete(startDate, 1, expectedTestResults);
+        testBasePlanComplete(startDate, 1, false);
     }
 
     @Test(groups = "fast", enabled = false)
     public void testBasePlanCompleteWithBillingDayAlignedWithTrial() throws Exception {
-        List<InvoiceTestResult> expectedTestResults = new ArrayList<InvoiceTestResult>();
         DateTime startDate = new DateTime(2012, 2, 1, 0, 3, 42, 0);
-        testBasePlanComplete(startDate, 2, expectedTestResults);
+        testBasePlanComplete(startDate, 2, false);
     }
 
     @Test(groups = "fast", enabled = true)
     public void testBasePlanCompleteWithBillingDayInFuture() throws Exception {
-        List<InvoiceTestResult> expectedTestResults = new ArrayList<InvoiceTestResult>();
         DateTime startDate = new DateTime(2012, 2, 1, 0, 3, 42, 0);
-
-        DateTime firstCTD = new DateTime(2012, 3, 2, 0, 3, 42, 0);
-        List<InvoiceItemData> firstItemList = new ArrayList<InvoiceItemData>();
-        firstItemList.add(new InvoiceItemData(BigDecimal.ZERO, startDate,firstCTD));
-        expectedTestResults.add(new InvoiceTestResult(firstCTD, firstItemList));
-
-        testBasePlanComplete(startDate, 3, expectedTestResults);
+        testBasePlanComplete(startDate, 3, true);
     }
 
     private void waitForDebug() throws Exception {
@@ -246,18 +270,28 @@ public class TestBasic {
 
     @Test(groups = "stress", enabled = false)
     public void stressTest() throws Exception {
-        final int maxIterations = 3;
+        final int maxIterations = 7;
         int curIteration = maxIterations;
         for (curIteration = 0; curIteration < maxIterations; curIteration++) {
             log.info("################################  ITERATION " + curIteration + "  #########################");
-            setupTest();
             Thread.sleep(1000);
+            setupTest();
             testBasePlanCompleteWithBillingDayPresent();
+            Thread.sleep(1000);
+            setupTest();
+            testBasePlanCompleteWithBillingDayInPast();
+            Thread.sleep(1000);
+            setupTest();
+            testBasePlanCompleteWithBillingDayAlignedWithTrial();
+            Thread.sleep(1000);
+            setupTest();
+            testBasePlanCompleteWithBillingDayInFuture();
         }
     }
 
-    private void testBasePlanComplete(DateTime initialCreationDate, int billingDay, List<InvoiceTestResult> expectedResults) throws Exception {
-        long DELAY = 5000;
+    private void testBasePlanComplete(DateTime initialCreationDate, int billingDay,
+                                      boolean proRationExpected) throws Exception {
+        long DELAY = 5000 * 10;
 
         Account account = accountUserApi.createAccount(getAccountData(billingDay), null, null);
         UUID accountId = account.getId();
@@ -271,8 +305,6 @@ public class TestBasic {
         BillingPeriod term = BillingPeriod.MONTHLY;
         String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
-        Iterator<InvoiceTestResult> testItemIterator = expectedResults.iterator();
-
         //
         // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
         //
@@ -288,8 +320,10 @@ public class TestBasic {
         //
         // VERIFY CTD HAS BEEN SET
         //
-
-        verifyTestResult(accountId, subscription.getId(), testItemIterator.next());
+        DateTime startDate = subscription.getCurrentPhaseStart();
+        DateTime endDate = startDate.plusDays(30);
+        BigDecimal price = subscription.getCurrentPhase().getFixedPrice().getPrice(Currency.USD);
+        verifyTestResult(accountId, subscription.getId(), startDate, endDate, price, endDate);
 
         //
         // CHANGE PLAN IMMEDIATELY AND EXPECT BOTH EVENTS: NextEvent.CHANGE NextEvent.INVOICE
@@ -308,7 +342,10 @@ public class TestBasic {
         //
         // VERIFY AGAIN CTD HAS BEEN SET
         //
-        verifyTestResult(accountId, subscription.getId(), testItemIterator.next());
+        startDate = subscription.getCurrentPhaseStart();
+        endDate = startDate.plusMonths(1);
+        price = subscription.getCurrentPhase().getFixedPrice().getPrice(Currency.USD);
+        verifyTestResult(accountId, subscription.getId(), startDate, endDate, price, endDate);
 
         //
         // MOVE TIME TO AFTER TRIAL AND EXPECT BOTH EVENTS :  NextEvent.PHASE NextEvent.INVOICE
@@ -316,6 +353,12 @@ public class TestBasic {
         busHandler.pushExpectedEvent(NextEvent.PHASE);
         busHandler.pushExpectedEvent(NextEvent.INVOICE);
         busHandler.pushExpectedEvent(NextEvent.PAYMENT);
+
+        if (proRationExpected) {
+            busHandler.pushExpectedEvent(NextEvent.INVOICE);
+            busHandler.pushExpectedEvent(NextEvent.PAYMENT);
+        }
+
         clock.setDeltaFromReality(AT_LEAST_ONE_MONTH_MS);
 
         assertTrue(busHandler.isCompleted(DELAY));
@@ -348,12 +391,14 @@ public class TestBasic {
         // MOVE TIME AFTER NEXT BILL CYCLE DAY AND EXPECT EVENT : NextEvent.INVOICE
         //
         int maxCycles = 3;
+        startDate = endDate;
+        endDate = startDate.plusMonths(1);
         do {
             busHandler.pushExpectedEvent(NextEvent.INVOICE);
             busHandler.pushExpectedEvent(NextEvent.PAYMENT);
             clock.addDeltaFromReality(AT_LEAST_ONE_MONTH_MS + 1000);
             assertTrue(busHandler.isCompleted(DELAY));
-            verifyTestResult(accountId, subscription.getId(), testItemIterator.next());
+            verifyTestResult(accountId, subscription.getId(), startDate, endDate, price, endDate);
         } while (maxCycles-- > 0);
 
         //
@@ -362,25 +407,28 @@ public class TestBasic {
         subscription = (SubscriptionData) entitlementUserApi.getSubscriptionFromId(subscription.getId());
         subscription.cancel(clock.getUTCNow(), false);
 
-//        // MOVE AFTER CANCEL DATE AND EXPECT EVENT : NextEvent.CANCEL
-//        busHandler.pushExpectedEvent(NextEvent.CANCEL);
-//        Interval it = new Interval(clock.getUTCNow(), lastCtd);
-//        clock.addDeltaFromReality(it.toDurationMillis());
-//        assertTrue(busHandler.isCompleted(DELAY));
-//
-//        //
-//        // CHECK AGAIN THERE IS NO MORE INVOICES GENERATED
-//        //
-//        busHandler.reset();
-//        clock.addDeltaFromReality(AT_LEAST_ONE_MONTH_MS + 1000);
-//        assertTrue(busHandler.isCompleted(DELAY));
-//
-//
-//        subscription = (SubscriptionData) entitlementUserApi.getSubscriptionFromId(subscription.getId());
-//        lastCtd = subscription.getChargedThroughDate();
-//        assertNotNull(lastCtd);
-//        log.info("Checking CTD: " + lastCtd.toString() + "; clock is " + clock.getUTCNow().toString());
-//        assertTrue(lastCtd.isBefore(clock.getUTCNow()));
+        // MOVE AFTER CANCEL DATE AND EXPECT EVENT : NextEvent.CANCEL
+        busHandler.pushExpectedEvent(NextEvent.CANCEL);
+        Interval it = new Interval(clock.getUTCNow(), endDate);
+        clock.addDeltaFromReality(it.toDurationMillis());
+        assertTrue(busHandler.isCompleted(DELAY));
+
+        //
+        // CHECK AGAIN THERE IS NO MORE INVOICES GENERATED
+        //
+        busHandler.reset();
+        clock.addDeltaFromReality(AT_LEAST_ONE_MONTH_MS + 1000);
+        assertTrue(busHandler.isCompleted(DELAY));
+
+        subscription = (SubscriptionData) entitlementUserApi.getSubscriptionFromId(subscription.getId());
+        DateTime lastCtd = subscription.getChargedThroughDate();
+        assertNotNull(lastCtd);
+        log.info("Checking CTD: " + lastCtd.toString() + "; clock is " + clock.getUTCNow().toString());
+        assertTrue(lastCtd.isBefore(clock.getUTCNow()));
+
+        // The invoice system is still working to verify there is nothing to do
+        Thread.sleep(DELAY);
+        log.info("TEST PASSED !");
     }
 
     @Test(enabled=false)
@@ -504,46 +552,4 @@ public class TestBasic {
         };
         return accountData;
     }
-
-    private class InvoiceTestResult {
-        private final DateTime chargeThroughDate;
-        private final List<InvoiceItemData> items;
-
-        private InvoiceTestResult(DateTime chargeThroughDate, List<InvoiceItemData> items) {
-            this.chargeThroughDate = chargeThroughDate;
-            this.items = items;
-        }
-
-        public DateTime getChargeThroughDate() {
-            return chargeThroughDate;
-        }
-
-        public List<InvoiceItemData> getItems() {
-            return items;
-        }
-    }
-
-    private class InvoiceItemData {
-        private final BigDecimal amount;
-        private final DateTime startDate;
-        private final DateTime endDate;
-
-        private InvoiceItemData(BigDecimal amount, DateTime startDate, DateTime endDate) {
-            this.amount = amount;
-            this.startDate = startDate;
-            this.endDate = endDate;
-        }
-
-        public BigDecimal getAmount() {
-            return amount;
-        }
-
-        public DateTime getStartDate() {
-            return startDate;
-        }
-
-        public DateTime getEndDate() {
-            return endDate;
-        }
-    }
 }
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/lifecycle/TestLifecycle.java b/beatrix/src/test/java/com/ning/billing/beatrix/lifecycle/TestLifecycle.java
index 9bf7c10..f60d61f 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/lifecycle/TestLifecycle.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/lifecycle/TestLifecycle.java
@@ -121,7 +121,7 @@ public class TestLifecycle {
 
 
 
-    @BeforeClass
+    @BeforeClass(alwaysRun=true)
     public void setup() {
         final Injector g = Guice.createInjector(Stage.DEVELOPMENT, new TestLifecycleModule());
         s1 = g.getInstance(Service1.class);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadAccountUserApi.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadAccountUserApi.java
index 967d8b6..4602fe6 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadAccountUserApi.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadAccountUserApi.java
@@ -23,6 +23,7 @@ import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountData;
 import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.account.api.MigrationAccountData;
 import com.ning.billing.util.customfield.CustomField;
 import com.ning.billing.util.tag.Tag;
 
@@ -66,4 +67,17 @@ public class BrainDeadAccountUserApi implements AccountUserApi {
 		throw new UnsupportedOperationException();
 	}
 
+	@Override
+	public Account migrateAccount(MigrationAccountData data,
+			List<CustomField> fields, List<Tag> tags)
+			throws AccountApiException {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public void updateAccount(String key, AccountData accountData)
+			throws AccountApiException {
+		throw new UnsupportedOperationException();
+	}
+
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java
index 92180ac..039f305 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java
@@ -109,10 +109,6 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
                     proposedItemIterator.remove();
                 }
             }
-//            if (existingInvoiceItems.contains(proposedItem)) {
-//                existingInvoiceItems.remove(proposedItem);
-//                proposedItemIterator.remove();
-//            }
         }
     }
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/notification/NextBillingDateEvent.java b/invoice/src/main/java/com/ning/billing/invoice/notification/NextBillingDateEvent.java
index 659d9e9..d4468c4 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/notification/NextBillingDateEvent.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/notification/NextBillingDateEvent.java
@@ -20,7 +20,7 @@ import java.util.UUID;
 
 import com.ning.billing.util.bus.BusEvent;
 
-public class NextBillingDateEvent implements BusEvent{
+public class NextBillingDateEvent implements BusEvent {
 	private final UUID subscriptionId;
 
 	public NextBillingDateEvent(UUID subscriptionId) {
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 6dc04e4..c868b05 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
@@ -196,7 +196,8 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     @Override
-    public Either<PaymentError, Void> updatePaymentProviderAccountContact(Account account) {
+    public Either<PaymentError, Void> updatePaymentProviderAccountContact(String externalKey) {
+    	Account account = accountUserApi.getAccountByKey(externalKey);
         final PaymentProviderPlugin plugin = getPaymentProviderPlugin(account);
         return plugin.updatePaymentProviderAccountExistingContact(account);
     }
diff --git a/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java b/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
index f7f682e..8c9d623 100644
--- a/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
+++ b/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
@@ -154,7 +154,7 @@ public abstract class TestPaymentApi {
                                                                   .billingCycleDay(account.getBillCycleDay())
                                                                   .build();
 
-        Either<PaymentError, Void> voidOrError = paymentApi.updatePaymentProviderAccountContact(accountToUpdate);
+        Either<PaymentError, Void> voidOrError = paymentApi.updatePaymentProviderAccountContact(accountToUpdate.getExternalKey());
         assertTrue(voidOrError.isRight());
     }
 
diff --git a/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java b/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
index 1e949a2..5ee7e88 100644
--- a/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
+++ b/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
@@ -39,6 +39,9 @@ import com.mysql.management.MysqldResourceI;
  */
 public class MysqlTestingHelper
 {
+
+    public static final String USE_LOCAL_DB_PROP = "com.ning.billing.dbi.test.useLocalDb";
+
     private static final Logger log = LoggerFactory.getLogger(MysqlTestingHelper.class);
 
     private static final String DB_NAME = "test_killbill";
@@ -47,24 +50,38 @@ public class MysqlTestingHelper
 
     private File dbDir;
     private MysqldResource mysqldResource;
-    private int port = 0;
+    private int port;
 
     public MysqlTestingHelper()
     {
-        // New socket on any free port
-        final ServerSocket socket;
-        try {
-            socket = new ServerSocket(0);
-            port = socket.getLocalPort();
-            socket.close();
-        }
-        catch (IOException e) {
-            Assert.fail();
+        if (isUsingLocalInstance()) {
+            port = 3306;
+        } else {
+            // New socket on any free port
+            final ServerSocket socket;
+            try {
+                socket = new ServerSocket(0);
+                port = socket.getLocalPort();
+                socket.close();
+            }
+            catch (IOException e) {
+                Assert.fail();
+            }
         }
     }
 
+
+    public boolean isUsingLocalInstance() {
+        return (System.getProperty(USE_LOCAL_DB_PROP) != null);
+    }
+
     public void startMysql() throws IOException
     {
+
+        if (isUsingLocalInstance()) {
+            return;
+        }
+
         dbDir = File.createTempFile("mysql", "");
         dbDir.delete();
         dbDir.mkdir();
@@ -87,6 +104,12 @@ public class MysqlTestingHelper
 
     public void cleanupTable(final String table)
     {
+
+        if (!isUsingLocalInstance() && (mysqldResource == null || !mysqldResource.isRunning())) {
+            log.error("Asked to cleanup table " + table + " but MySQL is not running!");
+            return;
+        }
+
         if (mysqldResource == null || !mysqldResource.isRunning()) {
             log.error("Asked to cleanup table " + table + " but MySQL is not running!");
             return;
@@ -122,6 +145,9 @@ public class MysqlTestingHelper
 
     public void initDb(final String ddl) throws IOException
     {
+        if (isUsingLocalInstance()) {
+            return;
+        }
         final IDBI dbi = getDBI();
         dbi.withHandle(new HandleCallback<Void>()
         {
diff --git a/util/src/test/java/com/ning/billing/util/clock/ClockMock.java b/util/src/test/java/com/ning/billing/util/clock/ClockMock.java
index 72fd8f4..ad3e5d3 100644
--- a/util/src/test/java/com/ning/billing/util/clock/ClockMock.java
+++ b/util/src/test/java/com/ning/billing/util/clock/ClockMock.java
@@ -38,13 +38,13 @@ public class ClockMock extends DefaultClock {
 
     private long deltaFromRealityMs;
     private List<Duration> deltaFromRealityDuration;
-    private long deltaFromRealitDurationEpsilon;
+    private long deltaFromRealityDurationEpsilon;
     private DeltaType deltaType;
 
     public ClockMock() {
         deltaType = DeltaType.DELTA_NONE;
         deltaFromRealityMs = 0;
-        deltaFromRealitDurationEpsilon = 0;
+        deltaFromRealityDurationEpsilon = 0;
         deltaFromRealityDuration = null;
     }
 
@@ -58,7 +58,7 @@ public class ClockMock extends DefaultClock {
         return getNow(DateTimeZone.UTC);
     }
 
-    private void logClockAdjustement(DateTime prev, DateTime next) {
+    private void logClockAdjustment(DateTime prev, DateTime next) {
         log.info(String.format("            ************      ADJUSTING CLOCK FROM %s to %s     ********************", prev, next));
     }
 
@@ -67,9 +67,9 @@ public class ClockMock extends DefaultClock {
         deltaType = DeltaType.DELTA_DURATION;
         deltaFromRealityDuration = new ArrayList<Duration>();
         deltaFromRealityDuration.add(delta);
-        deltaFromRealitDurationEpsilon = epsilon;
+        deltaFromRealityDurationEpsilon = epsilon;
         deltaFromRealityMs = 0;
-        logClockAdjustement(prev, getUTCNow());
+        logClockAdjustment(prev, getUTCNow());
     }
 
     public synchronized void addDeltaFromReality(Duration delta) {
@@ -78,16 +78,16 @@ public class ClockMock extends DefaultClock {
             throw new RuntimeException("ClockMock should be set with type DELTA_DURATION");
         }
         deltaFromRealityDuration.add(delta);
-        logClockAdjustement(prev, getUTCNow());
+        logClockAdjustment(prev, getUTCNow());
     }
 
     public synchronized void setDeltaFromReality(long delta) {
         DateTime prev = getUTCNow();
         deltaType = DeltaType.DELTA_ABS;
         deltaFromRealityDuration = null;
-        deltaFromRealitDurationEpsilon = 0;
+        deltaFromRealityDurationEpsilon = 0;
         deltaFromRealityMs = delta;
-        logClockAdjustement(prev, getUTCNow());
+        logClockAdjustment(prev, getUTCNow());
     }
 
     public synchronized void addDeltaFromReality(long delta) {
@@ -96,15 +96,15 @@ public class ClockMock extends DefaultClock {
             throw new RuntimeException("ClockMock should be set with type DELTA_ABS");
         }
         deltaFromRealityDuration = null;
-        deltaFromRealitDurationEpsilon = 0;
+        deltaFromRealityDurationEpsilon = 0;
         deltaFromRealityMs += delta;
-        logClockAdjustement(prev, getUTCNow());
+        logClockAdjustment(prev, getUTCNow());
     }
 
     public synchronized void resetDeltaFromReality() {
         deltaType = DeltaType.DELTA_NONE;
         deltaFromRealityDuration = null;
-        deltaFromRealitDurationEpsilon = 0;
+        deltaFromRealityDurationEpsilon = 0;
         deltaFromRealityMs = 0;
     }
 
@@ -125,8 +125,6 @@ public class ClockMock extends DefaultClock {
 
         DateTime result = input;
         for (Duration cur : deltaFromRealityDuration) {
-
-            int length = cur.getNumber();
             switch (cur.getUnit()) {
             case DAYS:
                 result = result.plusDays(cur.getNumber());
@@ -142,11 +140,11 @@ public class ClockMock extends DefaultClock {
 
             case UNLIMITED:
             default:
-                throw new RuntimeException("ClockMock is adjusting an unlimtited time period");
+                throw new RuntimeException("ClockMock is adjusting an unlimited time period");
             }
         }
-        if (deltaFromRealitDurationEpsilon != 0) {
-            result = result.plus(deltaFromRealitDurationEpsilon);
+        if (deltaFromRealityDurationEpsilon != 0) {
+            result = result.plus(deltaFromRealityDurationEpsilon);
         }
         return result;
     }