diff --git a/apps/alias-server/src/alias.js b/apps/alias-server/src/alias.js
--- a/apps/alias-server/src/alias.js
+++ b/apps/alias-server/src/alias.js
@@ -11,6 +11,7 @@
     isValidAliasString,
     getOutputScriptFromAddress,
 } = require('./utils');
+const { addOneAliasToDb } = require('./db');
 
 module.exports = {
     getAliasTxs: function (aliasTxHistory, aliasConstants) {
@@ -145,16 +146,20 @@
 
         return aliasTxsSortedByTxidAndBlockheight;
     },
-    getValidAliasRegistrations: function (registeredAliases, unsortedAliasTxs) {
-        /* Function that takes an array of already-registered aliases (strings)
-         * and an arbitrary collection of alias-prefixed txs from the alias registration
-         * address.
+    getValidAliasRegistrations: async function (db, unsortedAliasTxs) {
+        /* Add new valid aliases registration txs to the database. Return an array of what was added.
          *
-         * Outputs new valid alias registrations by discarding any repeated registrations
-         * as invalid.
+         * Input parameters
+         * db - the app database
+         * unsortedAliasTxs - array, arbitrary collection of alias-prefixed txs at the alias
+         *                    registration address
          *
-         * Will get all valid alias registrations if given the full tx history and an empty array
-         * for registeredAliases
+         * Outputs
+         * - The function adds new valid alias txs to the database
+         * - An array of objects, each one a new valid alias registration tx that was added to the database
+         *         *
+         * Will get all valid alias registrations if given the full tx history and
+         * the database is empty
          */
 
         // Sort aliases such that the earliest aliases are the valid ones
@@ -168,37 +173,39 @@
         // (and alphabetically first txids to last)
         for (let i = 0; i < aliasesSortedByTxidAndBlockheight.length; i += 1) {
             const thisAliasTx = aliasesSortedByTxidAndBlockheight[i];
-            const { alias, blockheight } = thisAliasTx;
-
-            // If you haven't seen this alias yet, it's a valid registered alias
-            if (!registeredAliases.includes(alias)) {
-                // If the tx is confirmed, add this alias to the registeredAlias array
-                registeredAliases.push(alias);
-                // If the tx is confirmed,
-                if (blockheight < 100000000) {
-                    // Add thisAliasObject to the validAliasObjects array
-                    validAliasRegistrations.push(thisAliasTx);
+            const { blockheight } = thisAliasTx;
+
+            /* If
+             * - You haven't seen this alias yet while going through aliasesSortedByTxidAndBlockheight
+             * - the alias does not exist in the database
+             * - the transaction is confirmed
+             *
+             * Then it is a valid alias
+             *
+             * The first two conditions are checked by trying to add this alias to the db
+             * If it's already there, this will fail, as the database is unique keyed to
+             * the 'alias' parameter
+             */
+            if (blockheight < config.unconfirmedBlockheight) {
+                // Attempt to add to the database
+                const aliasAdded = await addOneAliasToDb(db, thisAliasTx);
+                console.log(`aliasAdded ${thisAliasTx.alias}: ${aliasAdded}`);
+
+                // If database add is successful,
+                // add thisAliasObject to the validAliasObjects array
+                if (aliasAdded) {
+                    // Because thisAliasTx receives an "_id" key on being added to the db,
+                    // clone it without this field to return
+                    const { address, alias, blockheight, txid } = thisAliasTx;
+                    validAliasRegistrations.push({
+                        address,
+                        alias,
+                        blockheight,
+                        txid,
+                    });
                 }
-            } else {
-                // If you've already seen it at an earlier blockheight or earlier alphabetical txid,
-                // then this is not a valid registration.
-                // Do not include it in valid registrations
-
-                // Note, we could just remove this else block. But it's useful for code readability.
-                continue;
             }
         }
         return validAliasRegistrations;
     },
-    getAliasStringsFromValidAliasTxs: function (validAliases) {
-        /* Input
-         * validAliases, an array of alias registrations objects like those
-         * stored in the validAliases collection of the database
-         * Output
-         * an array of strings of all alias registrations
-         */
-        return validAliases.map(aliasObj => {
-            return aliasObj.alias;
-        });
-    },
 };
diff --git a/apps/alias-server/src/db.js b/apps/alias-server/src/db.js
--- a/apps/alias-server/src/db.js
+++ b/apps/alias-server/src/db.js
@@ -105,6 +105,21 @@
             return false;
         }
     },
+    addOneAliasToDb: async function (db, newAliasTx) {
+        try {
+            await db
+                .collection(config.database.collections.validAliases)
+                .insertOne(newAliasTx);
+            return true;
+        } catch (err) {
+            // Only log some error other than duplicate key error
+            if (err && err.code !== 11000) {
+                log(`Error in function addOneAliasToDb.`);
+                log(err);
+            }
+            return false;
+        }
+    },
     addAliasesToDb: async function (db, newValidAliases) {
         let validAliasesAddedToDbSuccess;
         try {
@@ -138,4 +153,37 @@
             return false;
         }
     },
