killbill-memoizeit

Merge remote-tracking branch 'origin/audit-rework-nocleanup'

11/16/2012 8:58:57 PM

Changes

account/src/test/java/com/ning/billing/account/api/user/DefaultAccountUserApiTestWithDB.java 106(+0 -106)

account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java 89(+0 -89)

account/src/test/java/com/ning/billing/account/glue/AccountModuleWithMocks.java 38(+0 -38)

Details

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 a633e09..0403101 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
@@ -149,12 +149,14 @@ public class DefaultAccountUserApi implements AccountUserApi {
         final CallContext migrationContext = callContextFactory.toMigrationCallContext(context, createdDate, updatedDate);
 
         // Create the account
-        final Account account = new DefaultAccount(UUID.randomUUID(), data);
-        createAccount(account, migrationContext);
+        final Account account = createAccount(data, migrationContext);
 
         // Add associated contact emails
-        for (final String cur : data.getAdditionalContactEmails()) {
-            addEmail(account.getId(), new DefaultAccountEmail(account.getId(), cur), migrationContext);
+        // In Killbill, we never return null for empty lists, but MigrationAccountData is implemented outside of Killbill
+        if (data.getAdditionalContactEmails() != null) {
+            for (final String cur : data.getAdditionalContactEmails()) {
+                addEmail(account.getId(), new DefaultAccountEmail(account.getId(), cur), migrationContext);
+            }
         }
 
         return account;
diff --git a/account/src/main/resources/com/ning/billing/account/ddl.sql b/account/src/main/resources/com/ning/billing/account/ddl.sql
index 730906e..28dc6e1 100644
--- a/account/src/main/resources/com/ning/billing/account/ddl.sql
+++ b/account/src/main/resources/com/ning/billing/account/ddl.sql
@@ -85,6 +85,7 @@ CREATE TABLE account_emails (
     PRIMARY KEY(record_id)
 ) ENGINE=innodb;
 CREATE UNIQUE INDEX account_email_id ON account_emails(id);
+CREATE INDEX account_email_account_id_email ON account_emails(account_id, email);
 CREATE INDEX account_emails_tenant_account_record_id ON account_emails(tenant_record_id, account_record_id);
 
 DROP TABLE IF EXISTS account_email_history;
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..b75ec6f
--- /dev/null
+++ b/account/src/test/java/com/ning/billing/account/AccountTestBase.java
@@ -0,0 +1,177 @@
+/*
+ * 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 AccountData retrievedAccount, final AccountData account) {
+        final UUID fakeId = UUID.randomUUID();
+        checkAccountsEqual(new AccountModelDao(fakeId, retrievedAccount), new AccountModelDao(fakeId, account));
+    }
+
+    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 AccountData accountData = createAccountData(billCycleDayUTC, billCycleDayLocal, phone);
+        return new AccountModelDao(UUID.randomUUID(), accountData);
+    }
+
+    protected AccountData createAccountData() {
+        return createAccountData(30, 31, UUID.randomUUID().toString().substring(0, 4));
+    }
+
+    private AccountData createAccountData(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);
+
+        return new DefaultMutableAccountData(externalKey, email, name, firstNameLength, currency,
+                                             billCycleDay, paymentMethodId, timeZone,
+                                             locale, address1, address2, companyName, city, stateOrProvince,
+                                             country, postalCode, phone, false, true);
+    }
+}
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..159b2de 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
@@ -16,119 +16,117 @@
 
 package com.ning.billing.account.api.user;
 
+import java.util.List;
 import java.util.UUID;
 
-import org.joda.time.DateTimeZone;
-import org.mockito.Mockito;
+import org.joda.time.DateTime;
 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.AccountTestBase;
+import com.ning.billing.account.api.Account;
 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.account.api.DefaultMutableAccountData;
+import com.ning.billing.account.api.MigrationAccountData;
+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 testMigrate() throws Exception {
+        final MigrationAccountData accountData = new TestMigrationAccountData(createAccountData());
+        final Account account = accountUserApi.migrateAccount(accountData, callContext);
+        checkAccountsEqual(account.toMutableAccountData(), accountData);
+
+        // Make sure we can retrieve the migrated account
+        final Account retrievedAccount = accountUserApi.getAccountById(account.getId(), callContext);
+        checkAccountsEqual(retrievedAccount, account);
+    }
+
+    @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, 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 = "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 = "slow", expectedExceptions = IllegalArgumentException.class)
+    public void testShouldntBeAbleToUpdateCurrency() throws Exception {
+        final Account account = accountUserApi.createAccount(new DefaultAccount(createTestAccount()), callContext);
+
+        final MutableAccountData otherAccount = new DefaultAccount(account.getId(), account).toMutableAccountData();
+        otherAccount.setCurrency(Currency.GBP);
+
+        accountUserApi.updateAccount(new DefaultAccount(account.getId(), otherAccount), callContext);
     }
 
-    @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 testShouldntBeAbleToUpdateExternalKey() throws Exception {
+        final Account account = accountUserApi.createAccount(new DefaultAccount(createTestAccount()), callContext);
+
+        final MutableAccountData otherAccount = new DefaultAccount(account.getId(), account).toMutableAccountData();
+        otherAccount.setExternalKey(UUID.randomUUID().toString());
+
+        accountUserApi.updateAccount(new DefaultAccount(account.getId(), otherAccount), callContext);
     }
 
-    @Test(groups = "fast")
-    public void testAddEmail() throws Exception {
-        final UUID accountId = UUID.randomUUID();
+    private class TestMigrationAccountData extends DefaultMutableAccountData implements MigrationAccountData {
 
-        // Verify the initial state
-        Assert.assertEquals(accountDao.getEmailsByAccountId(accountId, tenantContext).size(), 0);
+        public TestMigrationAccountData(final AccountData accountData) {
+            super(accountData);
+        }
 
-        // 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);
+        @Override
+        public DateTime getCreatedDate() {
+            return null;
+        }
 
-        // 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);
+        @Override
+        public DateTime getUpdatedDate() {
+            return null;
+        }
 
-        // Remove the first second one
-        accountUserApi.removeEmail(accountId, email1, callContext);
-        Assert.assertEquals(accountDao.getEmailsByAccountId(accountId, tenantContext).size(), 1);
+        @Override
+        public List<String> getAdditionalContactEmails() {
+            return null;
+        }
     }
 }
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/beatrix/src/test/java/com/ning/billing/beatrix/integration/BeatrixIntegrationModule.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/BeatrixIntegrationModule.java
index f62cc13..87cd5da 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/BeatrixIntegrationModule.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/BeatrixIntegrationModule.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2010-2011 Ning, Inc.
+ * 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
@@ -36,6 +36,7 @@ import com.ning.billing.beatrix.extbus.PersistentExternalBus;
 import com.ning.billing.beatrix.integration.overdue.IntegrationTestOverdueModule;
 import com.ning.billing.beatrix.lifecycle.DefaultLifecycle;
 import com.ning.billing.beatrix.lifecycle.Lifecycle;
+import com.ning.billing.beatrix.util.AuditChecker;
 import com.ning.billing.beatrix.util.InvoiceChecker;
 import com.ning.billing.beatrix.util.PaymentChecker;
 import com.ning.billing.catalog.api.CatalogService;
@@ -59,6 +60,7 @@ import com.ning.billing.util.clock.ClockMock;
 import com.ning.billing.util.config.PaymentConfig;
 import com.ning.billing.util.email.EmailModule;
 import com.ning.billing.util.email.templates.TemplateModule;
+import com.ning.billing.util.glue.AuditModule;
 import com.ning.billing.util.glue.BusModule;
 import com.ning.billing.util.glue.CallContextModule;
 import com.ning.billing.util.glue.CustomFieldModule;
@@ -112,9 +114,11 @@ public class BeatrixIntegrationModule extends AbstractModule {
         install(new PaymentPluginMockModule());
         install(new DefaultJunctionModule());
         install(new IntegrationTestOverdueModule());
+        install(new AuditModule());
 
         bind(InvoiceChecker.class).asEagerSingleton();
         bind(PaymentChecker.class).asEagerSingleton();
+        bind(AuditChecker.class).asEagerSingleton();
 
         installPublicBus();
     }
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/util/AuditChecker.java b/beatrix/src/test/java/com/ning/billing/beatrix/util/AuditChecker.java
index 11a6a2d..e674269 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/util/AuditChecker.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/util/AuditChecker.java
@@ -16,21 +16,40 @@
 
 package com.ning.billing.beatrix.util;
 
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
 
+import org.skife.jdbi.v2.Handle;
+import org.skife.jdbi.v2.IDBI;
+import org.skife.jdbi.v2.tweak.HandleCallback;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
 
+import com.ning.billing.dbi.DbiConfig;
 import com.ning.billing.entitlement.api.timeline.EntitlementRepairException;
+import com.ning.billing.entitlement.engine.dao.BundleSqlDao;
+import com.ning.billing.entitlement.engine.dao.EntitlementEventSqlDao;
+import com.ning.billing.entitlement.engine.dao.SubscriptionSqlDao;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.dao.InvoiceItemSqlDao;
+import com.ning.billing.invoice.dao.InvoiceSqlDao;
 import com.ning.billing.util.api.AuditLevel;
 import com.ning.billing.util.api.AuditUserApi;
 import com.ning.billing.util.audit.AuditLog;
 import com.ning.billing.util.audit.AuditLogsForBundles;
+import com.ning.billing.util.audit.AuditLogsForInvoices;
 import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.InternalCallContextFactory;
+import com.ning.billing.util.entity.Entity;
+import com.ning.billing.util.entity.dao.EntityModelDao;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
 
 import com.google.inject.Inject;
 
@@ -39,10 +58,30 @@ public class AuditChecker {
     private static final Logger log = LoggerFactory.getLogger(AuditChecker.class);
 
     private final AuditUserApi auditUserApi;
+    private final IDBI dbi;
+    private final InternalCallContextFactory callContextFactory;
+
 
     @Inject
-    public AuditChecker(final AuditUserApi auditUserApi) {
+    public AuditChecker(final AuditUserApi auditUserApi, final IDBI dbi, final InternalCallContextFactory callContextFactory) {
         this.auditUserApi = auditUserApi;
+        this.dbi = dbi;
+        this.callContextFactory = callContextFactory;
+    }
+
+    public void checkInvoiceCreated(final Invoice invoice, final CallContext context) {
+        AuditLogsForInvoices result = getAuditLogForInvoice(invoice, context);
+        Assert.assertEquals(result.getInvoiceAuditLogs().keySet().size(), 1);
+        final List<AuditLog> invoiceLogs = result.getInvoiceAuditLogs().get(invoice.getId());
+        Assert.assertEquals(invoiceLogs.size(), 1);
+        checkAuditLog(ChangeType.INSERT, context, invoiceLogs.get(0), invoice.getId(), InvoiceSqlDao.class);
+
+        Assert.assertEquals(result.getInvoiceItemsAuditLogs().keySet().size(), invoice.getInvoiceItems().size());
+        for (InvoiceItem cur : invoice.getInvoiceItems()) {
+            final List<AuditLog> auditLogs = result.getInvoiceItemsAuditLogs().get(cur.getId());
+            Assert.assertEquals(auditLogs.size(), 1);
+            checkAuditLog(ChangeType.INSERT, context, auditLogs.get(0), cur.getId(), InvoiceItemSqlDao.class);
+        }
     }
 
     // Pass the call context used to create the bundle
@@ -51,7 +90,7 @@ public class AuditChecker {
 
         Assert.assertEquals(auditLogsForBundles.getBundlesAuditLogs().keySet().size(), 1);
         Assert.assertEquals(auditLogsForBundles.getBundlesAuditLogs().get(bundleId).size(), 1);
-        checkAuditLog(ChangeType.INSERT, context, auditLogsForBundles.getBundlesAuditLogs().get(bundleId).get(0));
+        checkAuditLog(ChangeType.INSERT, context, auditLogsForBundles.getBundlesAuditLogs().get(bundleId).get(0), bundleId, BundleSqlDao.class);
     }
 
     // Pass the call context used to update the bundle
@@ -61,7 +100,7 @@ public class AuditChecker {
         Assert.assertEquals(auditLogsForBundles.getBundlesAuditLogs().keySet().size(), 1);
         Assert.assertEquals(auditLogsForBundles.getBundlesAuditLogs().get(bundleId).size(), 2);
         checkAuditLog(ChangeType.INSERT, auditLogsForBundles.getBundlesAuditLogs().get(bundleId).get(0));
-        checkAuditLog(ChangeType.UPDATE, context, auditLogsForBundles.getBundlesAuditLogs().get(bundleId).get(1));
+        checkAuditLog(ChangeType.UPDATE, context, auditLogsForBundles.getBundlesAuditLogs().get(bundleId).get(1), bundleId, BundleSqlDao.class);
     }
 
     // Pass the call context used to create the subscription
@@ -70,7 +109,7 @@ public class AuditChecker {
 
         Assert.assertEquals(auditLogsForBundles.getSubscriptionsAuditLogs().keySet().size(), 1);
         Assert.assertEquals(auditLogsForBundles.getSubscriptionsAuditLogs().get(subscriptionId).size(), 1);
-        checkAuditLog(ChangeType.INSERT, context, auditLogsForBundles.getSubscriptionsAuditLogs().get(subscriptionId).get(0));
+        checkAuditLog(ChangeType.INSERT, context, auditLogsForBundles.getSubscriptionsAuditLogs().get(subscriptionId).get(0), subscriptionId, SubscriptionSqlDao.class);
     }
 
     // Pass the call context used to update the subscription
@@ -80,7 +119,7 @@ public class AuditChecker {
         Assert.assertEquals(auditLogsForBundles.getSubscriptionsAuditLogs().keySet().size(), 1);
         Assert.assertEquals(auditLogsForBundles.getSubscriptionsAuditLogs().get(subscriptionId).size(), 2);
         checkAuditLog(ChangeType.INSERT, auditLogsForBundles.getSubscriptionsAuditLogs().get(subscriptionId).get(0));
-        checkAuditLog(ChangeType.UPDATE, context, auditLogsForBundles.getSubscriptionsAuditLogs().get(subscriptionId).get(1));
+        checkAuditLog(ChangeType.UPDATE, context, auditLogsForBundles.getSubscriptionsAuditLogs().get(subscriptionId).get(1), subscriptionId, SubscriptionSqlDao.class);
     }
 
     // Pass the call context used to create the subscription event
@@ -89,7 +128,7 @@ public class AuditChecker {
 
         Assert.assertEquals(auditLogsForBundles.getSubscriptionEventsAuditLogs().keySet().size(), 1);
         Assert.assertEquals(auditLogsForBundles.getSubscriptionEventsAuditLogs().get(subscriptionEventId).size(), 1);
-        checkAuditLog(ChangeType.INSERT, context, auditLogsForBundles.getSubscriptionEventsAuditLogs().get(subscriptionEventId).get(0));
+        checkAuditLog(ChangeType.INSERT, context, auditLogsForBundles.getSubscriptionEventsAuditLogs().get(subscriptionEventId).get(0), subscriptionEventId, EntitlementEventSqlDao.class);
     }
 
     // Pass the call context used to update the subscription event
@@ -99,7 +138,7 @@ public class AuditChecker {
         Assert.assertEquals(auditLogsForBundles.getSubscriptionEventsAuditLogs().keySet().size(), 1);
         Assert.assertEquals(auditLogsForBundles.getSubscriptionEventsAuditLogs().get(subscriptionEventId).size(), 2);
         checkAuditLog(ChangeType.INSERT, auditLogsForBundles.getSubscriptionEventsAuditLogs().get(subscriptionEventId).get(0));
-        checkAuditLog(ChangeType.UPDATE, context, auditLogsForBundles.getSubscriptionEventsAuditLogs().get(subscriptionEventId).get(1));
+        checkAuditLog(ChangeType.UPDATE, context, auditLogsForBundles.getSubscriptionEventsAuditLogs().get(subscriptionEventId).get(1), subscriptionEventId, EntitlementEventSqlDao.class);
     }
 
     private AuditLogsForBundles getAuditLogsForBundle(final UUID bundleId, final CallContext context) {
@@ -111,20 +150,44 @@ public class AuditChecker {
         }
     }
 
+    private AuditLogsForInvoices getAuditLogForInvoice(final Invoice invoice, final CallContext context) {
+        return auditUserApi.getAuditLogsForInvoices(Collections.singletonList(invoice), AuditLevel.FULL, context);
+    }
+
     private void checkAuditLog(final ChangeType insert, final AuditLog auditLog) {
-        checkAuditLog(insert, null, auditLog);
+        checkAuditLog(insert, null, auditLog, null, EntitySqlDao.class);
     }
 
-    private void checkAuditLog(final ChangeType changeType, @Nullable final CallContext context, final AuditLog auditLog) {
+
+    private <T extends EntitySqlDao<M, E>, M extends EntityModelDao<E>, E extends Entity> void checkAuditLog(final ChangeType changeType, @Nullable final CallContext context, final AuditLog auditLog, final UUID entityId, Class<T> sqlDao)  {
         Assert.assertEquals(auditLog.getChangeType(), changeType);
 
         if (context != null) {
+            /*
             Assert.assertEquals(auditLog.getUserName(), context.getUserName());
             Assert.assertEquals(auditLog.getReasonCode(), context.getReasonCode());
             // TODO check 'Next Billing Date' and 'Transition' - add comment, maybe internal reason code and token
             Assert.assertEquals(auditLog.getComment(), context.getComments());
             Assert.assertEquals(auditLog.getUserToken(), context.getUserToken().toString());
             Assert.assertEquals(auditLog.getCreatedDate(), context.getCreatedDate());
+            */
+
+            M entityModel = extractEntityModelFromAuditLog(context, auditLog, sqlDao);
+            entityModel.getTableName();
+            Assert.assertEquals(entityModel.getId(), entityId);
         }
     }
