killbill-memoizeit

Merge branch 'audit-rework-nocleanup' of github.com:killbill/killbill

11/16/2012 5:31:32 PM

Details

diff --git a/account/src/test/java/com/ning/billing/account/AccountTestBase.java b/account/src/test/java/com/ning/billing/account/AccountTestBase.java
new file mode 100644
index 0000000..733581e
--- /dev/null
+++ b/account/src/test/java/com/ning/billing/account/AccountTestBase.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2010-2012 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;
+
+import java.io.IOException;
+import java.util.Locale;
+import java.util.UUID;
+
+import org.joda.time.DateTimeZone;
+import org.skife.jdbi.v2.IDBI;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+
+import com.ning.billing.account.api.AccountData;
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.account.api.BillCycleDay;
+import com.ning.billing.account.api.DefaultBillCycleDay;
+import com.ning.billing.account.api.DefaultMutableAccountData;
+import com.ning.billing.account.api.user.DefaultAccountUserApi;
+import com.ning.billing.account.dao.AccountDao;
+import com.ning.billing.account.dao.AccountModelDao;
+import com.ning.billing.account.dao.DefaultAccountDao;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.audit.dao.AuditDao;
+import com.ning.billing.util.audit.dao.DefaultAuditDao;
+import com.ning.billing.util.bus.DefaultBusService;
+import com.ning.billing.util.bus.InMemoryInternalBus;
+import com.ning.billing.util.callcontext.DefaultCallContextFactory;
+import com.ning.billing.util.callcontext.InternalCallContextFactory;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.customfield.dao.CustomFieldDao;
+import com.ning.billing.util.customfield.dao.DefaultCustomFieldDao;
+import com.ning.billing.util.svcsapi.bus.BusService;
+import com.ning.billing.util.svcsapi.bus.InternalBus;
+import com.ning.billing.util.tag.api.user.TagEventBuilder;
+import com.ning.billing.util.tag.dao.DefaultTagDao;
+import com.ning.billing.util.tag.dao.DefaultTagDefinitionDao;
+import com.ning.billing.util.tag.dao.TagDao;
+import com.ning.billing.util.tag.dao.TagDefinitionDao;
+
+import static org.testng.Assert.fail;
+
+public abstract class AccountTestBase extends AccountTestSuiteWithEmbeddedDB {
+
+    protected final TagEventBuilder tagEventBuilder = new TagEventBuilder();
+    protected final Clock clock = new ClockMock();
+    protected final InternalBus bus = new InMemoryInternalBus();
+
+    protected AccountDao accountDao;
+    protected AuditDao auditDao;
+    protected CustomFieldDao customFieldDao;
+    protected TagDefinitionDao tagDefinitionDao;
+    protected TagDao tagDao;
+
+    protected AccountUserApi accountUserApi;
+
+    @BeforeClass(groups = "slow")
+    protected void setup() throws IOException {
+        try {
+            final IDBI dbi = getMysqlTestingHelper().getDBI();
+
+            final InternalCallContextFactory internalCallContextFactory = new InternalCallContextFactory(dbi, clock);
+            accountDao = new DefaultAccountDao(dbi, bus, internalCallContextFactory);
+            auditDao = new DefaultAuditDao(dbi);
+            customFieldDao = new DefaultCustomFieldDao(dbi);
+            tagDefinitionDao = new DefaultTagDefinitionDao(dbi, tagEventBuilder, bus);
+            tagDao = new DefaultTagDao(dbi, tagEventBuilder, bus);
+
+            // Health check test to make sure MySQL is setup properly
+            accountDao.test(internalCallContext);
+
+            final BusService busService = new DefaultBusService(bus);
+            ((DefaultBusService) busService).startBus();
+
+            final DefaultCallContextFactory callContextFactory = new DefaultCallContextFactory(clock);
+            accountUserApi = new DefaultAccountUserApi(callContextFactory, internalCallContextFactory, accountDao);
+        } catch (Throwable t) {
+            fail(t.toString());
+        }
+    }
+
+    protected void checkAccountsEqual(final AccountModelDao retrievedAccount, final AccountModelDao account) {
+        if (retrievedAccount == null || account == null) {
+            Assert.assertNull(retrievedAccount);
+            Assert.assertNull(account);
+            return;
+        }
+
+        // Check all fields but createdDate/updatedDate (comes from the context)
+        Assert.assertEquals(retrievedAccount.getId(), account.getId());
+        Assert.assertEquals(retrievedAccount.getExternalKey(), account.getExternalKey());
+        Assert.assertEquals(retrievedAccount.getEmail(), account.getEmail());
+        Assert.assertEquals(retrievedAccount.getName(), account.getName());
+        Assert.assertEquals(retrievedAccount.getFirstNameLength(), account.getFirstNameLength());
+        Assert.assertEquals(retrievedAccount.getCurrency(), account.getCurrency());
+        Assert.assertEquals(retrievedAccount.getBillingCycleDayLocal(), account.getBillingCycleDayLocal());
+        Assert.assertEquals(retrievedAccount.getBillingCycleDayUtc(), account.getBillingCycleDayUtc());
+        Assert.assertEquals(retrievedAccount.getPaymentMethodId(), account.getPaymentMethodId());
+        Assert.assertEquals(retrievedAccount.getTimeZone(), account.getTimeZone());
+        Assert.assertEquals(retrievedAccount.getLocale(), account.getLocale());
+        Assert.assertEquals(retrievedAccount.getAddress1(), account.getAddress1());
+        Assert.assertEquals(retrievedAccount.getAddress2(), account.getAddress2());
+        Assert.assertEquals(retrievedAccount.getCompanyName(), account.getCompanyName());
+        Assert.assertEquals(retrievedAccount.getCity(), account.getCity());
+        Assert.assertEquals(retrievedAccount.getStateOrProvince(), account.getStateOrProvince());
+        Assert.assertEquals(retrievedAccount.getCountry(), account.getCountry());
+        Assert.assertEquals(retrievedAccount.getPostalCode(), account.getPostalCode());
+        Assert.assertEquals(retrievedAccount.getPhone(), account.getPhone());
+        Assert.assertEquals(retrievedAccount.getIsNotifiedForInvoices(), account.getIsNotifiedForInvoices());
+        Assert.assertEquals(retrievedAccount.getMigrated(), account.getMigrated());
+    }
+
+    protected AccountModelDao createTestAccount() {
+        return createTestAccount(30, 31, UUID.randomUUID().toString().substring(0, 4));
+    }
+
+    protected AccountModelDao createTestAccount(final String phone) {
+        return createTestAccount(30, 31, phone);
+    }
+
+    protected AccountModelDao createTestAccount(final int billCycleDay) {
+        return createTestAccount(billCycleDay, billCycleDay, UUID.randomUUID().toString().substring(0, 4));
+    }
+
+    private AccountModelDao createTestAccount(final int billCycleDayUTC, final int billCycleDayLocal, final String phone) {
+        final String externalKey = UUID.randomUUID().toString();
+        final String email = UUID.randomUUID().toString().substring(0, 4) + '@' + UUID.randomUUID().toString().substring(0, 4);
+        final String name = UUID.randomUUID().toString();
+        final String locale = Locale.GERMANY.toString();
+        final DateTimeZone timeZone = DateTimeZone.forID("America/Los_Angeles");
+        final int firstNameLength = name.length();
+        final Currency currency = Currency.MXN;
+        final BillCycleDay billCycleDay = new DefaultBillCycleDay(billCycleDayLocal, billCycleDayUTC);
+        final UUID paymentMethodId = UUID.randomUUID();
+        final String address1 = UUID.randomUUID().toString();
+        final String address2 = UUID.randomUUID().toString();
+        final String companyName = UUID.randomUUID().toString();
+        final String city = UUID.randomUUID().toString();
+        final String stateOrProvince = UUID.randomUUID().toString();
+        final String country = Locale.GERMANY.getCountry();
+        final String postalCode = UUID.randomUUID().toString().substring(0, 4);
+
+        final AccountData accountData = new DefaultMutableAccountData(externalKey, email, name, firstNameLength, currency,
+                                                                      billCycleDay, paymentMethodId, null, timeZone,
+                                                                      locale, address1, address2, companyName, city, stateOrProvince,
+                                                                      country, postalCode, phone, false, true);
+        return new AccountModelDao(UUID.randomUUID(), accountData);
+    }
+}
diff --git a/account/src/test/java/com/ning/billing/account/api/user/TestDefaultAccountUserApi.java b/account/src/test/java/com/ning/billing/account/api/user/TestDefaultAccountUserApi.java
index 914a41d..07b4e15 100644
--- a/account/src/test/java/com/ning/billing/account/api/user/TestDefaultAccountUserApi.java
+++ b/account/src/test/java/com/ning/billing/account/api/user/TestDefaultAccountUserApi.java
@@ -18,117 +18,78 @@ package com.ning.billing.account.api.user;
 
 import java.util.UUID;
 