+    checkAliasAlreadyRegistered: async function (db, unprocessedAlias) {
+        // Check the database to see if this alias exists
+        let findThisAliasResult;
+        // Query for alias tx in db that has the alias 'unprocessedAlias'
+        const query = { alias: unprocessedAlias };
+        try {
+            findThisAliasResult = await db
+                .collection(config.database.collections.validAliases)
+                .findOne(query);
+            /* If the alias exists, you will get an object like
+             * {_id, address, alias, txid, blockheight}
+             *
+             * If the alias does not exist, you will get null
+             */
+            return findThisAliasResult !== null;
+        } catch (err) {
+            log(
+                `Error in determining findThisAliasResult in function checkAliasAlreadyRegistered.`,
+            );
+            log(err);
+
+            /* This database lookup function requires special error handling
+             *
+             * If there is an error in looking up any alias, we want to notify the admin
+             * and not include any of the batch of aliases
+             *
+             * We do not want to assume the alias cannot be added
+             * We definitely do not want to return false and assume the alias can be added
+             *
+             */
+            return 'error';
+        }
+    },
 };
diff --git a/apps/alias-server/src/events.js b/apps/alias-server/src/events.js
--- a/apps/alias-server/src/events.js
+++ b/apps/alias-server/src/events.js
@@ -5,8 +5,11 @@
 'use strict';
 const config = require('../config');
 const log = require('./log');
-const { wait } = require('./utils');
+const { wait, removeUnconfirmedTxsFromTxHistory } = require('./utils');
 const { isFinalBlock } = require('./rpc');
