diff --git a/.travis.yml b/.travis.yml
index 68421c53854359ae12525bf669d00091e383dd6a..3b4dc89da9d850a78c47b28d6da2be92276eea8b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,7 +4,8 @@ node_js:
  - "9.11.1"
  - "10.1.0"
 script:
- - npm test
+ - npm run-script build
+ - npm run-script coverage
  - npm run-script lint
 notifications:
     webhooks:
diff --git a/src/config.ts b/src/config.ts
index 1488f307fce28c7683a0fbc13ff928d4557e810f..0db41c76f83a6c0da6fc2e5ea7ff1dad84026952 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -1,11 +1,11 @@
 /** Type annotations for config/config.schema.yaml */
-
 export class DiscordBridgeConfig {
   public bridge: DiscordBridgeConfigBridge = new DiscordBridgeConfigBridge();
   public auth: DiscordBridgeConfigAuth = new DiscordBridgeConfigAuth();
   public logging: DiscordBridgeConfigLogging = new DiscordBridgeConfigLogging();
   public database: DiscordBridgeConfigDatabase = new DiscordBridgeConfigDatabase();
   public room: DiscordBridgeConfigRoom = new DiscordBridgeConfigRoom();
+  public limits: DiscordBridgeConfigLimits = new DiscordBridgeConfigLimits();
 }
 
 class DiscordBridgeConfigBridge {
@@ -37,3 +37,7 @@ class DiscordBridgeConfigLogging {
 class DiscordBridgeConfigRoom {
   public defaultVisibility: string;
 }
+
+class DiscordBridgeConfigLimits {
+  public roomGhostJoinDelay: number = 6000;
+}
diff --git a/src/matrixroomhandler.ts b/src/matrixroomhandler.ts
index cee2ea5aa33448a414c56c025d34d90a90942529..5c8f9d1f78b95b1849903d839626e0bda9d74335 100644
--- a/src/matrixroomhandler.ts
+++ b/src/matrixroomhandler.ts
@@ -17,7 +17,6 @@ import { Util } from "./util";
 import { Provisioner } from "./provisioner";
 
 const ICON_URL = "https://matrix.org/_matrix/media/r0/download/matrix.org/mlxoESwIsTbJrfXyAAogrNxA";
-const JOIN_DELAY = 6000;
 const HTTP_UNSUPPORTED = 501;
 const ROOM_NAME_PARTS = 2;
 const AGE_LIMIT = 900000; // 15 * 60 * 1000
@@ -65,32 +64,34 @@ export class MatrixRoomHandler {
   public OnAliasQueried (alias: string, roomId: string) {
     // Join a whole bunch of users.
     let promiseChain: any = Bluebird.resolve();
-    let delay = JOIN_DELAY; /* We delay the joins to give some implmentations a chance to breathe */
+    /* We delay the joins to give some implementations a chance to breathe */
+    let delay = this.config.limits.roomGhostJoinDelay;
     return this.discord.GetChannelFromRoomId(roomId).then((channel: Discord.Channel) => {
-      for (const member of (<Discord.TextChannel> channel).guild.members.array()) {
+      for (const member of (<Discord.TextChannel> channel).members.array()) {
         if (member.id === this.discord.GetBotId()) {
           continue;
         }
         promiseChain = promiseChain.return(Bluebird.delay(delay).then(() => {
           return this.discord.InitJoinUser(member, [roomId]);
         }));
-        delay += JOIN_DELAY;
+        delay += this.config.limits.roomGhostJoinDelay;
       }
     }).catch((err) => {
       log.verbose("OnAliasQueried => %s", err);
+      throw err;
     });
   }
 
-  public OnEvent (request, context) {
+  public OnEvent (request, context): Promise<any> {
     const event = request.getData();
     if (event.unsigned.age > AGE_LIMIT) {
       log.warn("MatrixRoomHandler", "Skipping event due to age %s > %s", event.unsigned.age, AGE_LIMIT);
-      return;
+      return Promise.reject("Event too old");
     }
     if (event.type === "m.room.member" && event.content.membership === "invite") {
-      this.HandleInvite(event);
+      return this.HandleInvite(event);
     } else if (event.type === "m.room.redaction" && context.rooms.remote) {
-      this.discord.ProcessMatrixRedact(event);
+      return this.discord.ProcessMatrixRedact(event);
     } else if (event.type === "m.room.message") {
       log.verbose("MatrixRoomHandler", "Got m.room.message event");
       if (event.content.body && event.content.body.startsWith("!discord")) {
@@ -104,6 +105,7 @@ export class MatrixRoomHandler {
     } else {
       log.verbose("MatrixRoomHandler", "Got non m.room.message event");
     }
+    return Promise.reject("Event not processed by bridge");
   }
 
   public HandleInvite(event: any) {
@@ -160,7 +162,7 @@ export class MatrixRoomHandler {
 
       if (command === "help" && args[0] === "bridge") {
           const link = Util.GetBotLink(this.config);
-          this.bridge.getIntent().sendMessage(event.room_id, {
+          return this.bridge.getIntent().sendMessage(event.room_id, {
               msgtype: "m.notice",
               body: "How to bridge a Discord guild:\n" +
               "1. Invite the bot to your Discord guild using this link: " + link + "\n" +
@@ -248,7 +250,7 @@ export class MatrixRoomHandler {
           } catch (err) {
               log.error("MatrixRoomHandler", "Error while unbridging room " + event.room_id);
               log.error("MatrixRoomHandler", err);
-              return this.bridge.getItent().sendMessage(event.room_id, {
+              return this.bridge.getIntent().sendMessage(event.room_id, {
                   msgtype: "m.notice",
                   body: "There was an error unbridging this room. " +
                     "Please try again later or contact the bridge operator.",
@@ -256,7 +258,7 @@ export class MatrixRoomHandler {
           }
       } else if (command === "help") {
           // Unknown command or no command given to get help on, so we'll just give them the help
-          this.bridge.getIntent().sendMessage(event.room_id, {
+          return this.bridge.getIntent().sendMessage(event.room_id, {
               msgtype: "m.notice",
               body: "Available commands:\n" +
               "!discord bridge <guild id> <channel id>   - Bridges this room to a Discord channel\n" +
diff --git a/test/mocks/channel.ts b/test/mocks/channel.ts
index c9669f94ec275ffd3e035b3a57074639e5816b5c..d9794702e3d4fe8177ee3ae628037e3d47588440 100644
--- a/test/mocks/channel.ts
+++ b/test/mocks/channel.ts
@@ -1,9 +1,8 @@
-import {MockUser} from "./user";
-import * as Discord from "discord.js";
 import {MockMember} from "./member";
 import {MockCollection} from "./collection";
 
 // Mocking TextChannel
 export class MockChannel {
     public members = new MockCollection<string, MockMember>();
+    constructor (public id: string = "", public guild: any = null) { }
 }
diff --git a/test/mocks/guild.ts b/test/mocks/guild.ts
index 30c85a23decfc45a89aaa5903a8ded6896cee95a..670d298392d9bfc0c105a39059a84f76a917083b 100644
--- a/test/mocks/guild.ts
+++ b/test/mocks/guild.ts
@@ -6,8 +6,10 @@ export class MockGuild {
   public channels = new MockCollection<string, Channel>();
   public members = new MockCollection<string, MockMember>();
   public id: string;
-  constructor(id: string, channels: any[]) {
+  public name: string;
+  constructor(id: string, channels: any[] = [], name: string = null) {
     this.id = id;
+    this.name = name || id;
     channels.forEach((item) => {
       this.channels.set(item.id, item);
     });
diff --git a/test/test_matrixroomhandler.ts b/test/test_matrixroomhandler.ts
new file mode 100644
index 0000000000000000000000000000000000000000..36a0e7001b7aabc78fb6f9f2d736cc2502358783
--- /dev/null
+++ b/test/test_matrixroomhandler.ts
@@ -0,0 +1,536 @@
+import * as Chai from "chai";
+import * as ChaiAsPromised from "chai-as-promised";
+import * as Proxyquire from "proxyquire";
+import {DiscordBridgeConfig} from "../src/config";
+import {MockDiscordClient} from "./mocks/discordclient";
+import * as log from "npmlog";
+import {PresenceHandler} from "../src/presencehandler";
+import {DiscordBot} from "../src/bot";
+import {MatrixRoomHandler} from "../src/matrixroomhandler";
+import {MockChannel} from "./mocks/channel";
+import {MockMember} from "./mocks/member";
+import * as Bluebird from "bluebird";
+import {MockGuild} from "./mocks/guild";
+import {Guild} from "discord.js";
+
+Chai.use(ChaiAsPromised);
+const expect = Chai.expect;
+
+// const DiscordClientFactory = Proxyquire("../src/clientfactory", {
+//     "discord.js": { Client: require("./mocks/discordclient").MockDiscordClient },
+// }).DiscordClientFactory;
+
+let USERSJOINED = 0;
+
+function buildRequest(eventData) {
+    if (eventData.unsigned === undefined) {
+        eventData.unsigned = {age: 0};
+    }
+    return {
+        getData: () => eventData,
+    };
+}
+
+function createRH(opts: any = {}) {
+    log.level = "silent";
+    USERSJOINED = 0;
+    const bot = {
+        GetChannelFromRoomId: (roomid: string) => {
+            if (roomid === "!accept:localhost") {
+                const chan = new MockChannel();
+                if (opts.createMembers) {
+                    chan.members.set("12345", new MockMember("12345", "testuser1"));
+                    chan.members.set("54321", new MockMember("54321", "testuser2"));
+                    chan.members.set("bot12345", new MockMember("bot12345", "botuser"));
+                }
+                return Promise.resolve(chan);
+            } else {
+                return Promise.reject("Roomid not found");
+            }
+        },
+        InitJoinUser: (member: MockMember, roomids: string[]) => {
+                if (opts.failUser) {
+                    return Promise.reject("test is rejecting joins");
+                }
+                USERSJOINED++;
+                return Promise.resolve();
+
+        },
+        GetBotId: () => "bot12345",
+        ProcessMatrixRedact: () => Promise.resolve("redacted"),
+        ProcessMatrixMsgEvent: () => Promise.resolve("processed"),
+        LookupRoom: (guildid, discordid) => {
+            if (guildid !== "123") {
+                return Promise.reject("Guild not found");
+            } else if (discordid !== "456") {
+                return Promise.reject("Channel not found");
+            }
+            const channel = new MockChannel();
+            return Promise.resolve({channel, botUser: true });
+        },
+        GetGuilds: () => [new MockGuild("123", [])],
+        ThirdpartySearchForChannels: () => {
+            return [];
+        },
+    };
+    const config = new DiscordBridgeConfig();
+    config.limits.roomGhostJoinDelay = 0;
+    if (opts.disableSS) {
+        config.bridge.enableSelfServiceBridging = false;
+    } else {
+        config.bridge.enableSelfServiceBridging = true;
+    }
+    const mxClient = {
+        getStateEvent: () => {
+            return Promise.resolve(opts.powerLevels || {});
+        },
+    };
+    const provisioner = {
+        AskBridgePermission: () => {
+            return opts.denyBridgePermission ?
+                Promise.reject(new Error("The bridge has been declined by the Discord guild")) : Promise.resolve();
+        },
+        BridgeMatrixRoom: () => {
+            if (opts.failBridgeMatrix) {
+                throw new Error("Test failed matrix bridge");
+            }
+        },
+        UnbridgeRoom: () => {
+            return opts.failUnbridge ?
+                Promise.reject(new Error("Test failed unbridge")) : Promise.resolve();
+        },
+    };
+    const handler = new MatrixRoomHandler(bot as any, config, "@botuser:localhost", provisioner as any);
+    handler.setBridge({
+        getIntent: () => { return {
+            sendMessage: (roomId, content) => Promise.resolve(content),
+            getClient: () => mxClient,
+        }; },
+    });
+    return handler;
+}
+
+describe("MatrixRoomHandler", () => {
+    describe("OnAliasQueried", () => {
+        it("should join successfully", () => {
+            const handler = createRH();
+            return expect(handler.OnAliasQueried("#accept:localhost", "!accept:localhost")).to.be.fulfilled;
+        });
+        it("should join successfully and create ghosts", () => {
+            const EXPECTEDUSERS = 2;
+            const TESTDELAY = 50;
+            const handler = createRH({createMembers: true});
+            return  handler.OnAliasQueried("#accept:localhost", "!accept:localhost").then(() => {
+                return Bluebird.delay(TESTDELAY);
+            }).then(() => {
+                    expect(USERSJOINED).to.equal(EXPECTEDUSERS);
+                    // test for something
+                    return true;
+            });
+        });
+        it("should not join successfully", () => {
+            const handler = createRH();
+            return expect(handler.OnAliasQueried("#reject:localhost", "!reject:localhost")).to.be.rejected;
+        });
+    });
+    describe("OnEvent", () => {
+        it("should reject old events", () => {
+            const AGE = 900001; // 15 * 60 * 1000
+            const handler = createRH();
+            return expect(handler.OnEvent(
+                buildRequest({unsigned: {age: AGE}}), null))
+                .to.be.rejectedWith("Event too old");
+        });
+        it("should reject un-processable events", () => {
+            const AGE = 900000; // 15 * 60 * 1000
+            const handler = createRH();
+            return expect(handler.OnEvent(buildRequest({
+                content: {},
+                type: "m.potato",
+                unsigned: {age: AGE}}), null)).to.be.rejectedWith("Event not processed by bridge");
+        });
+        it("should handle invites", () => {
+            const handler = createRH();
+            handler.HandleInvite = (ev) => Promise.resolve("invited");
+            return expect(handler.OnEvent(buildRequest({
+                content: {membership: "invite"},
+                type: "m.room.member"}), null)).to.eventually.equal("invited");
+        });
+        it("should ignore other member types", () => {
+            const handler = createRH();
+            handler.HandleInvite = (ev) => Promise.resolve("invited");
+            return expect(handler.OnEvent(buildRequest({
+                content: {membership: "join"},
+                type: "m.room.member"}), null)).to.be.rejectedWith("Event not processed by bridge");
+        });
+        it("should handle redactions with existing rooms", () => {
+            const handler = createRH();
+            const context = {
+                rooms: {
+                    remote: true,
+                },
+            };
+            return expect(handler.OnEvent(buildRequest({
+                type: "m.room.redaction"}), context)).to.eventually.equal("redacted");
+        });
+        it("should ignore redactions with no linked room", () => {
+            const handler = createRH();
+            const context = {
+                rooms: {
+                    remote: null,
+                },
+            };
+            return expect(handler.OnEvent(buildRequest({
+                type: "m.room.redaction"}), context)).to.be.rejectedWith("Event not processed by bridge");
+        });
+        it("should process regular messages", () => {
+            const handler = createRH();
+            const context = {
+                rooms: {
+                    remote: {
+                        roomId: "_discord_123_456",
+                    },
+                },
+            };
+            return expect(handler.OnEvent(buildRequest({
+                type: "m.room.message", content: {body: "abc"}}), context)).to.eventually.equal("processed");
+        });
+        it("should process !discord commands", () => {
+            const handler = createRH();
+            handler.ProcessCommand = (ev) => Promise.resolve("processedcmd");
+            return expect(handler.OnEvent(buildRequest({
+                type: "m.room.message", content: {body: "!discord cmd"}}), null))
+                .to.eventually.equal("processedcmd");
+        });
+        it("should ignore regular messages with no linked room", () => {
+            const handler = createRH();
+            const context = {
+                rooms: {
+                    remote: null,
+                },
+            };
+            return expect(handler.OnEvent(buildRequest({
+                type: "m.room.message", content: {body: "abc"}}), context))
+                .to.be.rejectedWith("Event not processed by bridge");
+        });
+    });
+    describe("HandleInvite", () => {
+        it("should accept invite for bot user", () => {
+            const handler: any = createRH();
+            handler.joinRoom = () => Promise.resolve("joinedroom");
+            return expect(handler.HandleInvite({
+                state_key: "@botuser:localhost",
+            })).to.eventually.be.equal("joinedroom");
+        });
+        it("should deny invite for other users", () => {
+            const handler: any = createRH();
+            handler.joinRoom = () => Promise.resolve("joinedroom");
+            return expect(handler.HandleInvite({
+                state_key: "@user:localhost",
+            })).to.be.undefined;
+        });
+    });
+    describe("ProcessCommand", () => {
+        it("should warn if self service is disabled", () => {
+            const handler: any = createRH({disableSS: true});
+            return expect(handler.ProcessCommand({
+                room_id: "!123:localhost",
+            })).to.eventually.be.deep.equal({
+                msgtype: "m.notice",
+                body: "The owner of this bridge does not permit self-service bridging.",
+            });
+        });
+        it("should warn if user is not powerful enough with defaults", () => {
+            const handler: any = createRH();
+            return expect(handler.ProcessCommand({
+                room_id: "!123:localhost",
+            })).to.eventually.be.deep.equal({
+                msgtype: "m.notice",
+                body: "You do not have the required power level in this room to create a bridge to a Discord channel.",
+            });
+        });
+        it("should warn if user is not powerful enough with custom state default", () => {
+            const handler: any = createRH({powerLevels: {
+                state_default: 67,
+            }});
+            return expect(handler.ProcessCommand({
+                room_id: "!123:localhost",
+            })).to.eventually.be.deep.equal({
+                msgtype: "m.notice",
+                body: "You do not have the required power level in this room to create a bridge to a Discord channel.",
+            });
+        });
+        it("should allow if user is powerful enough with defaults", () => {
+            const handler: any = createRH({powerLevels: {
+                    users_default: 60,
+                }});
+            return handler.ProcessCommand({
+                room_id: "!123:localhost",
+                content: {body: "!discord help"},
+            }).then((evt) => {
+                return expect(evt.body.startsWith("Available commands")).to.be.true;
+            });
+        });
+        it("should allow if user is powerful enough with their own state", () => {
+            const handler: any = createRH({powerLevels: {
+                    users: {
+                     "@user:localhost": 100,
+                    },
+                }});
+            return handler.ProcessCommand({
+                room_id: "!123:localhost",
+                sender: "@user:localhost",
+                content: {body: "!discord help"},
+            }).then((evt) => {
+                return expect(evt.body.startsWith("Available commands")).to.be.true;
+            });
+        });
+        describe("!discord bridge", () => {
+            it("will bridge a new room, and ask for permissions", () => {
+                const handler: any = createRH({powerLevels: {
+                        users_default: 100,
+                    }});
+                const context = {rooms: {}};
+                return handler.ProcessCommand({
+                    room_id: "!123:localhost",
+                    content: {body: "!discord bridge 123 456"},
+                }, context).then((evt) => {
+                    return expect(evt.body).to.be.eq("I have bridged this room to your channel");
+                });
+            });
+            it("will fail to bridge if permissions were denied", () => {
+                const handler: any = createRH({powerLevels: {
+                        users_default: 100,
+                    }, denyBridgePermission: true});
+                const context = {rooms: {}};
+                return handler.ProcessCommand({
+                    room_id: "!123:localhost",
+                    content: {body: "!discord bridge 123 456"},
+                }, context).then((evt) => {
+                    return expect(evt.body).to.be.eq("The bridge has been declined by the Discord guild");
+                });
+            });
+            it("will fail to bridge if permissions were denied", () => {
+                const handler: any = createRH({powerLevels: {
+                        users_default: 100,
+                    }, failBridgeMatrix: true});
+                const context = {rooms: {}};
+                return handler.ProcessCommand({
+                    room_id: "!123:localhost",
+                    content: {body: "!discord bridge 123 456"},
+                }, context).then((evt) => {
+                    return expect(evt.body).to.be
+                        .eq("There was a problem bridging that channel - has the guild owner approved the bridge?");
+                });
+            });
+            it("will not bridge if a link already exists", () => {
+                const handler: any = createRH({powerLevels: {
+                        users_default: 100,
+                    }});
+                const context = {rooms: { remote: true }};
+                return handler.ProcessCommand({
+                    room_id: "!123:localhost",
+                    content: {body: "!discord bridge"},
+                }, context).then((evt) => {
+                    return expect(evt.body).to.be.eq("This room is already bridged to a Discord guild.");
+                });
+            });
+            it("will not bridge without required args", () => {
+                const handler: any = createRH({powerLevels: {
+                        users_default: 100,
+                    }});
+                const context = {rooms: {}};
+                return handler.ProcessCommand({
+                    room_id: "!123:localhost",
+                    content: {body: "!discord bridge"},
+                }, context).then((evt) => {
+                    return expect(evt.body).to.contain("Invalid syntax");
+                });
+            });
+        });
+        describe("!discord unbridge", () => {
+            it("will unbridge", () => {
+                const handler: any = createRH({powerLevels: {
+                        users_default: 100,
+                    }});
+                const context = {rooms: { remote: {
+                    data: {
+                        plumbed: true,
+                    },
+                        } }};
+                return handler.ProcessCommand({
+                    room_id: "!123:localhost",
+                    content: {body: "!discord unbridge"},
+                }, context).then((evt) => {
+                    return expect(evt.body).to.be.eq("This room has been unbridged");
+                });
+            });
+            it("will not unbridge if a link does not exist", () => {
+                const handler: any = createRH({powerLevels: {
+                        users_default: 100,
+                    }});
+                const context = {rooms: { remote: undefined }};
+                return handler.ProcessCommand({
+                    room_id: "!123:localhost",
+                    content: {body: "!discord unbridge"},
+                }, context).then((evt) => {
+                    return expect(evt.body).to.be.eq("This room is not bridged.");
+                });
+            });
+            it("will not unbridge non-plumbed rooms", () => {
+                const handler: any = createRH({powerLevels: {
+                        users_default: 100,
+                    }});
+                const context = {rooms: { remote: {
+                            data: {
+                                plumbed: false,
+                }}}};
+                return handler.ProcessCommand({
+                    room_id: "!123:localhost",
+                    content: {body: "!discord unbridge"},
+                }, context).then((evt) => {
+                    return expect(evt.body).to.be.eq("This room cannot be unbridged.");
+                });
+            });
+            it("will show error if unbridge fails", () => {
+                const handler: any = createRH({powerLevels: {
+                        users_default: 100,
+                    }, failUnbridge: true});
+                const context = {rooms: { remote: {
+                            data: {
+                                plumbed: true,
+                            }}}};
+                return handler.ProcessCommand({
+                    room_id: "!123:localhost",
+                    content: {body: "!discord unbridge"},
+                }, context).then((evt) => {
+                    return expect(evt.body).to.contain("There was an error unbridging this room.");
+                });
+            });
+        });
+    });
+    describe("OnAliasQuery", () => {
+        it("will create room", () => {
+            const handler: any = createRH({});
+            handler.createMatrixRoom = () => true;
+            return expect(handler.OnAliasQuery(
+                "_discord_123_456:localhost",
+                "_discord_123_456")).to.eventually.be.true;
+        });
+        it("will not create room if guild cannot be found", () => {
+            const handler: any = createRH({});
+            handler.createMatrixRoom = () => true;
+            return expect(handler.OnAliasQuery(
+                "_discord_111_456:localhost",
+                "_discord_111_456")).to.eventually.be.undefined;
+        });
+        it("will not create room if channel cannot be found", () => {
+            const handler: any = createRH({});
+            handler.createMatrixRoom = () => true;
+            return expect(handler.OnAliasQuery(
+                "_discord_123_444:localhost",
+                "_discord_123_444")).to.eventually.be.undefined;
+        });
+        it("will not create room if alias is wrong", () => {
+            const handler: any = createRH({});
+            handler.createMatrixRoom = () => true;
+            return expect(handler.OnAliasQuery(
+                "_discord_123:localhost",
+                "_discord_123")).to.be.undefined;
+        });
+    });
+    describe("tpGetProtocol", () => {
+       it("will return an object", () => {
+           const handler: any = createRH({});
+           return handler.tpGetProtocol("").then((protocol) => {
+               expect(protocol).to.not.be.null;
+               expect(protocol.instances[0].network_id).to.equal("123");
+               expect(protocol.instances[0].bot_user_id).to.equal("@botuser:localhost");
+               expect(protocol.instances[0].desc).to.equal("123");
+               expect(protocol.instances[0].network_id).to.equal("123");
+           });
+       });
+    });
+    describe("tpGetLocation", () => {
+        it("will return an array", () => {
+            const handler: any = createRH({});
+            return handler.tpGetLocation("", {
+                guild_id: "",
+                channel_name: "",
+            }).then((channels) => {
+                expect(channels).to.be.a("array");
+            });
+        });
+    });
+    describe("tpParseLocation", () => {
+        it("will reject", () => {
+            const handler: any = createRH({});
+            return expect(handler.tpParseLocation("alias")).to.eventually.be.rejected;
+        });
+    });
+    describe("tpGetUser", () => {
+        it("will reject", () => {
+            const handler: any = createRH({});
+            return expect(handler.tpGetUser("", {})).to.eventually.be.rejected;
+        });
+    });
+    describe("tpParseUser", () => {
+        it("will reject", () => {
+            const handler: any = createRH({});
+            return expect(handler.tpParseUser("alias")).to.eventually.be.rejected;
+        });
+    });
+    describe("joinRoom", () => {
+        it("will join immediately", () => {
+            const handler: any = createRH({});
+            const intent = {
+                getClient: () => {
+                    return {
+                      joinRoom: () => {
+                          return Promise.resolve();
+                      },
+                    };
+                },
+            };
+            const startTime = Date.now();
+            const MAXTIME = 1000;
+            return expect(handler.joinRoom(intent, "#test:localhost")).to.eventually.be.fulfilled.and.satisfy(() => {
+                return (Date.now() - startTime) < MAXTIME;
+            });
+        });
+        it("will fail first, join after", () => {
+            log.level = "error";
+            const handler: any = createRH({});
+            let shouldFail = true;
+            const intent = {
+                getClient: () => {
+                    return {
+                        joinRoom: () => {
+                            if (shouldFail) {
+                                shouldFail = false;
+                                return Promise.reject("Test failed first time");
+                            }
+                            return Promise.resolve();
+                        },
+                        getUserId: () => "@test:localhost",
+                    };
+                },
+            };
+            const startTime = Date.now();
+            const MINTIME = 1000;
+            return expect(handler.joinRoom(intent, "#test:localhost")).to.eventually.be.fulfilled.and.satisfy(() => {
+                expect(shouldFail).to.be.false;
+                return (Date.now() - startTime) > MINTIME;
+            });
+        });
+    });
+    describe("createMatrixRoom", () => {
+        it("will return an object", () => {
+            const handler: any = createRH({});
+            const channel = new MockChannel("123", new MockGuild("456"));
+            const roomOpts = handler.createMatrixRoom(channel, "#test:localhost");
+            expect(roomOpts.creationOpts).to.exist;
+            expect(roomOpts.remote).to.exist;
+        });
+    });
+});