-import org.joda.time.DateTimeZone;
-import org.mockito.Mockito;
 import org.testng.Assert;
-import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import com.ning.billing.account.AccountTestSuite;
-import com.ning.billing.account.api.AccountData;
-import com.ning.billing.account.api.AccountEmail;
+import com.ning.billing.account.AccountTestBase;
+import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.BillCycleDay;
 import com.ning.billing.account.api.DefaultAccount;
-import com.ning.billing.account.api.DefaultAccountEmail;
-import com.ning.billing.account.api.DefaultBillCycleDay;
-import com.ning.billing.account.dao.AccountDao;
-import com.ning.billing.account.dao.AccountModelDao;
-import com.ning.billing.account.dao.MockAccountDao;
+import com.ning.billing.account.api.DefaultMutableAccountData;
+import com.ning.billing.account.api.MutableAccountData;
 import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.util.callcontext.CallContext;
-import com.ning.billing.util.callcontext.CallContextFactory;
-import com.ning.billing.util.callcontext.InternalCallContextFactory;
-import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.svcsapi.bus.InternalBus;
-
-public class TestDefaultAccountUserApi extends AccountTestSuite {
-
-    private final CallContextFactory factory = Mockito.mock(CallContextFactory.class);
-    private final InternalCallContextFactory internalFactory = Mockito.mock(InternalCallContextFactory.class);
-    private final CallContext callContext = Mockito.mock(CallContext.class);
-    private final InternalTenantContext tenantContext = Mockito.mock(InternalTenantContext.class);
-
-    private AccountDao accountDao;
-    private DefaultAccountUserApi accountUserApi;
-
-    @BeforeMethod(groups = "fast")
-    public void setUp() throws Exception {
-        accountDao = new MockAccountDao(Mockito.mock(InternalBus.class));
-        accountUserApi = new DefaultAccountUserApi(factory, internalFactory, accountDao);
+
+public class TestDefaultAccountUserApi extends AccountTestBase {
+
+    @Test(groups = "slow")
+    public void testShouldBeAbleToPassNullForSomeFieldsToAvoidUpdate() throws Exception {
+        final Account account = accountUserApi.createAccount(new DefaultAccount(createTestAccount()), callContext);
+
+        // Update the address and leave other fields null
+        final MutableAccountData mutableAccountData = new DefaultMutableAccountData(null, null, null, 0, null, null, null,
+                                                                                    null, null, null, null, null, null, null,
+                                                                                    null, null, null, null, false, false);
+        final String newAddress1 = UUID.randomUUID().toString();
+        mutableAccountData.setAddress1(newAddress1);
+
+        accountUserApi.updateAccount(account.getId(), mutableAccountData, callContext);
+
+        final Account retrievedAccount = accountUserApi.getAccountById(account.getId(), callContext);
+        Assert.assertEquals(retrievedAccount.getAddress1(), newAddress1);
+        Assert.assertEquals(retrievedAccount.getAddress2(), account.getAddress2());
+        Assert.assertEquals(retrievedAccount.getCurrency(), account.getCurrency());
+        Assert.assertEquals(retrievedAccount.getExternalKey(), account.getExternalKey());
+        Assert.assertEquals(retrievedAccount.getBillCycleDay().getDayOfMonthLocal(), account.getBillCycleDay().getDayOfMonthLocal());
+        Assert.assertEquals(retrievedAccount.getBillCycleDay().getDayOfMonthUTC(), account.getBillCycleDay().getDayOfMonthUTC());
     }
 
-    @Test(groups = "fast")
-    public void testCreateAccount() throws Exception {
-        final UUID id = UUID.randomUUID();
-        final String externalKey = UUID.randomUUID().toString();
-        final String email = UUID.randomUUID().toString();
-        final String name = UUID.randomUUID().toString();
-        final Integer firstNameLength = Integer.MAX_VALUE;
-        final Currency currency = Currency.BRL;
-        final BillCycleDay billCycleDay = new DefaultBillCycleDay(Integer.MIN_VALUE, Integer.MAX_VALUE);
-        final UUID paymentMethodId = UUID.randomUUID();
-        final DateTimeZone timeZone = DateTimeZone.UTC;
-        final String locale = UUID.randomUUID().toString();
-        final String address1 = UUID.randomUUID().toString();
-        final String address2 = UUID.randomUUID().toString();
-        final String companyName = UUID.randomUUID().toString();
-        final String city = UUID.randomUUID().toString();
-        final String stateOrProvince = UUID.randomUUID().toString();
-        final String country = UUID.randomUUID().toString();
-        final String postalCode = UUID.randomUUID().toString();
-        final String phone = UUID.randomUUID().toString();
-        final Boolean isMigrated = true;
-        final Boolean isNotifiedForInvoices = false;
-        final AccountData data = new DefaultAccount(id, externalKey, email, name, firstNameLength, currency, billCycleDay,
-                                                    paymentMethodId, timeZone, locale, address1, address2, companyName,
-                                                    city, stateOrProvince, country, postalCode, phone, isMigrated, isNotifiedForInvoices);
-
-        accountUserApi.createAccount(data, callContext);
-
-        final AccountModelDao account = accountDao.getAccountByKey(externalKey, tenantContext);
-        Assert.assertEquals(account.getExternalKey(), externalKey);
-        Assert.assertEquals(account.getEmail(), email);
-        Assert.assertEquals(account.getName(), name);
-        Assert.assertEquals(account.getFirstNameLength(), firstNameLength);
-        Assert.assertEquals(account.getCurrency(), currency);
-        Assert.assertEquals(account.getBillingCycleDayLocal(), billCycleDay.getDayOfMonthLocal());
-        Assert.assertEquals(account.getBillingCycleDayUtc(), billCycleDay.getDayOfMonthUTC());
-        Assert.assertEquals(account.getPaymentMethodId(), paymentMethodId);
-        Assert.assertEquals(account.getTimeZone(), timeZone);
-        Assert.assertEquals(account.getLocale(), locale);
-        Assert.assertEquals(account.getAddress1(), address1);
-        Assert.assertEquals(account.getAddress2(), address2);
-        Assert.assertEquals(account.getCompanyName(), companyName);
-        Assert.assertEquals(account.getCity(), city);
-        Assert.assertEquals(account.getStateOrProvince(), stateOrProvince);
-        Assert.assertEquals(account.getCountry(), country);
-        Assert.assertEquals(account.getPostalCode(), postalCode);
-        Assert.assertEquals(account.getPhone(), phone);
-        Assert.assertEquals(account.getMigrated(), isMigrated);
-        Assert.assertEquals(account.getIsNotifiedForInvoices(), isNotifiedForInvoices);
+    @Test(groups = "slow", expectedExceptions = IllegalArgumentException.class)
+    public void testShouldntBeAbleToUpdateBillCycleDay() throws Exception {
+        final Account account = accountUserApi.createAccount(new DefaultAccount(createTestAccount()), callContext);
+
+        final MutableAccountData otherAccount = new DefaultAccount(account.getId(), account).toMutableAccountData();
+        otherAccount.setBillCycleDay(new BillCycleDay() {
+            @Override
+            public int getDayOfMonthUTC() {
+                return account.getBillCycleDay().getDayOfMonthUTC() + 2;
+            }
+
+            @Override
+            public int getDayOfMonthLocal() {
+                return account.getBillCycleDay().getDayOfMonthLocal() + 2;
+            }
+        });
+
+        accountUserApi.updateAccount(new DefaultAccount(account.getId(), otherAccount), callContext);
     }
 
-    @Test(groups = "fast")
-    public void testAddEmail() throws Exception {
-        final UUID accountId = UUID.randomUUID();
+    @Test(groups = "slow", expectedExceptions = IllegalArgumentException.class)
+    public void testShouldntBeAbleToUpdateCurrency() throws Exception {
+        final Account account = accountUserApi.createAccount(new DefaultAccount(createTestAccount()), callContext);
 
-        // Verify the initial state
-        Assert.assertEquals(accountDao.getEmailsByAccountId(accountId, tenantContext).size(), 0);
+        final MutableAccountData otherAccount = new DefaultAccount(account.getId(), account).toMutableAccountData();
+        otherAccount.setCurrency(Currency.GBP);
+
+        accountUserApi.updateAccount(new DefaultAccount(account.getId(), otherAccount), callContext);
+    }
 
-        // Add the first email
-        final String emailAddress1 = UUID.randomUUID().toString();
-        final AccountEmail email1 = new DefaultAccountEmail(accountId, emailAddress1);
-        accountUserApi.addEmail(accountId, email1, callContext);
-        Assert.assertEquals(accountDao.getEmailsByAccountId(accountId, tenantContext).size(), 1);
+    @Test(groups = "slow", expectedExceptions = IllegalArgumentException.class)
+    public void testShouldntBeAbleToUpdateExternalKey() throws Exception {
+        final Account account = accountUserApi.createAccount(new DefaultAccount(createTestAccount()), callContext);
 
-        // Add a second one
-        final String emailAddress2 = UUID.randomUUID().toString();
-        final AccountEmail email2 = new DefaultAccountEmail(accountId, emailAddress2);
-        accountUserApi.addEmail(accountId, email2, callContext);
-        Assert.assertEquals(accountDao.getEmailsByAccountId(accountId, tenantContext).size(), 2);
+        final MutableAccountData otherAccount = new DefaultAccount(account.getId(), account).toMutableAccountData();
+        otherAccount.setExternalKey(UUID.randomUUID().toString());
 
-        // Remove the first second one
-        accountUserApi.removeEmail(accountId, email1, callContext);
-        Assert.assertEquals(accountDao.getEmailsByAccountId(accountId, tenantContext).size(), 1);
+        accountUserApi.updateAccount(new DefaultAccount(account.getId(), otherAccount), callContext);
     }
 }
diff --git a/account/src/test/java/com/ning/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java b/account/src/test/java/com/ning/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java
new file mode 100644
index 0000000..72f04d8
--- /dev/null
+++ b/account/src/test/java/com/ning/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2010-2012 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.user;
+
+import java.util.UUID;
+
+import org.joda.time.DateTimeZone;
+import org.mockito.Mockito;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.ning.billing.account.AccountTestSuite;
+import com.ning.billing.account.api.AccountData;
+import com.ning.billing.account.api.AccountEmail;
+import com.ning.billing.account.api.BillCycleDay;
+import com.ning.billing.account.api.DefaultAccount;
+import com.ning.billing.account.api.DefaultAccountEmail;
+import com.ning.billing.account.api.DefaultBillCycleDay;
+import com.ning.billing.account.dao.AccountDao;
+import com.ning.billing.account.dao.AccountModelDao;
+import com.ning.billing.account.dao.MockAccountDao;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextFactory;
+import com.ning.billing.util.callcontext.InternalCallContextFactory;
+import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.svcsapi.bus.InternalBus;
+
+public class TestDefaultAccountUserApiWithMocks extends AccountTestSuite {
+
+    private final CallContextFactory factory = Mockito.mock(CallContextFactory.class);
+    private final InternalCallContextFactory internalFactory = Mockito.mock(InternalCallContextFactory.class);
+    private final CallContext callContext = Mockito.mock(CallContext.class);
+    private final InternalTenantContext tenantContext = Mockito.mock(InternalTenantContext.class);
+
+    private AccountDao accountDao;
+    private DefaultAccountUserApi accountUserApi;
+
+    @BeforeMethod(groups = "fast")
+    public void setUp() throws Exception {
+        accountDao = new MockAccountDao(Mockito.mock(InternalBus.class));
+        accountUserApi = new DefaultAccountUserApi(factory, internalFactory, accountDao);
+    }
+
+    @Test(groups = "fast")
+    public void testCreateAccount() throws Exception {
+        final UUID id = UUID.randomUUID();
+        final String externalKey = UUID.randomUUID().toString();
+        final String email = UUID.randomUUID().toString();
+        final String name = UUID.randomUUID().toString();
+        final Integer firstNameLength = Integer.MAX_VALUE;
+        final Currency currency = Currency.BRL;
+        final BillCycleDay billCycleDay = new DefaultBillCycleDay(Integer.MIN_VALUE, Integer.MAX_VALUE);
+        final UUID paymentMethodId = UUID.randomUUID();
+        final DateTimeZone timeZone = DateTimeZone.UTC;
+        final String locale = UUID.randomUUID().toString();
+        final String address1 = UUID.randomUUID().toString();
+        final String address2 = UUID.randomUUID().toString();
+        final String companyName = UUID.randomUUID().toString();
+        final String city = UUID.randomUUID().toString();
+        final String stateOrProvince = UUID.randomUUID().toString();
+        final String country = UUID.randomUUID().toString();
+        final String postalCode = UUID.randomUUID().toString();
+        final String phone = UUID.randomUUID().toString();
+        final Boolean isMigrated = true;
+        final Boolean isNotifiedForInvoices = false;
+        final AccountData data = new DefaultAccount(id, externalKey, email, name, firstNameLength, currency, billCycleDay,
+                                                    paymentMethodId, timeZone, locale, address1, address2, companyName,
+                                                    city, stateOrProvince, country, postalCode, phone, isMigrated, isNotifiedForInvoices);
+
+        accountUserApi.createAccount(data, callContext);
+
+        final AccountModelDao account = accountDao.getAccountByKey(externalKey, tenantContext);
+        Assert.assertEquals(account.getExternalKey(), externalKey);
+        Assert.assertEquals(account.getEmail(), email);
+        Assert.assertEquals(account.getName(), name);
+        Assert.assertEquals(account.getFirstNameLength(), firstNameLength);
+        Assert.assertEquals(account.getCurrency(), currency);
+        Assert.assertEquals(account.getBillingCycleDayLocal(), billCycleDay.getDayOfMonthLocal());
+        Assert.assertEquals(account.getBillingCycleDayUtc(), billCycleDay.getDayOfMonthUTC());
+        Assert.assertEquals(account.getPaymentMethodId(), paymentMethodId);
+        Assert.assertEquals(account.getTimeZone(), timeZone);
+        Assert.assertEquals(account.getLocale(), locale);
+        Assert.assertEquals(account.getAddress1(), address1);
+        Assert.assertEquals(account.getAddress2(), address2);
+        Assert.assertEquals(account.getCompanyName(), companyName);
+        Assert.assertEquals(account.getCity(), city);
+        Assert.assertEquals(account.getStateOrProvince(), stateOrProvince);
+        Assert.assertEquals(account.getCountry(), country);
+        Assert.assertEquals(account.getPostalCode(), postalCode);
+        Assert.assertEquals(account.getPhone(), phone);
+        Assert.assertEquals(account.getMigrated(), isMigrated);
+        Assert.assertEquals(account.getIsNotifiedForInvoices(), isNotifiedForInvoices);
+    }
+
+    @Test(groups = "fast")
+    public void testAddEmail() throws Exception {
+        final UUID accountId = UUID.randomUUID();
+
+        // Verify the initial state
+        Assert.assertEquals(accountDao.getEmailsByAccountId(accountId, tenantContext).size(), 0);
+
+        // Add the first email
+        final String emailAddress1 = UUID.randomUUID().toString();
+        final AccountEmail email1 = new DefaultAccountEmail(accountId, emailAddress1);
+        accountUserApi.addEmail(accountId, email1, callContext);
+        Assert.assertEquals(accountDao.getEmailsByAccountId(accountId, tenantContext).size(), 1);
+
+        // Add a second one
+        final String emailAddress2 = UUID.randomUUID().toString();
+        final AccountEmail email2 = new DefaultAccountEmail(accountId, emailAddress2);
+        accountUserApi.addEmail(accountId, email2, callContext);
+        Assert.assertEquals(accountDao.getEmailsByAccountId(accountId, tenantContext).size(), 2);
+
+        // Remove the first second one
+        accountUserApi.removeEmail(accountId, email1, callContext);
+        Assert.assertEquals(accountDao.getEmailsByAccountId(accountId, tenantContext).size(), 1);
+    }
+}
diff --git a/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java b/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
index 6940c41..e4ad71a 100644
--- a/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
@@ -42,14 +42,12 @@ import com.ning.billing.util.svcsapi.bus.InternalBus.EventBusException;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
-import com.google.inject.Inject;
 
 public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account, AccountApiException> implements AccountDao {
 
     private final MockEntityDaoBase<AccountEmailModelDao, AccountEmail, AccountApiException> accountEmailSqlDao = new MockEntityDaoBase<AccountEmailModelDao, AccountEmail, AccountApiException>();
     private final InternalBus eventBus;
 
-    @Inject
     public MockAccountDao(final InternalBus eventBus) {
         this.eventBus = eventBus;
     }
@@ -64,15 +62,15 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account, 
                                                         : context.getTenantRecordId();
             eventBus.post(new DefaultAccountCreationEvent(account, null, accountRecordId, tenantRecordId), context);
         } catch (final EventBusException ex) {
-            throw new RuntimeException(ex);
+            Assert.fail(ex.toString());
         }
     }
 
     @Override
     public void update(final AccountModelDao account, final InternalCallContext context) {
-        final AccountModelDao currentAccount = getById(account.getId(), context);
         super.update(account, context);
 
+        final AccountModelDao currentAccount = getById(account.getId(), context);
         final Long accountRecordId = getRecordId(account.getId(), context);
         final long tenantRecordId = context == null ? InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID
                                                     : context.getTenantRecordId();
@@ -82,7 +80,7 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account, 
             try {
                 eventBus.post(changeEvent, context);
             } catch (final EventBusException ex) {
-                throw new RuntimeException(ex);
+                Assert.fail(ex.toString());
             }
         }
     }
diff --git a/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java b/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java
index 95c0653..2e2f87d 100644
--- a/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java
@@ -27,77 +27,71 @@ import org.testng.annotations.Test;
 
 import com.ning.billing.ErrorCode;
 import com.ning.billing.ObjectType;
+import com.ning.billing.account.AccountTestBase;
 import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountData;
 import com.ning.billing.account.api.AccountEmail;
 import com.ning.billing.account.api.BillCycleDay;
 import com.ning.billing.account.api.DefaultAccount;
 import com.ning.billing.account.api.DefaultAccountEmail;
-import com.ning.billing.account.api.DefaultBillCycleDay;
 import com.ning.billing.account.api.MutableAccountData;
-import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.mock.MockAccountBuilder;
+import com.ning.billing.util.api.AuditLevel;
 import com.ning.billing.util.api.CustomFieldApiException;
 import com.ning.billing.util.api.TagApiException;
-import com.ning.billing.util.audit.dao.AuditDao;
-import com.ning.billing.util.audit.dao.DefaultAuditDao;
+import com.ning.billing.util.api.TagDefinitionApiException;
+import com.ning.billing.util.audit.AuditLog;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.customfield.CustomField;
 import com.ning.billing.util.customfield.StringCustomField;
-import com.ning.billing.util.customfield.dao.CustomFieldDao;
 import com.ning.billing.util.customfield.dao.CustomFieldModelDao;
-import com.ning.billing.util.customfield.dao.DefaultCustomFieldDao;
-import com.ning.billing.util.entity.EntityPersistenceException;
-import com.ning.billing.util.tag.DefaultTagDefinition;
+import com.ning.billing.util.dao.TableName;
 import com.ning.billing.util.tag.DescriptiveTag;
 import com.ning.billing.util.tag.Tag;
-import com.ning.billing.util.tag.TagDefinition;
-import com.ning.billing.util.tag.dao.DefaultTagDao;
-import com.ning.billing.util.tag.dao.TagDao;
 import com.ning.billing.util.tag.dao.TagDefinitionModelDao;
-import com.ning.billing.util.tag.dao.TagDefinitionSqlDao;
 import com.ning.billing.util.tag.dao.TagModelDao;
 
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertNull;
-import static org.testng.Assert.assertTrue;
-import static org.testng.Assert.fail;
-
-public class TestAccountDao extends AccountDaoTestBase {
+public class TestAccountDao extends AccountTestBase {
 
     @Test(groups = "slow")
     public void testBasic() throws AccountApiException {
-        final AccountModelDao a = createTestAccount(5);
-        accountDao.create(a, internalCallContext);
-        final String key = a.getExternalKey();
+        final AccountModelDao account = createTestAccount();
+        accountDao.create(account, internalCallContext);
 
-        AccountModelDao r = accountDao.getAccountByKey(key, internalCallContext);
-        assertNotNull(r);
-        assertEquals(r.getExternalKey(), a.getExternalKey());
+        // Retrieve by key
+        AccountModelDao retrievedAccount = accountDao.getAccountByKey(account.getExternalKey(), internalCallContext);
+        checkAccountsEqual(retrievedAccount, account);
 
-        r = accountDao.getById(r.getId(), internalCallContext);
-        assertNotNull(r);
-        assertEquals(r.getExternalKey(), a.getExternalKey());
+        // Retrieve by id
+        retrievedAccount = accountDao.getById(retrievedAccount.getId(), internalCallContext);
+        checkAccountsEqual(retrievedAccount, account);
 
+        // Retrieve all
         final List<AccountModelDao> all = accountDao.get(internalCallContext);
-        assertNotNull(all);
-        assertTrue(all.size() >= 1);
+        Assert.assertNotNull(all);
+        Assert.assertEquals(all.size(), 1);
+        checkAccountsEqual(all.get(0), account);
+
+        // Verify audits
+        final List<AuditLog> auditLogsForAccount = auditDao.getAuditLogsForId(TableName.ACCOUNT, account.getId(), AuditLevel.FULL, internalCallContext);
+        Assert.assertEquals(auditLogsForAccount.size(), 1);
+        Assert.assertEquals(auditLogsForAccount.get(0).getChangeType(), ChangeType.INSERT);
     }
 
-    // simple test to ensure long phone numbers can be stored
+    // Simple test to ensure long phone numbers can be stored
     @Test(groups = "slow")
     public void testLongPhoneNumber() throws AccountApiException {
-        final AccountModelDao account = createTestAccount(1, "123456789012345678901234");
+        final AccountModelDao account = createTestAccount("123456789012345678901234");
         accountDao.create(account, internalCallContext);
 
-        final AccountModelDao saved = accountDao.getAccountByKey(account.getExternalKey(), internalCallContext);
-        assertNotNull(saved);
+        final AccountModelDao retrievedAccount = accountDao.getAccountByKey(account.getExternalKey(), internalCallContext);
+        checkAccountsEqual(retrievedAccount, account);
     }
 
-    // simple test to ensure excessively long phone numbers cannot be stored
+    // Simple test to ensure excessively long phone numbers cannot be stored
     @Test(groups = "slow")
     public void testOverlyLongPhoneNumber() throws AccountApiException {
-        final AccountModelDao account = createTestAccount(1, "12345678901234567890123456");
+        final AccountModelDao account = createTestAccount("12345678901234567890123456");
         try {
             accountDao.create(account, internalCallContext);
             Assert.fail();
@@ -107,83 +101,53 @@ public class TestAccountDao extends AccountDaoTestBase {
     }
 
     @Test(groups = "slow")
-    public void testGetById() throws AccountApiException {
-        AccountModelDao account = createTestAccount(1);
-        final UUID id = account.getId();
-        final String key = account.getExternalKey();
-        final String name = account.getName();
-        final Integer firstNameLength = account.getFirstNameLength();
-
-        accountDao.create(account, internalCallContext);
-
-        account = accountDao.getById(id, internalCallContext);
-        assertNotNull(account);
-        assertEquals(account.getId(), id);
-        assertEquals(account.getExternalKey(), key);
-        assertEquals(account.getName(), name);
-        assertEquals(account.getFirstNameLength(), firstNameLength);
-    }
-
-    @Test(groups = "slow")
     public void testCustomFields() throws CustomFieldApiException {
-        final String fieldName = "testField1";
-        final String fieldValue = "testField1_value";
-
         final UUID accountId = UUID.randomUUID();
+        final String fieldName = UUID.randomUUID().toString().substring(0, 4);
+        final String fieldValue = UUID.randomUUID().toString();
 
-        CustomField field = new StringCustomField(fieldName, fieldValue, ObjectType.ACCOUNT, accountId, internalCallContext.getCreatedDate());
-        final CustomFieldDao customFieldDao = new DefaultCustomFieldDao(dbi);
+        final CustomField field = new StringCustomField(fieldName, fieldValue, ObjectType.ACCOUNT, accountId, internalCallContext.getCreatedDate());
         customFieldDao.create(new CustomFieldModelDao(field), internalCallContext);
 
         final List<CustomFieldModelDao> customFieldMap = customFieldDao.getCustomFields(accountId, ObjectType.ACCOUNT, internalCallContext);
-        assertEquals(customFieldMap.size(), 1);
+        Assert.assertEquals(customFieldMap.size(), 1);
+
         final CustomFieldModelDao customField = customFieldMap.get(0);
-        assertEquals(customField.getFieldName(), fieldName);
-        assertEquals(customField.getFieldValue(), fieldValue);
+        Assert.assertEquals(customField.getFieldName(), fieldName);
+        Assert.assertEquals(customField.getFieldValue(), fieldValue);
     }
 
     @Test(groups = "slow")
-    public void testTags() throws EntityPersistenceException, TagApiException {
-        final AccountModelDao account = createTestAccount(1);
-        final TagDefinition definition = new DefaultTagDefinition("Test Tag", "For testing only", false);
-        final TagDefinitionSqlDao tagDefinitionDao = dbi.onDemand(TagDefinitionSqlDao.class);
-        tagDefinitionDao.create(new TagDefinitionModelDao(definition), internalCallContext);
-
-        final TagDao tagDao = new DefaultTagDao(dbi, tagEventBuilder, bus);
-
-        final TagDefinitionModelDao tagDefinition = tagDefinitionDao.getById(definition.getId().toString(), internalCallContext);
+    public void testTags() throws TagApiException, TagDefinitionApiException {
+        final AccountModelDao account = createTestAccount();
+        final TagDefinitionModelDao tagDefinition = tagDefinitionDao.create(UUID.randomUUID().toString().substring(0, 4), UUID.randomUUID().toString(), internalCallContext);
         final Tag tag = new DescriptiveTag(tagDefinition.getId(), ObjectType.ACCOUNT, account.getId(), internalCallContext.getCreatedDate());
-
         tagDao.create(new TagModelDao(tag), internalCallContext);
 
         final List<TagModelDao> tags = tagDao.getTags(account.getId(), ObjectType.ACCOUNT, internalCallContext);
-        assertEquals(tags.size(), 1);
-
-        assertEquals(tags.get(0).getTagDefinitionId(), definition.getId());
+        Assert.assertEquals(tags.size(), 1);
+        Assert.assertEquals(tags.get(0).getTagDefinitionId(), tagDefinition.getId());
+        Assert.assertEquals(tags.get(0).getObjectId(), account.getId());
+        Assert.assertEquals(tags.get(0).getObjectType(), ObjectType.ACCOUNT);
     }
 
     @Test(groups = "slow")
     public void testGetIdFromKey() throws AccountApiException {
-        final AccountModelDao account = createTestAccount(1);
+        final AccountModelDao account = createTestAccount();
         accountDao.create(account, internalCallContext);
 
-        try {
-            final UUID accountId = accountDao.getIdFromKey(account.getExternalKey(), internalCallContext);
-            assertEquals(accountId, account.getId());
-        } catch (AccountApiException a) {
-            fail("Retrieving account failed.");
-        }
+        final UUID accountId = accountDao.getIdFromKey(account.getExternalKey(), internalCallContext);
+        Assert.assertEquals(accountId, account.getId());
     }
 
     @Test(groups = "slow", expectedExceptions = AccountApiException.class)
     public void testGetIdFromKeyForNullKey() throws AccountApiException {
-        final String key = null;
-        accountDao.getIdFromKey(key, internalCallContext);
+        accountDao.getIdFromKey(null, internalCallContext);
     }
 
     @Test(groups = "slow")
     public void testUpdate() throws Exception {
-        final AccountModelDao account = createTestAccount(1);
+        final AccountModelDao account = createTestAccount();
         accountDao.create(account, internalCallContext);
 
         final AccountData accountData = new MockAccountBuilder(new DefaultAccount(account)).migrated(false)
@@ -191,87 +155,29 @@ public class TestAccountDao extends AccountDaoTestBase {
                                                                                            .timeZone(DateTimeZone.forID("Australia/Darwin"))
                                                                                            .locale("FR-CA")
                                                                                            .build();
-
         final AccountModelDao updatedAccount = new AccountModelDao(account.getId(), accountData);
         accountDao.update(updatedAccount, internalCallContext);
 
-        final AccountModelDao savedAccount = accountDao.getAccountByKey(account.getExternalKey(), internalCallContext);
-
-        assertNotNull(savedAccount);
-        assertEquals(savedAccount.getName(), updatedAccount.getName());
-        assertEquals(savedAccount.getEmail(), updatedAccount.getEmail());
-        assertEquals(savedAccount.getPaymentMethodId(), updatedAccount.getPaymentMethodId());
-        assertEquals(savedAccount.getBillingCycleDayLocal(), updatedAccount.getBillingCycleDayLocal());
-        assertEquals(savedAccount.getBillingCycleDayUtc(), updatedAccount.getBillingCycleDayUtc());
-        assertEquals(savedAccount.getFirstNameLength(), updatedAccount.getFirstNameLength());
-        assertEquals(savedAccount.getTimeZone(), updatedAccount.getTimeZone());
-        assertEquals(savedAccount.getLocale(), updatedAccount.getLocale());
-        assertEquals(savedAccount.getAddress1(), updatedAccount.getAddress1());
-        assertEquals(savedAccount.getAddress2(), updatedAccount.getAddress2());
-        assertEquals(savedAccount.getCity(), updatedAccount.getCity());
-        assertEquals(savedAccount.getStateOrProvince(), updatedAccount.getStateOrProvince());
-        assertEquals(savedAccount.getCountry(), updatedAccount.getCountry());
-        assertEquals(savedAccount.getPostalCode(), updatedAccount.getPostalCode());
-        assertEquals(savedAccount.getPhone(), updatedAccount.getPhone());
+        final AccountModelDao retrievedAccount = accountDao.getAccountByKey(account.getExternalKey(), internalCallContext);
+        checkAccountsEqual(retrievedAccount, updatedAccount);
     }
 
     @Test(groups = "slow")
     public void testUpdatePaymentMethod() throws Exception {
-        final AccountModelDao account = createTestAccount(1);
+        final AccountModelDao account = createTestAccount();
         accountDao.create(account, internalCallContext);
 
         final UUID newPaymentMethodId = UUID.randomUUID();
         accountDao.updatePaymentMethod(account.getId(), newPaymentMethodId, internalCallContext);
 
         final AccountModelDao newAccount = accountDao.getById(account.getId(), internalCallContext);
-        assertEquals(newAccount.getPaymentMethodId(), newPaymentMethodId);
+        Assert.assertEquals(newAccount.getPaymentMethodId(), newPaymentMethodId);
 
-        // And then set it to null
+        // And then set it to null (delete the default payment method)
         accountDao.updatePaymentMethod(account.getId(), null, internalCallContext);
 
         final AccountModelDao newAccountWithPMNull = accountDao.getById(account.getId(), internalCallContext);
-        assertNull(newAccountWithPMNull.getPaymentMethodId());
-
-    }
-
-    @Test(groups = "slow")
-    public void testAddingContactInformation() throws Exception {
-        final UUID accountId = UUID.randomUUID();
-        final DefaultAccount account = new DefaultAccount(accountId, "extKey123456", "myemail123456@glam.com",
-                                                          "John Smith", 4, Currency.USD, new DefaultBillCycleDay(15), null,
-                                                          DateTimeZone.forID("America/Cambridge_Bay"), "EN-CA",
-                                                          null, null, null, null, null, null, null, null, false, false);
-        accountDao.create(new AccountModelDao(accountId, account), internalCallContext);
-
-        final String address1 = "123 address 1";
-        final String address2 = "456 address 2";
-        final String companyName = "Some Company";
-        final String city = "Cambridge Bay";
-        final String stateOrProvince = "Nunavut";
-        final String country = "Canada";
-        final String postalCode = "X0B 0C0";
-        final String phone = "18001112222";
-
-        final DefaultAccount updatedAccount = new DefaultAccount(accountId, "extKey123456", "myemail123456@glam.com",
-                                                                 "John Smith", 4, Currency.USD, new DefaultBillCycleDay(15), null,
-                                                                 DateTimeZone.forID("America/Cambridge_Bay"), "EN-CA",
-                                                                 address1, address2, companyName, city, stateOrProvince, country,
-                                                                 postalCode, phone, false, false);
-
-        accountDao.update(new AccountModelDao(accountId, updatedAccount), internalCallContext);
-
-        final AccountModelDao savedAccount = accountDao.getById(accountId, internalCallContext);
-
-        assertNotNull(savedAccount);
-        assertEquals(savedAccount.getId(), accountId);
-        assertEquals(savedAccount.getAddress1(), address1);
-        assertEquals(savedAccount.getAddress2(), address2);
-        assertEquals(savedAccount.getCompanyName(), companyName);
-        assertEquals(savedAccount.getCity(), city);
-        assertEquals(savedAccount.getStateOrProvince(), stateOrProvince);
-        assertEquals(savedAccount.getCity(), city);
-        assertEquals(savedAccount.getPostalCode(), postalCode);
-        assertEquals(savedAccount.getPhone(), phone);
+        Assert.assertNull(newAccountWithPMNull.getPaymentMethodId());
     }
 
     @Test(groups = "slow")
@@ -282,13 +188,11 @@ public class TestAccountDao extends AccountDaoTestBase {
         final MutableAccountData otherAccount = new DefaultAccount(account).toMutableAccountData();
         otherAccount.setAddress1(UUID.randomUUID().toString());
         otherAccount.setEmail(UUID.randomUUID().toString());
-
         final AccountModelDao newAccount = new AccountModelDao(account.getId(), otherAccount);
         accountDao.update(newAccount, internalCallContext);
 
         final AccountModelDao retrievedAccount = accountDao.getById(account.getId(), internalCallContext);
-        Assert.assertEquals(retrievedAccount.getAddress1(), newAccount.getAddress1());
-        Assert.assertEquals(retrievedAccount.getEmail(), newAccount.getEmail());
+        checkAccountsEqual(retrievedAccount, newAccount);
     }
 
     @Test(groups = "slow")
@@ -299,7 +203,7 @@ public class TestAccountDao extends AccountDaoTestBase {
         final MutableAccountData otherAccount = new DefaultAccount(account).toMutableAccountData();
         otherAccount.setAddress1(UUID.randomUUID().toString());
         otherAccount.setEmail(UUID.randomUUID().toString());
-        // Same BCD, but not .equals method
+        // Same BCD, but no .equals method
         otherAccount.setBillCycleDay(new BillCycleDay() {
             @Override
             public int getDayOfMonthUTC() {
@@ -316,9 +220,8 @@ public class TestAccountDao extends AccountDaoTestBase {
         accountDao.update(newAccount, internalCallContext);
 
         final AccountModelDao newFetchedAccount = accountDao.getById(account.getId(), internalCallContext);
-        Assert.assertEquals(newFetchedAccount.getAddress1(), newAccount.getAddress1());
-        Assert.assertEquals(newFetchedAccount.getEmail(), newAccount.getEmail());
-        // Same BCD
+        checkAccountsEqual(newFetchedAccount, newAccount);
+        // Verify it is the same BCD
         Assert.assertEquals(newFetchedAccount.getBillingCycleDayUtc(), account.getBillingCycleDayUtc());
         Assert.assertEquals(newFetchedAccount.getBillingCycleDayLocal(), account.getBillingCycleDayLocal());
     }
@@ -327,9 +230,8 @@ public class TestAccountDao extends AccountDaoTestBase {
     public void testShouldBeAbleToHandleBCDOfZeroZero() throws Exception {
         final AccountModelDao account = createTestAccount(0);
         accountDao.create(account, internalCallContext);
-        final AccountModelDao fetchedAccount = accountDao.getById(account.getId(), internalCallContext);
 
-        final MutableAccountData otherAccount = new DefaultAccount(fetchedAccount).toMutableAccountData();
+        final MutableAccountData otherAccount = new DefaultAccount(account).toMutableAccountData();
         // Set BCD to null
         otherAccount.setBillCycleDay(null);
 
@@ -338,8 +240,7 @@ public class TestAccountDao extends AccountDaoTestBase {
 
         // Same BCD (zero/zero)
         final AccountModelDao retrievedAccount = accountDao.getById(account.getId(), internalCallContext);
-        Assert.assertEquals(retrievedAccount.getBillingCycleDayUtc(), fetchedAccount.getBillingCycleDayUtc());
-        Assert.assertEquals(retrievedAccount.getBillingCycleDayLocal(), fetchedAccount.getBillingCycleDayLocal());
+        checkAccountsEqual(retrievedAccount, account);
     }
 
     @Test(groups = "slow")
@@ -362,63 +263,69 @@ public class TestAccountDao extends AccountDaoTestBase {
     }
 
     @Test(groups = "slow")
-    public void testAccountEmail() throws AccountApiException {
-        List<AccountEmailModelDao> emails;
-
-        // generate random account id
+    public void testAddRemoveAccountEmail() throws AccountApiException {
         final UUID accountId = UUID.randomUUID();
 
-        // add a new e-mail
+        // Add a new e-mail
         final AccountEmail email = new DefaultAccountEmail(accountId, "test@gmail.com");
         accountDao.addEmail(new AccountEmailModelDao(email), internalCallContext);
-        emails = accountDao.getEmailsByAccountId(accountId, internalCallContext);
-        assertEquals(emails.size(), 1);
 
-        // verify that audit contains one entry
-        final AuditDao audit = new DefaultAuditDao(dbi);
-        // TODO - uncomment when TableName story is fixed
-        //final List<AuditLog> auditLogs = audit.getAuditLogsForId(TableName.ACCOUNT_EMAIL, email.getId(), AuditLevel.FULL, internalCallContext);
-        //assertEquals(auditLogs.size(), 1);
+        final List<AccountEmailModelDao> accountEmails = accountDao.getEmailsByAccountId(accountId, internalCallContext);
+        Assert.assertEquals(accountEmails.size(), 1);
+        Assert.assertEquals(accountEmails.get(0).getAccountId(), accountId);
+        Assert.assertEquals(accountEmails.get(0).getEmail(), email.getEmail());
 
-        // delete e-mail
-        accountDao.removeEmail(new AccountEmailModelDao(email), internalCallContext);
+        // Verify audits
+        final List<AuditLog> auditLogsForAccountEmail = auditDao.getAuditLogsForId(TableName.ACCOUNT_EMAIL, email.getId(), AuditLevel.FULL, internalCallContext);
+        Assert.assertEquals(auditLogsForAccountEmail.size(), 1);
+        Assert.assertEquals(auditLogsForAccountEmail.get(0).getChangeType(), ChangeType.INSERT);
 
-        emails = accountDao.getEmailsByAccountId(accountId, internalCallContext);
-        assertEquals(emails.size(), 0);
+        // Delete the e-mail
+        accountDao.removeEmail(new AccountEmailModelDao(email), internalCallContext);
+        Assert.assertEquals(accountDao.getEmailsByAccountId(accountId, internalCallContext).size(), 0);
     }
 
     @Test(groups = "slow")
-    public void testAddAndRemoveAccountEmail() throws AccountApiException {
+    public void testAddAndRemoveMultipleAccountEmails() throws AccountApiException {
         final UUID accountId = UUID.randomUUID();
         final String email1 = UUID.randomUUID().toString();
         final String email2 = UUID.randomUUID().toString();
 
         // Verify the original state
-        assertEquals(accountDao.getEmailsByAccountId(accountId, internalCallContext).size(), 0);
+        Assert.assertEquals(accountDao.getEmailsByAccountId(accountId, internalCallContext).size(), 0);
 
         // Add a new e-mail
         final AccountEmail accountEmail1 = new DefaultAccountEmail(accountId, email1);
         accountDao.addEmail(new AccountEmailModelDao(accountEmail1), internalCallContext);
         final List<AccountEmailModelDao> firstEmails = accountDao.getEmailsByAccountId(accountId, internalCallContext);
-        assertEquals(firstEmails.size(), 1);
-        assertEquals(firstEmails.get(0).getAccountId(), accountId);
-        assertEquals(firstEmails.get(0).getEmail(), email1);
+        Assert.assertEquals(firstEmails.size(), 1);
+        Assert.assertEquals(firstEmails.get(0).getAccountId(), accountId);
+        Assert.assertEquals(firstEmails.get(0).getEmail(), email1);
 
         // Add a second e-mail
         final AccountEmail accountEmail2 = new DefaultAccountEmail(accountId, email2);
         accountDao.addEmail(new AccountEmailModelDao(accountEmail2), internalCallContext);
         final List<AccountEmailModelDao> secondEmails = accountDao.getEmailsByAccountId(accountId, internalCallContext);
-        assertEquals(secondEmails.size(), 2);
-        assertTrue(secondEmails.get(0).getAccountId().equals(accountId));
-        assertTrue(secondEmails.get(1).getAccountId().equals(accountId));
-        assertTrue(secondEmails.get(0).getEmail().equals(email1) || secondEmails.get(0).getEmail().equals(email2));
-        assertTrue(secondEmails.get(1).getEmail().equals(email1) || secondEmails.get(1).getEmail().equals(email2));
+        Assert.assertEquals(secondEmails.size(), 2);
+        Assert.assertTrue(secondEmails.get(0).getAccountId().equals(accountId));
+        Assert.assertTrue(secondEmails.get(1).getAccountId().equals(accountId));
+        Assert.assertTrue(secondEmails.get(0).getEmail().equals(email1) || secondEmails.get(0).getEmail().equals(email2));
+        Assert.assertTrue(secondEmails.get(1).getEmail().equals(email1) || secondEmails.get(1).getEmail().equals(email2));
 
         // Delete the first e-mail
         accountDao.removeEmail(new AccountEmailModelDao(accountEmail1), internalCallContext);
         final List<AccountEmailModelDao> thirdEmails = accountDao.getEmailsByAccountId(accountId, internalCallContext);
-        assertEquals(thirdEmails.size(), 1);
-        assertEquals(thirdEmails.get(0).getAccountId(), accountId);
-        assertEquals(thirdEmails.get(0).getEmail(), email2);
+        Assert.assertEquals(thirdEmails.size(), 1);
+        Assert.assertEquals(thirdEmails.get(0).getAccountId(), accountId);
+        Assert.assertEquals(thirdEmails.get(0).getEmail(), email2);
+
+        // Verify audits
+        final List<AuditLog> auditLogsForAccountEmail1 = auditDao.getAuditLogsForId(TableName.ACCOUNT_EMAIL, accountEmail1.getId(), AuditLevel.FULL, internalCallContext);
+        Assert.assertEquals(auditLogsForAccountEmail1.size(), 2);
+        Assert.assertEquals(auditLogsForAccountEmail1.get(0).getChangeType(), ChangeType.INSERT);
+        Assert.assertEquals(auditLogsForAccountEmail1.get(1).getChangeType(), ChangeType.DELETE);
+        final List<AuditLog> auditLogsForAccountEmail2 = auditDao.getAuditLogsForId(TableName.ACCOUNT_EMAIL, accountEmail2.getId(), AuditLevel.FULL, internalCallContext);
+        Assert.assertEquals(auditLogsForAccountEmail2.size(), 1);
+        Assert.assertEquals(auditLogsForAccountEmail2.get(0).getChangeType(), ChangeType.INSERT);
     }
 }
diff --git a/server/src/main/java/com/ning/billing/server/modules/DBIProvider.java b/server/src/main/java/com/ning/billing/server/modules/DBIProvider.java
new file mode 100644
index 0000000..5d988df
--- /dev/null
+++ b/server/src/main/java/com/ning/billing/server/modules/DBIProvider.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2010-2012 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.server.modules;
+
+import java.util.concurrent.TimeUnit;
+
+import org.skife.jdbi.v2.DBI;
+import org.skife.jdbi.v2.TimingCollector;
+import org.skife.jdbi.v2.tweak.SQLLog;
+import org.skife.jdbi.v2.tweak.TransactionHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.ning.billing.util.dao.DateTimeArgumentFactory;
+import com.ning.billing.util.dao.DateTimeZoneArgumentFactory;
+import com.ning.billing.util.dao.EnumArgumentFactory;
+import com.ning.billing.util.dao.LocalDateArgumentFactory;
+import com.ning.billing.util.dao.UUIDArgumentFactory;
+import com.ning.billing.util.dao.UuidMapper;
+import com.ning.jetty.jdbi.config.DaoConfig;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.jolbox.bonecp.BoneCPConfig;
+import com.jolbox.bonecp.BoneCPDataSource;
+import com.yammer.metrics.core.MetricsRegistry;
+import com.yammer.metrics.jdbi.InstrumentedTimingCollector;
+import com.yammer.metrics.jdbi.strategies.BasicSqlNameStrategy;
+
+public class DBIProvider implements Provider<DBI> {
+
+    private static final Logger logger = LoggerFactory.getLogger(DBIProvider.class);
+
+    private final MetricsRegistry metricsRegistry;
+    private final DaoConfig config;
+    private SQLLog sqlLog;
+
+    @Inject
+    public DBIProvider(final MetricsRegistry metricsRegistry, final DaoConfig config) {
+        this.metricsRegistry = metricsRegistry;
+        this.config = config;
+    }
+
+    @Inject(optional = true)
+    public void setSqlLog(final SQLLog sqlLog) {
+        this.sqlLog = sqlLog;
+    }
+
+    @Override
+    public DBI get() {
+        final BoneCPConfig dbConfig = new BoneCPConfig();
+        dbConfig.setJdbcUrl(config.getJdbcUrl());
+        dbConfig.setUsername(config.getUsername());
+        dbConfig.setPassword(config.getPassword());
+        dbConfig.setMinConnectionsPerPartition(config.getMinIdle());
+        dbConfig.setMaxConnectionsPerPartition(config.getMaxActive());
+        dbConfig.setConnectionTimeout(config.getConnectionTimeout().getPeriod(), config.getConnectionTimeout().getUnit());
+        dbConfig.setIdleMaxAge(config.getIdleMaxAge().getPeriod(), config.getIdleMaxAge().getUnit());
+        dbConfig.setMaxConnectionAge(config.getMaxConnectionAge().getPeriod(), config.getMaxConnectionAge().getUnit());
+        dbConfig.setIdleConnectionTestPeriod(config.getIdleConnectionTestPeriod().getPeriod(), config.getIdleConnectionTestPeriod().getUnit());
+        dbConfig.setPartitionCount(1);
+        dbConfig.setDefaultTransactionIsolation("READ_COMMITTED");
+        dbConfig.setDisableJMX(false);
+
+        final BoneCPDataSource ds = new BoneCPDataSource(dbConfig);
+        final DBI dbi = new DBI(ds);
+        dbi.registerArgumentFactory(new UUIDArgumentFactory());
+        dbi.registerArgumentFactory(new DateTimeZoneArgumentFactory());
+        dbi.registerArgumentFactory(new DateTimeArgumentFactory());
+        dbi.registerArgumentFactory(new LocalDateArgumentFactory());
+        dbi.registerArgumentFactory(new EnumArgumentFactory());
+        dbi.registerMapper(new UuidMapper());
+
+        if (sqlLog != null) {
+            dbi.setSQLLog(sqlLog);
+        }
+
+        if (config.getTransactionHandlerClass() != null) {
+            logger.info("Using " + config.getTransactionHandlerClass() + " as a transaction handler class");
+            try {
+                dbi.setTransactionHandler((TransactionHandler) Class.forName(config.getTransactionHandlerClass()).newInstance());
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        final BasicSqlNameStrategy basicSqlNameStrategy = new BasicSqlNameStrategy();
+        final TimingCollector timingCollector = new InstrumentedTimingCollector(metricsRegistry, basicSqlNameStrategy, TimeUnit.MILLISECONDS, TimeUnit.SECONDS);
+        dbi.setTimingCollector(timingCollector);
+
+        return dbi;
+    }
+}
diff --git a/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java b/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
index 5a25746..0bed99d 100644
--- a/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
+++ b/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
@@ -16,7 +16,6 @@
 
 package com.ning.billing.server.modules;
 
-
 import org.skife.jdbi.v2.DBI;
 import org.skife.jdbi.v2.IDBI;
 
@@ -55,11 +54,11 @@ import com.ning.billing.util.glue.ExportModule;
 import com.ning.billing.util.glue.GlobalLockerModule;
 import com.ning.billing.util.glue.NotificationQueueModule;
 import com.ning.billing.util.glue.TagStoreModule;
-import com.ning.jetty.jdbi.guice.providers.DBIProvider;
 
 import com.google.inject.AbstractModule;
 
 public class KillbillServerModule extends AbstractModule {
+
     @Override
     protected void configure() {
         configureDao();
@@ -68,17 +67,15 @@ public class KillbillServerModule extends AbstractModule {
         configurePushNotification();
     }
 
-
     protected void configurePushNotification() {
         bind(ServerService.class).to(DefaultServerService.class).asEagerSingleton();
         bind(PushNotificationListener.class).asEagerSingleton();
     }
 
-
     protected void configureDao() {
         // Load mysql driver if needed
         try {
-            Class.forName ("com.mysql.jdbc.Driver").newInstance ();
+            Class.forName("com.mysql.jdbc.Driver").newInstance();
         } catch (final Exception ignore) {
         }
         bind(IDBI.class).to(DBI.class).asEagerSingleton();
diff --git a/tenant/src/main/resources/com/ning/billing/tenant/dao/TenantKVSqlDao.sql.stg b/tenant/src/main/resources/com/ning/billing/tenant/dao/TenantKVSqlDao.sql.stg
index b44b46c..fac3271 100644
--- a/tenant/src/main/resources/com/ning/billing/tenant/dao/TenantKVSqlDao.sql.stg
+++ b/tenant/src/main/resources/com/ning/billing/tenant/dao/TenantKVSqlDao.sql.stg
@@ -22,7 +22,7 @@ tableValues() ::= <<
 , :updatedBy
 >>
 
-accountRecordIdFieldWithComma() ::= ""
+accountRecordIdFieldWithComma(prefix) ::= ""
 
 accountRecordIdValueWithComma() ::= ""
 
diff --git a/util/src/main/resources/com/ning/billing/util/tag/dao/TagDefinitionSqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/tag/dao/TagDefinitionSqlDao.sql.stg
index 2d796c1..cdb5144 100644
--- a/util/src/main/resources/com/ning/billing/util/tag/dao/TagDefinitionSqlDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/tag/dao/TagDefinitionSqlDao.sql.stg
@@ -22,7 +22,7 @@ tableValues() ::= <<
 , :updatedDate
 >>
 
-accountRecordIdFieldWithComma() ::= ""
+accountRecordIdFieldWithComma(prefix) ::= ""
 
 accountRecordIdValueWithComma() ::= ""