+const { getServerState, updateServerState } = require('./db');
+const { getUnprocessedTxHistory } = require('./chronik');
+const { getAliasTxs, getValidAliasRegistrations } = require('./alias');
 
 module.exports = {
     handleAppStartup: async function (
@@ -122,23 +125,72 @@
             return false;
         }
 
-        // TODO Get the valid aliases already in the db
+        const serverState = await getServerState(db);
+        if (!serverState) {
+            // TODO notify admin
+            return false;
+        }
+
+        const { processedBlockheight, processedConfirmedTxs } = serverState;
+
+        // If serverState is, somehow, ahead of the calling block, return false
+        if (processedBlockheight > tipHeight) {
+            // TODO notify admin
+            return false;
+        }
 
-        // TODO get server state
-        // processedConfirmedTxs - count of processed confirmed txs
-        // processedBlockheight - highest blockheight seen by the server
+        const allUnprocessedTxs = await getUnprocessedTxHistory(
+            chronik,
+            config.aliasConstants.registrationAddress,
+            processedBlockheight,
+            processedConfirmedTxs,
+        );
 
-        // TODO get set of transactions not yet processed by the server
-        // If app startup, this is full tx history of alias registration address
+        // Remove unconfirmed txs as these are not eligible for valid alias registrations
+        const confirmedUnprocessedTxs =
+            removeUnconfirmedTxsFromTxHistory(allUnprocessedTxs);
 
-        // TODO parse tx history for latest valid alias registrations
-        // with valid format and fee
+        // Get all potentially valid alias registrations
+        // i.e. correct fee is paid, prefix is good, everything good but not yet checked against
+        // conflicting aliases that registered earlier or have alphabetically earlier txid in
+        // same block
+        const unprocessedAliasTxs = getAliasTxs(
+            confirmedUnprocessedTxs,
+            config.aliasConstants,
+        );
 
-        // TODO update database with latest valid alias information
+        // Add new valid alias txs to the database and get a list of what was added
+        await getValidAliasRegistrations(db, unprocessedAliasTxs);
+
+        // If you processed more confirmed txs for aliases, update serverState appropriately
+        if (confirmedUnprocessedTxs.length > 0) {
+            // New processedBlockheight is the highest one seen, or the
+            // height of the first entry of the confirmedUnprocessedTxs array
+            // New processedConfirmedTxs is determined by adding the count of now-processed txs
+            const newServerState = {
+                processedBlockheight: confirmedUnprocessedTxs[0].block.height,
+                processedConfirmedTxs:
+                    processedConfirmedTxs + confirmedUnprocessedTxs.length,
+            };
+            // Update serverState
+            const serverStateUpdated = await updateServerState(
+                db,
+                newServerState,
+            );
+            if (!serverStateUpdated) {
+                // Don't exit loop, since you've already added aliases to the db here
+                // App will run next on the old server state, so will re-process txs
+                // These can't be added to the db, so you will get errors
+                // If you get here, there is something wrong with the server that needs to be checked out
+                // TODO notify admin
+                log(
+                    `serverState failed to update to new serverState`,
+                    newServerState,
+                );
+            }
+        }
 
-        // TODO update server state
-        // TODO If you have new aliases to add to the db, add them + send a tg msg
-        // TODO If not, exit loop
+        // TODO telegram notifications for new alias registrations
 
         log(
             `Alias registrations updated to block ${tipHash} at height ${tipHeight}`,
diff --git a/apps/alias-server/test/aliasTests.js b/apps/alias-server/test/aliasTests.js
--- a/apps/alias-server/test/aliasTests.js
+++ b/apps/alias-server/test/aliasTests.js
@@ -10,7 +10,6 @@
     getAliasTxs,
     sortAliasTxsByTxidAndBlockheight,
     getValidAliasRegistrations,
-    getAliasStringsFromValidAliasTxs,
 } = require('../src/alias');
 const { getOutputScriptFromAddress } = require('../src/utils');
 const {
@@ -18,8 +17,25 @@
     testAddressAliasesWithUnconfirmedTxs,
     aliases_fake_data,
 } = require('./mocks/aliasMocks');
+// Mock mongodb
+const { MongoClient } = require('mongodb');
+const { MongoMemoryServer } = require('mongodb-memory-server');
+const { initializeDb, addAliasesToDb } = require('../src/db');
 
-describe('alias-server alias.js', function () {
+describe('alias-server alias.js', async function () {
+    let mongoServer, testMongoClient;
+    before(async () => {
+        // Start mongo memory server before running this suite of unit tests
+        mongoServer = await MongoMemoryServer.create();
+        const mongoUri = mongoServer.getUri();
+        testMongoClient = new MongoClient(mongoUri);
+    });
+
+    after(async () => {
+        // Shut down mongo memory server after running this suite of unit tests
+        await testMongoClient.close();
+        await mongoServer.stop();
+    });
     it('Correctly parses a 5-character alias transaction', function () {
         const registrationOutputScript = getOutputScriptFromAddress(
             config.aliasConstants.registrationAddress,
@@ -143,57 +159,88 @@
             testAddressAliasesWithUnconfirmedTxs.allAliasTxsSortedByTxidAndBlockheight,
         );
     });
-    it('Correctly returns only valid alias registrations at test address ecash:qp3c268rd5946l2f5m5es4x25f7ewu4sjvpy52pqa8', function () {
+    it('Correctly returns only valid alias registrations at test address ecash:qp3c268rd5946l2f5m5es4x25f7ewu4sjvpy52pqa8 starting with an empty database', async function () {
+        // Initialize db before each unit test
+        let testDb = await initializeDb(testMongoClient);
+
+        // Clone unprocessedAliasTxs since the act of adding to db gives it an _id field
+        const mockAllAliasTxs = JSON.parse(
+            JSON.stringify(testAddressAliases.allAliasTxs),
+        );
+
         assert.deepEqual(
-            getValidAliasRegistrations([], testAddressAliases.allAliasTxs),
+            await getValidAliasRegistrations(testDb, mockAllAliasTxs),
             testAddressAliases.validAliasTxs,
         );
+
+        // Wipe the database after this unit test
+        await testDb.dropDatabase();
     });
-    it('Correctly returns only new valid alias registrations at test address ecash:qp3c268rd5946l2f5m5es4x25f7ewu4sjvpy52pqa8 given partial txHistory and list of registered aliases', function () {
+    it('Correctly returns only new valid alias registrations at test address ecash:qp3c268rd5946l2f5m5es4x25f7ewu4sjvpy52pqa8 given partial txHistory and list of registered aliases', async function () {
         // Take only txs after registration of alias 'bytesofman'
+        // Note: allAliasTxs are sorted with most recent txs first
         const unprocessedAliasTxs = testAddressAliases.allAliasTxs.slice(
+            0,
             testAddressAliases.allAliasTxs.findIndex(
                 i => i.alias === 'bytesofman',
             ),
         );
+
+        // Clone unprocessedAliasTxs since the act of adding to db gives it an _id field
+        const mockUnprocessedAliasTxs = JSON.parse(
+            JSON.stringify(unprocessedAliasTxs),
+        );
+
         // Get list of all valid alias registrations before 'bytesofman'
-        const registeredAliases = getAliasStringsFromValidAliasTxs(
-            testAddressAliases.validAliasTxs.slice(
-                0,
-                testAddressAliases.validAliasTxs.findIndex(
-                    i => i.alias === 'bytesofman',
-                ),
-            ),
+        // Note: validAliasTxs are sorted with most recent txs last
+        // Note: you want to include bytesofman here
+        const registeredAliases = testAddressAliases.validAliasTxs.slice(
+            0,
+            testAddressAliases.validAliasTxs.findIndex(
+                i => i.alias === 'bytesofman',
+            ) + 1,
         );
 
         // newlyValidAliases will be all the valid alias txs registered after 'bytesofman'
+        // Note: you do not want bytesofman in this set
         const newlyValidAliases = testAddressAliases.validAliasTxs.slice(
             testAddressAliases.validAliasTxs.findIndex(
                 i => i.alias === 'bytesofman',
-            ),
+            ) + 1,
+        );
+        // Initialize db before each unit test
+        let testDb = await initializeDb(testMongoClient);
+
+        // mockRegisteredAliases needs to be a clone of the mock because
+        // each object gets an _id field when added to the database
+        const mockRegisteredAliases = JSON.parse(
+            JSON.stringify(registeredAliases),
         );
+        // Add expected registered aliases to the db
+        await addAliasesToDb(testDb, mockRegisteredAliases);
 
         assert.deepEqual(
-            getValidAliasRegistrations(registeredAliases, unprocessedAliasTxs),
+            await getValidAliasRegistrations(testDb, mockUnprocessedAliasTxs),
             newlyValidAliases,
         );
+        // Wipe the database after this unit test
+        await testDb.dropDatabase();
     });
-    it('Correctly returns valid alias registrations at test address ecash:qp3c268rd5946l2f5m5es4x25f7ewu4sjvpy52pqa8 given some unconfirmed txs in history', function () {
-        assert.deepEqual(
-            getValidAliasRegistrations(
-                [],
-                testAddressAliasesWithUnconfirmedTxs.allAliasTxs,
-            ),
+    it('Correctly returns valid alias registrations at test address ecash:qp3c268rd5946l2f5m5es4x25f7ewu4sjvpy52pqa8 given some unconfirmed txs in history', async function () {
+        // Initialize db before each unit test
+        let testDb = await initializeDb(testMongoClient);
 
-            testAddressAliasesWithUnconfirmedTxs.validAliasTxs,
+        // Clone all alias mock since the act of adding to db gives it an _id field
+        const mockAllAliases = JSON.parse(
+            JSON.stringify(testAddressAliasesWithUnconfirmedTxs.allAliasTxs),
         );
-    });
-    it('getAliasStringsFromValidAliasTxs returns an array of string of the alias object key from an array of valid alias registrations', function () {
+
+        // This tests startup condition, so add no aliases to the database
         assert.deepEqual(
-            getAliasStringsFromValidAliasTxs(
-                testAddressAliasesWithUnconfirmedTxs.validAliasTxs,
-            ),
-            testAddressAliasesWithUnconfirmedTxs.validAliasStrings,
+            await getValidAliasRegistrations(testDb, mockAllAliases),
+            testAddressAliasesWithUnconfirmedTxs.validAliasTxs,
         );
+        // Wipe the database after this unit test
+        await testDb.dropDatabase();
     });
 });
diff --git a/apps/alias-server/test/chronikWsHandlerTests.js b/apps/alias-server/test/chronikWsHandlerTests.js
--- a/apps/alias-server/test/chronikWsHandlerTests.js
+++ b/apps/alias-server/test/chronikWsHandlerTests.js
@@ -4,6 +4,7 @@
 
 'use strict';
 const assert = require('assert');
+const config = require('../config');
 const cashaddr = require('ecashaddrjs');
 const {
     initializeWebsocket,
@@ -14,8 +15,36 @@
 const mockSecrets = require('../secrets.sample');
 const MockAdapter = require('axios-mock-adapter');
 const axios = require('axios');
+// Mock mongodb
+const { initializeDb } = require('../src/db');
+const { MongoClient } = require('mongodb');
+const { MongoMemoryServer } = require('mongodb-memory-server');
+const { testAddressAliases } = require('./mocks/aliasMocks');
 
 describe('alias-server chronikWsHandler.js', async function () {
+    let mongoServer, testMongoClient;
+    before(async () => {
+        // Start mongo memory server before running this suite of unit tests
+        mongoServer = await MongoMemoryServer.create();
+        const mongoUri = mongoServer.getUri();
+        testMongoClient = new MongoClient(mongoUri);
+    });
+
+    after(async () => {
+        // Shut down mongo memory server after running this suite of unit tests
+        await testMongoClient.close();
+        await mongoServer.stop();
+    });
+
+    let testDb;
+    beforeEach(async () => {
+        // Initialize db before each unit test
+        testDb = await initializeDb(testMongoClient);
+    });
+    afterEach(async () => {
+        // Wipe the database after each unit test
+        await testDb.dropDatabase();
+    });
     it('initializeWebsocket returns expected websocket object for a p2pkh address', async function () {
         const wsTestAddress =
             'ecash:qp3c268rd5946l2f5m5es4x25f7ewu4sjvpy52pqa8';
@@ -77,7 +106,7 @@
     it('parseWebsocketMessage correctly processes a chronik websocket BlockConnected message if block is avalanche finalized', async function () {
         // Initialize chronik mock
         const mockedChronik = new MockChronikClient();
-        const db = null;
+        const db = testDb;
         const telegramBot = null;
         const channelId = null;
         const { avalancheRpc } = mockSecrets;
@@ -97,6 +126,16 @@
             output: mockBlock,
         });
 
+        // Add tx history to mockedChronik
+        // Set the script
+        const { type, hash } = cashaddr.decode(
+            config.aliasConstants.registrationAddress,
+            true,
+        );
+        mockedChronik.setScript(type, hash);
+        // Set the mock tx history
+        mockedChronik.setTxHistory(testAddressAliases.txHistory);
+
         // Mock avalanche RPC call
         // onNoMatch: 'throwException' helps to debug if mock is not being used
         const mock = new MockAdapter(axios, { onNoMatch: 'throwException' });
@@ -162,7 +201,7 @@
     it('If parseWebsocketMessage is called before a previous call to handleBlockConnected has completed, the next call to handleBlockConnected will not enter until the first is completed', async function () {
         // Initialize mocks for the first call to parseWebsocketMessage
         const mockedChronik = new MockChronikClient();
-        const db = null;
+        const db = testDb;
         const telegramBot = null;
         const channelId = null;
         const { avalancheRpc } = mockSecrets;
@@ -183,6 +222,16 @@
             output: mockBlock,
         });
 
+        // Add tx history to mockedChronik
+        // Set the script
+        const { type, hash } = cashaddr.decode(
+            config.aliasConstants.registrationAddress,
+            true,
+        );
+        mockedChronik.setScript(type, hash);
+        // Set the mock tx history
+        mockedChronik.setTxHistory(testAddressAliases.txHistory);
+
         // Mock avalanche RPC call
         // onNoMatch: 'throwException' helps to debug if mock is not being used
         const mock = new MockAdapter(axios, { onNoMatch: 'throwException' });
@@ -205,11 +254,20 @@
                 height: 786879,
             },
         };
+
         // Tell mockedChronik what response we expect
         nextMockedChronik.setMock('block', {
             input: nextWsMsg.blockHash,
             output: nextMockBlock,
         });
+
+        // Add tx history to nextMockedChronik
+        // Set the script
+        nextMockedChronik.setScript(type, hash);
+        // Set the mock tx history
+        // For now, assume it's the same as before, i.e. no new txs found
+        nextMockedChronik.setTxHistory(testAddressAliases.txHistory);
+
         const firstCallPromise = parseWebsocketMessage(
             mockedChronik,
             db,
diff --git a/apps/alias-server/test/dbTests.js b/apps/alias-server/test/dbTests.js
--- a/apps/alias-server/test/dbTests.js
+++ b/apps/alias-server/test/dbTests.js
@@ -7,8 +7,10 @@
     initializeDb,
     getServerState,
     updateServerState,
+    addOneAliasToDb,
     addAliasesToDb,
     getAliasesFromDb,
+    checkAliasAlreadyRegistered,
 } = require('../src/db');
 // Mock mongodb
 const { MongoClient } = require('mongodb');
@@ -46,6 +48,7 @@
     it('getServerState returns expected initial server state on initialized database', async function () {
         // Check that serverState was initialized properly
         const initialServerState = await getServerState(testDb);
+
         assert.deepEqual(initialServerState, {
             processedBlockheight: 0,
             processedConfirmedTxs: 0,
@@ -95,7 +98,6 @@
         const fetchedServerStateOnRestart = await getServerState(
             testDbOnRestart,
         );
-        // Verify serverState has not reverted to initial value
         assert.deepEqual(fetchedServerStateOnRestart, newServerState);
     });
     it('addAliasesToDb successfully adds new valid aliases to an empty collection', async function () {
@@ -112,6 +114,94 @@
         // Verify addedValidAliases match the added mock
         assert.deepEqual(addedValidAliases, testAddressAliases.validAliasTxs);
     });
+    it('addOneAliasToDb successfully adds a new valid alias to an empty collection', async function () {
+        // newValidAliases needs to be a clone of the mock because
+        // each object gets an _id field when added to the database
+        const newValidAliases = JSON.parse(
+            JSON.stringify(testAddressAliases.validAliasTxs),
+        );
+        const aliasAddedSuccess = await addOneAliasToDb(
+            testDb,
+            newValidAliases[0],
+        );
+        // Get the newly added valid aliases
+        // Note we return valid aliases without the database _id field
+        const addedValidAliases = await getAliasesFromDb(testDb);
+
+        // Verify the function returns true on alias add success
+        assert.strictEqual(aliasAddedSuccess, true);
+        // Verify the database has the expected alias
+        assert.deepEqual(addedValidAliases, [
+            testAddressAliases.validAliasTxs[0],
+        ]);
+    });
+    it('addOneAliasToDb successfully adds a new valid alias to an existing collection', async function () {
+        // newValidAliases needs to be a clone of the mock because
+        // each object gets an _id field when added to the database
+        const newValidAliases = JSON.parse(
+            JSON.stringify(testAddressAliases.validAliasTxs),
+        );
+        // Pre-populate the aliases collection
+        await addAliasesToDb(testDb, newValidAliases);
+
+        const newMockAlias = {
+            address: 'ecash:qrmz0egsqxj35x5jmzf8szrszdeu72fx0uxgwk3r48',
+            alias: 'rico',
+            txid: '3ff9c28fa07cb88c87000ef0f5ee61953d874ffade154cd3f88fd60b88ea2879',
+            blockheight: 787674,
+        };
+
+        // clone to check unit test result as _id will be added to newMockAlias
+        const newMockAliasClone = JSON.parse(JSON.stringify(newMockAlias));
+
+        // Add an alias tx that does not exist
+        const aliasAddedSuccess = await addOneAliasToDb(testDb, newMockAlias);
+        // Get the newly added valid aliases
+        // Note we return valid aliases without the database _id field
+        const addedValidAliases = await getAliasesFromDb(testDb);
+
+        // Verify the function returns true on alias add success
+        assert.strictEqual(aliasAddedSuccess, true);
+        // Verify the database has the expected alias
+        assert.deepEqual(
+            addedValidAliases,
+            testAddressAliases.validAliasTxs.concat(newMockAliasClone),
+        );
+    });
+    it('addOneAliasToDb returns false and fails to add an alias if it is already in the database', async function () {
+        // newValidAliases needs to be a clone of the mock because
+        // each object gets an _id field when added to the database
+        const newValidAliases = JSON.parse(
+            JSON.stringify(testAddressAliases.validAliasTxs),
+        );
+        // Pre-populate the aliases collection
+        await addAliasesToDb(testDb, newValidAliases);
+
+        const newMockAliasTxRegisteringExistingAlias = {
+            address: 'ecash:qzvydd4n3lm3xv62cx078nu9rg0e3srmqq0knykfed',
+            alias: 'foo',
+            txid: 'f41ccfbd88d228bbb695b771dd0c266b0351eda9a35aeb8c5e3cb7670e7e17cc',
+            blockheight: 776576,
+        };
+
+        // Add an alias tx that does not exist
+        const aliasAddedSuccess = await addOneAliasToDb(
+            testDb,
+            newMockAliasTxRegisteringExistingAlias,
+        );
+        // Get the newly added valid aliases
+        // Note we return valid aliases without the database _id field
+        const addedValidAliases = await getAliasesFromDb(testDb);
+
+        // Verify the function returns true on alias add success
+        assert.strictEqual(aliasAddedSuccess, false);
+        // Verify the database has the expected aliases (without the failed add)
+        assert.deepEqual(addedValidAliases, testAddressAliases.validAliasTxs);
+    });
+    it('getAliasesFromDb returns an empty array if no aliases have been added to the collection', async function () {
+        const validAliases = await getAliasesFromDb(testDb);
+        assert.deepEqual(validAliases, []);
+    });
     it('addAliasesToDb returns false if you attempt to add aliases whose txid already exists in the database', async function () {
         // Startup the app and initialize serverState
         const testDb = await initializeDb(testMongoClient);
@@ -134,4 +224,41 @@
         // Verify addAliasesToDb returned false on attempt to add duplicate aliases to the db
         assert.deepEqual(failedResult, false);
     });
+    it('checkAliasAlreadyRegistered returns false if alias is not in an empty database collection', async function () {
+        const aliasAlreadyRegistered = await checkAliasAlreadyRegistered(
+            testDb,
+            'sometestalias',
+        );
+        assert.strictEqual(aliasAlreadyRegistered, false);
+    });
+    it('checkAliasAlreadyRegistered returns true for an alias that already exists in the database collection', async function () {
+        // Add aliases to the database
+        // aliasesInDb needs to be a clone of the mock because
+        // each object gets an _id field when added to the database
+        const aliasesInDb = JSON.parse(
+            JSON.stringify(testAddressAliases.validAliasTxs),
+        );
+        await addAliasesToDb(testDb, aliasesInDb);
+
+        const aliasAlreadyRegistered = await checkAliasAlreadyRegistered(
+            testDb,
+            'nfs',
+        );
+        assert.strictEqual(aliasAlreadyRegistered, true);
+    });
+    it('checkAliasAlreadyRegistered returns false for an alias that does not exist in a non-empty database collection', async function () {
+        // Add aliases to the database
+        // aliasesInDb needs to be a clone of the mock because
+        // each object gets an _id field when added to the database
+        const aliasesInDb = JSON.parse(
+            JSON.stringify(testAddressAliases.validAliasTxs),
+        );
+        await addAliasesToDb(testDb, aliasesInDb);
+
+        const aliasAlreadyRegistered = await checkAliasAlreadyRegistered(
+            testDb,
+            'thisoneisnew',
+        );
+        assert.strictEqual(aliasAlreadyRegistered, false);
+    });
 });
diff --git a/apps/alias-server/test/eventsTests.js b/apps/alias-server/test/eventsTests.js
--- a/apps/alias-server/test/eventsTests.js
+++ b/apps/alias-server/test/eventsTests.js
@@ -3,14 +3,44 @@
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
 'use strict';
+const config = require('../config');
 const assert = require('assert');
+const cashaddr = require('ecashaddrjs');
 const mockSecrets = require('../secrets.sample');
-const { handleAppStartup } = require('../src/events');
+const { handleAppStartup, handleBlockConnected } = require('../src/events');
 const { MockChronikClient } = require('./mocks/chronikMock');
 const MockAdapter = require('axios-mock-adapter');
 const axios = require('axios');
+// Mock mongodb
+const { initializeDb, updateServerState } = require('../src/db');
+const { MongoClient } = require('mongodb');
+const { MongoMemoryServer } = require('mongodb-memory-server');
+const { testAddressAliases } = require('./mocks/aliasMocks');
 
 describe('alias-server events.js', async function () {
+    let mongoServer, testMongoClient;
+    before(async () => {
+        // Start mongo memory server before running this suite of unit tests
+        mongoServer = await MongoMemoryServer.create();
+        const mongoUri = mongoServer.getUri();
+        testMongoClient = new MongoClient(mongoUri);
+    });
+
+    after(async () => {
+        // Shut down mongo memory server after running this suite of unit tests
+        await testMongoClient.close();
+        await mongoServer.stop();
+    });
+
+    let testDb;
+    beforeEach(async () => {
+        // Initialize db before each unit test
+        testDb = await initializeDb(testMongoClient);
+    });
+    afterEach(async () => {
+        // Wipe the database after each unit test
+        await testDb.dropDatabase();
+    });
     it('handleAppStartup calls handleBlockConnected with tipHeight and completes function if block is avalanche finalized', async function () {
         // Initialize chronik mock
         const mockedChronik = new MockChronikClient();
@@ -27,6 +57,16 @@
             output: mockBlockchaininfoResponse,
         });
 
+        // Add tx history to mockedChronik
+        // Set the script
+        const { type, hash } = cashaddr.decode(
+            config.aliasConstants.registrationAddress,
+            true,
+        );
+        mockedChronik.setScript(type, hash);
+        // Set the mock tx history
+        mockedChronik.setTxHistory(testAddressAliases.txHistory);
+
         // Mock avalanche RPC call
         // onNoMatch: 'throwException' helps to debug if mock is not being used
         const mock = new MockAdapter(axios, { onNoMatch: 'throwException' });
@@ -37,7 +77,7 @@
             id: 'isfinalblock',
         });
 
-        const db = null;
+        const db = testDb;
         const telegramBot = null;
         const channelId = null;
         const { avalancheRpc } = mockSecrets;
@@ -128,6 +168,128 @@
             avalancheRpc,
         );
 
+        assert.deepEqual(result, false);
+    });
+    it('handleBlockConnected returns false if the function fails to obtain serverState', async function () {
+        // tipHash called with
+        const tipHash =
+            '00000000000000000b0519ddbffcf6dbab212b95207e398ae3ed2ba312fa561d';
+
+        // Initialize chronik mock
+        const mockedChronik = new MockChronikClient();
+
+        const mockBlock = {
+            blockInfo: {
+                height: 783136,
+            },
+        };
+
+        // Tell mockedChronik what response we expect
+        mockedChronik.setMock('block', {
+            input: tipHash,
+            output: mockBlock,
+        });
+
+        // Add tx history to mockedChronik
+        // Set the script
+        const { type, hash } = cashaddr.decode(
+            config.aliasConstants.registrationAddress,
+            true,
+        );
+        mockedChronik.setScript(type, hash);
+        // Set the mock tx history
+        mockedChronik.setTxHistory(testAddressAliases.txHistory);
+
+        // Mock avalanche RPC call
+        // onNoMatch: 'throwException' helps to debug if mock is not being used
+        const mock = new MockAdapter(axios, { onNoMatch: 'throwException' });
+        // Mock response for rpc return of true for isfinalblock method
+        mock.onPost().reply(200, {
+            result: true,
+            error: null,
+            id: 'isfinalblock',
+        });
+
+        const telegramBot = null;
+        const channelId = null;
+        const { avalancheRpc } = mockSecrets;
+
+        // Rename the serverState collection
+        await testDb
+            .collection(config.database.collections.serverState)
+            .rename('notTheSameName');
+
+        const result = await handleBlockConnected(
+            mockedChronik,
+            testDb,
+            telegramBot,
+            channelId,
+            avalancheRpc,
+            tipHash,
+        );
+
+        assert.deepEqual(result, false);
+    });
+    it('handleBlockConnected returns false if called with a block of height lower than serverState', async function () {
+        // tipHash called with
+        const tipHash =
+            '00000000000000000b0519ddbffcf6dbab212b95207e398ae3ed2ba312fa561d';
+
+        // Initialize chronik mock
+        const mockedChronik = new MockChronikClient();
+
+        const mockBlock = {
+            blockInfo: {
+                height: 783136,
+            },
+        };
+
+        // Tell mockedChronik what response we expect
+        mockedChronik.setMock('block', {
+            input: tipHash,
+            output: mockBlock,
+        });
+
+        // Add tx history to mockedChronik
+        // Set the script
+        const { type, hash } = cashaddr.decode(
+            config.aliasConstants.registrationAddress,
+            true,
+        );
+        mockedChronik.setScript(type, hash);
+        // Set the mock tx history
+        mockedChronik.setTxHistory(testAddressAliases.txHistory);
+
+        // Mock avalanche RPC call
+        // onNoMatch: 'throwException' helps to debug if mock is not being used
+        const mock = new MockAdapter(axios, { onNoMatch: 'throwException' });
+        // Mock response for rpc return of true for isfinalblock method
+        mock.onPost().reply(200, {
+            result: true,
+            error: null,
+            id: 'isfinalblock',
+        });
+
+        const telegramBot = null;
+        const channelId = null;
+        const { avalancheRpc } = mockSecrets;
+
+        // Give the app a serverState in the future of the calling block
+        const mockServerState = {
+            processedBlockheight: 783137,
+            processedConfirmedTxs: 100,
+        };
+        await updateServerState(testDb, mockServerState);
+
+        const result = await handleBlockConnected(
+            mockedChronik,
+            testDb,
+            telegramBot,
+            channelId,
+            avalancheRpc,
+            tipHash,
+        );
+
         assert.deepEqual(result, false);
     });
 });
diff --git a/apps/alias-server/test/mainTests.js b/apps/alias-server/test/mainTests.js
--- a/apps/alias-server/test/mainTests.js
+++ b/apps/alias-server/test/mainTests.js
@@ -9,6 +9,7 @@
 const mockSecrets = require('../secrets.sample');
 const MockAdapter = require('axios-mock-adapter');
 const axios = require('axios');
+const { testAddressAliases } = require('./mocks/aliasMocks');
 
 // Mock mongodb
 const { MongoClient } = require('mongodb');
@@ -16,22 +17,20 @@
 // Mock chronik
 const { MockChronikClient } = require('./mocks/chronikMock');
 
-let mongoServer, testMongoClient;
-before(async () => {
-    mongoServer = await MongoMemoryServer.create();
-    const mongoUri = mongoServer.getUri();
-    testMongoClient = new MongoClient(mongoUri);
-});
+describe('alias-server main.js', async function () {
+    let mongoServer, testMongoClient;
+    before(async () => {
+        mongoServer = await MongoMemoryServer.create();
+        const mongoUri = mongoServer.getUri();
+        testMongoClient = new MongoClient(mongoUri);
+    });
 
-after(async () => {
-    await testMongoClient.close();
-    await mongoServer.stop();
-});
+    after(async () => {
+        await testMongoClient.close();
+        await mongoServer.stop();
+    });
 
-describe('alias-server main.js', async function () {
     it('main() intializes database correctly, connects to a websocket, and runs handleAppStartup() correctly', async function () {
-        // Define params
-        const mongoClient = testMongoClient;
         // Initialize chronik mock
         const mockedChronik = new MockChronikClient();
         const mockBlockchaininfoResponse = {
@@ -46,6 +45,16 @@
             output: mockBlockchaininfoResponse,
         });
 
+        // Add tx history to mockedChronik
+        // Set the script
+        const { type, hash } = cashaddr.decode(
+            config.aliasConstants.registrationAddress,
+            true,
+        );
+        mockedChronik.setScript(type, hash);
+        // Set the mock tx history
+        mockedChronik.setTxHistory(testAddressAliases.txHistory);
+
         // Mock avalanche RPC call
         // onNoMatch: 'throwException' helps to debug if mock is not being used
         const mock = new MockAdapter(axios, { onNoMatch: 'throwException' });
@@ -55,6 +64,9 @@
             error: null,
             id: 'isfinalblock',
         });
+
+        // Define params
+        const mongoClient = testMongoClient;
         const chronik = mockedChronik;
         const address = config.aliasConstants.registrationAddress;
         const telegramBot = null;
@@ -70,7 +82,6 @@
             avalancheRpc,
             returnMocks,
         );
-        const { type, hash } = cashaddr.decode(address, true);
         // Check that the database was initialized properly
         assert.strictEqual(result.db.namespace, config.database.name);
         // Check that websocket is connected