+
+    private <T extends EntitySqlDao<M, E>, M extends EntityModelDao<E>, E extends Entity> M extractEntityModelFromAuditLog(final CallContext context, final AuditLog auditLog, final Class<T> sqlDao) {
+        final Integer targetRecordId = dbi.withHandle(new HandleCallback<Integer>() {
+            @Override
+            public Integer withHandle(final Handle handle) throws Exception {
+
+                List<Map<String, Object>> res = handle.select("select target_record_id from audit_log where id = '" + auditLog.getId().toString() + "';");
+                return (Integer)res.get(0).get("target_record_id");
+            }
+        });
+
+        return dbi.onDemand(sqlDao).getByRecordId(Long.valueOf(targetRecordId), callContextFactory.createInternalCallContext(context));
+    }
 }
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/util/InvoiceChecker.java b/beatrix/src/test/java/com/ning/billing/beatrix/util/InvoiceChecker.java
index d211238..ab0d4c7 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/util/InvoiceChecker.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/util/InvoiceChecker.java
@@ -36,7 +36,7 @@ import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.api.InvoiceItemType;
 import com.ning.billing.invoice.api.InvoiceUserApi;
-import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.billing.util.callcontext.CallContext;
 
 import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
@@ -52,36 +52,38 @@ public class InvoiceChecker {
 
     private final InvoiceUserApi invoiceUserApi;
     private final EntitlementUserApi entitlementApi;
+    private final AuditChecker auditChecker;
 
     @Inject
-    public InvoiceChecker(final InvoiceUserApi invoiceUserApi, final EntitlementUserApi entitlementApi) {
+    public InvoiceChecker(final InvoiceUserApi invoiceUserApi, final EntitlementUserApi entitlementApi, final AuditChecker auditChecker) {
         this.invoiceUserApi = invoiceUserApi;
         this.entitlementApi = entitlementApi;
+        this.auditChecker = auditChecker;
     }
 
-    public void checkInvoice(final UUID accountId, final int invoiceOrderingNumber, final TenantContext context, final ExpectedItemCheck... expected) throws InvoiceApiException {
+    public void checkInvoice(final UUID accountId, final int invoiceOrderingNumber, final CallContext context, final ExpectedItemCheck... expected) throws InvoiceApiException {
         checkInvoice(accountId, invoiceOrderingNumber, context, ImmutableList.<ExpectedItemCheck>copyOf(expected));
     }
 
-    public void checkInvoice(final UUID accountId, final int invoiceOrderingNumber, final TenantContext context, final List<ExpectedItemCheck> expected) throws InvoiceApiException {
+    public void checkInvoice(final UUID accountId, final int invoiceOrderingNumber, final CallContext context, final List<ExpectedItemCheck> expected) throws InvoiceApiException {
         final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(accountId, context);
         Assert.assertEquals(invoices.size(), invoiceOrderingNumber);
         final Invoice invoice = invoices.get(invoiceOrderingNumber - 1);
         checkInvoice(invoice.getId(), context, expected);
     }
 
-    public void checkRepairedInvoice(final UUID accountId, final int invoiceNb, final TenantContext context, final ExpectedItemCheck... expected) throws InvoiceApiException {
+    public void checkRepairedInvoice(final UUID accountId, final int invoiceNb, final CallContext context, final ExpectedItemCheck... expected) throws InvoiceApiException {
         checkRepairedInvoice(accountId, invoiceNb, context, ImmutableList.<ExpectedItemCheck>copyOf(expected));
     }
 
-    public void checkRepairedInvoice(final UUID accountId, final int invoiceNb, final TenantContext context, final List<ExpectedItemCheck> expected) throws InvoiceApiException {
+    public void checkRepairedInvoice(final UUID accountId, final int invoiceNb, final CallContext context, final List<ExpectedItemCheck> expected) throws InvoiceApiException {
         final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(accountId, context);
         Assert.assertTrue(invoices.size() > invoiceNb);
         final Invoice invoice = invoices.get(invoiceNb - 1);
         checkInvoice(invoice.getId(), context, expected);
     }
 
-    public void checkInvoice(final UUID invoiceId, final TenantContext context, final List<ExpectedItemCheck> expected) throws InvoiceApiException {
+    public void checkInvoice(final UUID invoiceId, final CallContext context, final List<ExpectedItemCheck> expected) throws InvoiceApiException {
         final Invoice invoice = invoiceUserApi.getInvoice(invoiceId, context);
         Assert.assertNotNull(invoice);
 
@@ -113,16 +115,14 @@ public class InvoiceChecker {
                                           cur.getType(), cur.getStartDate(), cur.getAmount(), cur.getEndDate(), invoice.getId()));
             }
         }
+        auditChecker.checkInvoiceCreated(invoice, context);
     }
 
-    public void checkNullChargedThroughDate(final UUID subscriptionId, final TenantContext context) {
+    public void checkNullChargedThroughDate(final UUID subscriptionId, final CallContext context) {
         checkChargedThroughDate(subscriptionId, null, context);
     }
-// Checking CTD for subscription 2a8f1ca9-e463-4efb-bb4d-5314ec1a8e72 : expectedLocalCTD = 2012-05-01 => expectedCTD = 2012-05-01T00:03:42.000Z,
-//
-// got 2012-05-01T07:03:42.000Z expected:<true> but was:<false>
 
-    public void checkChargedThroughDate(final UUID subscriptionId, final LocalDate expectedLocalCTD, final TenantContext context) {
+    public void checkChargedThroughDate(final UUID subscriptionId, final LocalDate expectedLocalCTD, final CallContext context) {
         try {
             final Subscription subscription = entitlementApi.getSubscriptionFromId(subscriptionId, context);
             if (expectedLocalCTD == null) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/model/EntitlementEventModelDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/model/EntitlementEventModelDao.java
index 203bcf6..42f2c45 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/model/EntitlementEventModelDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/model/EntitlementEventModelDao.java
@@ -55,13 +55,14 @@ public class EntitlementEventModelDao extends EntityBase implements EntityModelD
     private String priceListName;
     private long currentVersion;
     private boolean isActive;
+    private UUID userToken;
 
     public EntitlementEventModelDao() { /* For the DAO mapper */ }
 
     public EntitlementEventModelDao(final UUID id, final long totalOrdering, final EventType eventType, final ApiEventType userType,
                                     final DateTime requestedDate, final DateTime effectiveDate, final UUID subscriptionId,
                                     final String planName, final String phaseName, final String priceListName, final long currentVersion,
-                                    final boolean active, final DateTime createDate, final DateTime updateDate) {
+                                    final boolean active, final DateTime createDate, final DateTime updateDate, final UUID userToken) {
         super(id, createDate, updateDate);
         this.totalOrdering = totalOrdering;
         this.eventType = eventType;
@@ -74,6 +75,7 @@ public class EntitlementEventModelDao extends EntityBase implements EntityModelD
         this.priceListName = priceListName;
         this.currentVersion = currentVersion;
         this.isActive = active;
+        this.userToken = userToken;
     }
 
     public EntitlementEventModelDao(final EntitlementEvent src) {
@@ -95,6 +97,11 @@ public class EntitlementEventModelDao extends EntityBase implements EntityModelD
         return totalOrdering;
     }
 
+    public UUID getUserToken() {
+        return userToken;
+    }
+
+
     public EventType getEventType() {
         return eventType;
     }
@@ -165,7 +172,8 @@ public class EntitlementEventModelDao extends EntityBase implements EntityModelD
                     .setEventPlanPhase(src.getPhaseName())
                     .setEventPriceList(src.getPriceListName())
                     .setEventType(src.getUserType())
-                    .setFromDisk(true);
+                    .setFromDisk(true)
+                    .setUserToken(src.getUserToken());
 
             if (src.getUserType() == ApiEventType.CREATE) {
                 result = new ApiEventCreate(builder);
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemFactory.java b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemFactory.java
index 51bf702..a8b3708 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemFactory.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemFactory.java
@@ -32,6 +32,10 @@ public class InvoiceItemFactory {
     private InvoiceItemFactory() {}
 
     public static InvoiceItem fromModelDao(final InvoiceItemModelDao invoiceItemModelDao) {
+        if (invoiceItemModelDao == null) {
+            return null;
+        }
+
         final UUID id = invoiceItemModelDao.getId();
         final DateTime createdDate = invoiceItemModelDao.getCreatedDate();
         final UUID invoiceId = invoiceItemModelDao.getInvoiceId();
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountEmailJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountEmailJson.java
index 3efaf7f..26311b8 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountEmailJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountEmailJson.java
@@ -44,8 +44,7 @@ public class AccountEmailJson {
         return email;
     }
 
-    public AccountEmail toAccountEmail() {
-        final UUID accountEmailId = UUID.randomUUID();
+    public AccountEmail toAccountEmail(final UUID accountEmailId) {
 
         return new AccountEmail() {
             @Override
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
index a3b5ff1..0162c8f 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
@@ -569,7 +569,7 @@ public class AccountResource extends JaxRsResourceBase {
         // Make sure the account exist or we will confuse the history and auditing code
         accountApi.getAccountById(accountId, callContext);
 
-        accountApi.addEmail(accountId, json.toAccountEmail(), callContext);
+        accountApi.addEmail(accountId, json.toAccountEmail(UUID.randomUUID()), callContext);
 
         return uriBuilder.buildResponse(AccountResource.class, "getEmails", json.getAccountId());
     }
@@ -584,10 +584,15 @@ public class AccountResource extends JaxRsResourceBase {
                                 @HeaderParam(HDR_COMMENT) final String comment,
                                 @javax.ws.rs.core.Context final HttpServletRequest request) {
         final UUID accountId = UUID.fromString(id);
-        final AccountEmailJson accountEmailJson = new AccountEmailJson(id, email);
-        final AccountEmail accountEmail = accountEmailJson.toAccountEmail();
-        accountApi.removeEmail(accountId, accountEmail, context.createContext(createdBy, reason, comment, request));
 
+        final List<AccountEmail> emails = accountApi.getEmails(accountId, context.createContext(request));
+        for (AccountEmail cur : emails) {
+            if (cur.getEmail().equals(email)) {
+                final AccountEmailJson accountEmailJson = new AccountEmailJson(accountId.toString(), email);
+                final AccountEmail accountEmail = accountEmailJson.toAccountEmail(cur.getId());
+                accountApi.removeEmail(accountId, accountEmail, context.createContext(createdBy, reason, comment, request));
+            }
+        }
         return Response.status(Status.OK).build();
     }
 
diff --git a/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestAccountEmailJson.java b/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestAccountEmailJson.java
index 08a56fd..9c0e859 100644
--- a/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestAccountEmailJson.java
+++ b/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestAccountEmailJson.java
@@ -54,7 +54,7 @@ public class TestAccountEmailJson extends JaxrsTestSuite {
         Assert.assertEquals(accountEmailJson.getAccountId(), accountId);
         Assert.assertEquals(accountEmailJson.getEmail(), email);
 
-        final AccountEmail accountEmail = accountEmailJson.toAccountEmail();
+        final AccountEmail accountEmail = accountEmailJson.toAccountEmail(UUID.randomUUID());
         Assert.assertEquals(accountEmail.getAccountId().toString(), accountId);
         Assert.assertEquals(accountEmail.getEmail(), email);
     }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
index d3ccd89..381fd3b 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
@@ -251,7 +251,7 @@ public class DefaultPaymentDao implements PaymentDao {
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<PaymentMethodModelDao>>() {
             @Override
             public List<PaymentMethodModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-                return entitySqlDaoWrapperFactory.become(PaymentMethodSqlDao.class).get(context);
+                return entitySqlDaoWrapperFactory.become(PaymentMethodSqlDao.class).getByAccountId(accountId.toString(), context);
             }
         });
     }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodSqlDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodSqlDao.java
index 25e32f2..2c04db0 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodSqlDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodSqlDao.java
@@ -49,5 +49,5 @@ public interface PaymentMethodSqlDao extends EntitySqlDao<PaymentMethodModelDao,
                                                          @BindBean final InternalTenantContext context);
 
     @SqlQuery
-    List<PaymentMethodModelDao> getByAccountId(@Bind("accountId") final String accountId, @BindBean final InternalCallContext context);
+    List<PaymentMethodModelDao> getByAccountId(@Bind("accountId") final String accountId, @BindBean final InternalTenantContext context);
 }
diff --git a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentMethodSqlDao.sql.stg b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentMethodSqlDao.sql.stg
index e6d5794..51ef088 100644
--- a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentMethodSqlDao.sql.stg
+++ b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentMethodSqlDao.sql.stg
@@ -66,5 +66,6 @@ select
 <allTableFields()>
 from <tableName()>
 where account_id = :accountId
+and is_active = 1
 ;
 >>
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 2365c5e..188644c 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;
 
@@ -54,7 +53,7 @@ import com.ning.billing.util.glue.CustomFieldModule;
 import com.ning.billing.util.glue.ExportModule;
 import com.ning.billing.util.glue.GlobalLockerModule;
 import com.ning.billing.util.glue.NotificationQueueModule;
-import com.ning.jetty.jdbi.guice.providers.DBIProvider;
+import com.ning.billing.util.glue.TagStoreModule;
 
 import com.google.inject.AbstractModule;
 
@@ -68,13 +67,11 @@ 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 {
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestAccountEmail.java b/server/src/test/java/com/ning/billing/jaxrs/TestAccountEmail.java
index 581053a..cf28890 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestAccountEmail.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestAccountEmail.java
@@ -70,8 +70,8 @@ public class TestAccountEmail extends TestJaxrsBase {
         Assert.assertEquals(fourthEmails.get(0).getAccountId(), accountId);
         Assert.assertEquals(fourthEmails.get(0).getEmail(), email2);
 
-        // Try to add the same email
-        addEmailToAccount(accountId, accountEmailJson2);
+        // Try to add the same email -- that works because we removed the unique constraints for soft deletion.
+        // addEmailToAccount(accountId, accountEmailJson2);
         Assert.assertEquals(getEmailsForAccount(accountId), fourthEmails);
     }
 }
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestAccountTimeline.java b/server/src/test/java/com/ning/billing/jaxrs/TestAccountTimeline.java
index eba983c..7d369b8 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestAccountTimeline.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestAccountTimeline.java
@@ -40,7 +40,7 @@ import com.ning.billing.util.audit.ChangeType;
 public class TestAccountTimeline extends TestJaxrsBase {
 
     private static final String PAYMENT_REQUEST_PROCESSOR = "PaymentRequestProcessor";
-    private static final String TRANSITION = "Transition";
+    private static final String TRANSITION = "SubscriptionTransition";
 
     @Test(groups = "slow")
     public void testAccountTimeline() throws Exception {
diff --git a/tenant/src/main/java/com/ning/billing/tenant/dao/DefaultTenantDao.java b/tenant/src/main/java/com/ning/billing/tenant/dao/DefaultTenantDao.java
index a7084a5..959d8bd 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/dao/DefaultTenantDao.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/dao/DefaultTenantDao.java
@@ -114,7 +114,7 @@ public class DefaultTenantDao extends EntityDaoBase<TenantModelDao, Tenant, Tena
                 return ImmutableList.copyOf(Collections2.transform(tenantKV, new Function<TenantKVModelDao, String>() {
                     @Override
                     public String apply(final TenantKVModelDao in) {
-                        return in.getValue();
+                        return in.getTenantValue();
                     }
                 }));
             }
@@ -138,7 +138,12 @@ public class DefaultTenantDao extends EntityDaoBase<TenantModelDao, Tenant, Tena
         transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
             public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-                entitySqlDaoWrapperFactory.become(TenantKVSqlDao.class).markTenantKeyAsDeleted(key, context);
+                final List<TenantKVModelDao> tenantKVs = entitySqlDaoWrapperFactory.become(TenantKVSqlDao.class).getTenantValueForKey(key, context);
+                for (TenantKVModelDao cur : tenantKVs) {
+                    if (cur.getTenantKey().equals(key)) {
+                        entitySqlDaoWrapperFactory.become(TenantKVSqlDao.class).markTenantKeyAsDeleted(cur.getId().toString(), context);
+                    }
+                }
                 return null;
             }
         });
diff --git a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantKVModelDao.java b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantKVModelDao.java
index 516ce59..362bbbe 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantKVModelDao.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantKVModelDao.java
@@ -27,25 +27,26 @@ import com.ning.billing.util.entity.dao.EntityModelDao;
 
 public class TenantKVModelDao extends EntityBase implements EntityModelDao<TenantKV> {
 
-    private String key;
-    private String value;
+    private String tenantKey;
+    private String tenantValue;
+
     private Boolean isActive;
 
     public TenantKVModelDao() { /* For the DAO mapper */ }
 
     public TenantKVModelDao(final UUID id, final DateTime createdDate, final DateTime updatedDate, final String key, final String value) {
         super(id, createdDate, updatedDate);
-        this.key = key;
-        this.value = value;
+        this.tenantKey = key;
+        this.tenantValue = value;
         this.isActive = true;
     }
 
-    public String getKey() {
-        return key;
+    public String getTenantKey() {
+        return tenantKey;
     }
 
-    public String getValue() {
-        return value;
+    public String getTenantValue() {
+        return tenantValue;
     }
 
     public Boolean getIsActive() {
@@ -56,8 +57,8 @@ public class TenantKVModelDao extends EntityBase implements EntityModelDao<Tenan
     public String toString() {
         final StringBuilder sb = new StringBuilder();
         sb.append("TenantKVModelDao");
-        sb.append("{key='").append(key).append('\'');
-        sb.append(", value='").append(value).append('\'');
+        sb.append("{key='").append(tenantKey).append('\'');
+        sb.append(", value='").append(tenantValue).append('\'');
         sb.append(", isActive=").append(isActive);
         sb.append('}');
         return sb.toString();
@@ -80,10 +81,10 @@ public class TenantKVModelDao extends EntityBase implements EntityModelDao<Tenan
         if (isActive != null ? !isActive.equals(that.isActive) : that.isActive != null) {
             return false;
         }
-        if (key != null ? !key.equals(that.key) : that.key != null) {
+        if (tenantKey != null ? !tenantKey.equals(that.tenantKey) : that.tenantKey != null) {
             return false;
         }
-        if (value != null ? !value.equals(that.value) : that.value != null) {
+        if (tenantValue != null ? !tenantValue.equals(that.tenantValue) : that.tenantValue != null) {
             return false;
         }
 
@@ -93,8 +94,8 @@ public class TenantKVModelDao extends EntityBase implements EntityModelDao<Tenan
     @Override
     public int hashCode() {
         int result = super.hashCode();
-        result = 31 * result + (key != null ? key.hashCode() : 0);
-        result = 31 * result + (value != null ? value.hashCode() : 0);
+        result = 31 * result + (tenantKey != null ? tenantKey.hashCode() : 0);
+        result = 31 * result + (tenantValue != null ? tenantValue.hashCode() : 0);
         result = 31 * result + (isActive != null ? isActive.hashCode() : 0);
         return result;
     }
diff --git a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantKVSqlDao.java b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantKVSqlDao.java
index f75be0f..0ff9119 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantKVSqlDao.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantKVSqlDao.java
@@ -35,11 +35,11 @@ import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 public interface TenantKVSqlDao extends EntitySqlDao<TenantKVModelDao, TenantKV> {
 
     @SqlQuery
-    public List<TenantKVModelDao> getTenantValueForKey(@Bind("key") final String key,
+    public List<TenantKVModelDao> getTenantValueForKey(@Bind("tenantKey") final String key,
                                                        @BindBean final InternalTenantContext context);
 
     @SqlUpdate
     @Audited(ChangeType.DELETE)
-    public void markTenantKeyAsDeleted(@Bind("key") final String key,
+    public void markTenantKeyAsDeleted(@Bind("id")final String id,
                                        @BindBean final InternalCallContext context);
 }
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 20edb99..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
@@ -3,8 +3,8 @@ group TenantKVSqlDao: EntitySqlDao;
 tableName() ::= "tenant_kvs"
 
 tableFields(prefix) ::= <<
-  <prefix>t_key
-, <prefix>t_value
+  <prefix>tenant_key
+, <prefix>tenant_value
 , <prefix>is_active
 , <prefix>created_date
 , <prefix>created_by
@@ -13,8 +13,8 @@ tableFields(prefix) ::= <<
 >>
 
 tableValues() ::= <<
-  :key
-, :value
+  :tenantKey
+, :tenantValue
 , :isActive
 , :createdDate
 , :createdBy
@@ -22,7 +22,7 @@ tableValues() ::= <<
 , :updatedBy
 >>
 
-accountRecordIdFieldWithComma() ::= ""
+accountRecordIdFieldWithComma(prefix) ::= ""
 
 accountRecordIdValueWithComma() ::= ""
 
@@ -31,7 +31,8 @@ getTenantValueForKey() ::= <<
 select
   <allTableFields("t.")>
 from <tableName()> t
-where t.t_key = :key
+where t.tenant_key = :tenantKey
+and  t.is_active = 1
 <AND_CHECK_TENANT("t.")>
 ;
 >>
@@ -39,7 +40,7 @@ where t.t_key = :key
 markTenantKeyAsDeleted() ::= <<
 update <tableName()> t
 set t.is_active = 0
-where t.t_key = :key
+where t.id = :id
 <AND_CHECK_TENANT("t.")>
 ;
 >>
diff --git a/tenant/src/main/resources/com/ning/billing/tenant/ddl.sql b/tenant/src/main/resources/com/ning/billing/tenant/ddl.sql
index 795fb00..664a2e0 100644
--- a/tenant/src/main/resources/com/ning/billing/tenant/ddl.sql
+++ b/tenant/src/main/resources/com/ning/billing/tenant/ddl.sql
@@ -21,13 +21,13 @@ CREATE TABLE tenant_kvs (
    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
    id char(36) NOT NULL,
    tenant_record_id int(11) unsigned default null,
-   t_key varchar(64) NOT NULL,
-   t_value varchar(1024) NOT NULL,
-   is_active bool default true,
+   tenant_key varchar(64) NOT NULL,
+   tenant_value varchar(1024) NOT NULL,
+   is_active bool DEFAULT 1,
    created_date datetime NOT NULL,
    created_by varchar(50) NOT NULL,
    updated_date datetime DEFAULT NULL,
    updated_by varchar(50) DEFAULT NULL,
    PRIMARY KEY(record_id)
 ) ENGINE=innodb;
-CREATE INDEX tenant_kvs_key ON tenant_kvs(tenant_record_id, t_key);
+CREATE INDEX tenant_kvs_key ON tenant_kvs(tenant_key);
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..1a23c7e 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() ::= ""
 
@@ -65,3 +65,14 @@ and <idField("t.")> in (<tag_definition_ids: {id | :id_<i0>}; separator="," >)
 <AND_CHECK_TENANT("t.")>
 ;
 >>
+
+get(limit) ::= <<
+select
+<allTableFields("t.")>
+from <tableName()> t
+where
+t.is_active
+<AND_CHECK_TENANT("t.")>
+<if(limit)>limit :limit<endif>
+;
+>>
diff --git a/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java b/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java
index cfa14f4..309e20f 100644
--- a/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java
+++ b/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java
@@ -27,7 +27,10 @@ import com.ning.billing.ObjectType;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.tag.Tag;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
 
 public class MockTagDao implements TagDao {
 
@@ -63,6 +66,15 @@ public class MockTagDao implements TagDao {
 
     @Override
     public List<TagModelDao> getTags(final UUID objectId, final ObjectType objectType, final InternalTenantContext internalTenantContext) {
-        throw new UnsupportedOperationException();
+        if (tagStore.get(objectId) == null) {
+            return ImmutableList.<TagModelDao>of();
+        }
+
+        return ImmutableList.<TagModelDao>copyOf(Collections2.filter(tagStore.get(objectId), new Predicate<TagModelDao>() {
+            @Override
+            public boolean apply(final TagModelDao input) {
+                return objectType.equals(input.getObjectType());
+            }
+        }));
     }
 }
diff --git a/util/src/test/java/com/ning/billing/util/tag/dao/TestDefaultTagDefinitionDao.java b/util/src/test/java/com/ning/billing/util/tag/dao/TestDefaultTagDefinitionDao.java
index d124b9f..b53ff95 100644
--- a/util/src/test/java/com/ning/billing/util/tag/dao/TestDefaultTagDefinitionDao.java
+++ b/util/src/test/java/com/ning/billing/util/tag/dao/TestDefaultTagDefinitionDao.java
@@ -35,7 +35,6 @@ import com.ning.billing.util.bus.InMemoryBusModule;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.events.BusInternalEvent;
 import com.ning.billing.util.events.TagDefinitionInternalEvent;
-import com.ning.billing.util.glue.BusModule;
 import com.ning.billing.util.glue.ClockModule;
 import com.ning.billing.util.glue.TagStoreModule;
 import com.ning.billing.util.svcsapi.bus.InternalBus;