diff --git a/src/provisioner.ts b/src/provisioner.ts
index 478e81e6a343ce83b34eed06d0c33862120167b2..ad5d4f982b827d7dbc667ee6c226da8bdeac4744 100644
--- a/src/provisioner.ts
+++ b/src/provisioner.ts
@@ -10,7 +10,7 @@ const PERMISSION_REQUEST_TIMEOUT = 300000; // 5 minutes
 export class Provisioner {
 
     private bridge: Bridge;
-    private pendingRequests: { [channelId: string]: (approved: boolean) => void } = {}; // [channelId]: resolver fn
+    private pendingRequests: Map<string, (approved: boolean) => void> = new Map(); // [channelId]: resolver fn
 
     public SetBridge(bridge: Bridge): void {
         this.bridge = bridge;
@@ -32,38 +32,47 @@ export class Provisioner {
         return this.bridge.getRoomStore().removeEntriesByRemoteRoomId(remoteRoom.getId());
     }
 
-    public async AskBridgePermission(channel: Discord.TextChannel, requestor: string): Promise<void> {
+    public async AskBridgePermission(
+        channel: Discord.TextChannel,
+        requestor: string,
+        timeout: number = PERMISSION_REQUEST_TIMEOUT): Promise<string> {
         const channelId = `${channel.guild.id}/${channel.id}`;
 
         let responded = false;
+        let resolve: (msg: string) => void;
+        let reject: (err: Error) => void;
+        const deferP: Promise<string> = new Promise((res, rej) => {resolve = res; reject = rej; });
+
         const approveFn = (approved: boolean, expired = false) => {
             if (responded) {
                 return;
             }
 
             responded = true;
-            delete this.pendingRequests[channelId];
+            this.pendingRequests.delete(channelId);
             if (approved) {
-                return;
+                resolve("Approved");
             } else {
                 if (expired) {
-                    throw new Error("Timed out waiting for a response from the Discord owners");
+                    reject(Error("Timed out waiting for a response from the Discord owners"));
                 } else {
-                    throw new Error("The bridge has been declined by the Discord guild");
+                    reject(Error("The bridge has been declined by the Discord guild"));
                 }
             }
         };
 
-        this.pendingRequests[channelId] = approveFn;
-        setTimeout(() => approveFn(false, true), PERMISSION_REQUEST_TIMEOUT);
+        this.pendingRequests.set(channelId, approveFn);
+        setTimeout(() => approveFn(false, true), timeout);
 
-        await channel.sendMessage(`${requestor} on matrix would like to bridge this channel. Someone with permission` +
+        await channel.send(`${requestor} on matrix would like to bridge this channel. Someone with permission` +
             " to manage webhooks please reply with !approve or !deny in the next 5 minutes");
+        return await deferP;
+
     }
 
     public HasPendingRequest(channel: Discord.TextChannel): boolean {
         const channelId = `${channel.guild.id}/${channel.id}`;
-        return !!this.pendingRequests[channelId];
+        return this.pendingRequests.has(channelId);
     }
 
     public async MarkApproved(
@@ -72,17 +81,17 @@ export class Provisioner {
         allow: boolean,
     ): Promise<boolean> {
         const channelId = `${channel.guild.id}/${channel.id}`;
-        if (!this.pendingRequests[channelId]) {
+        if (!this.pendingRequests.has(channelId)) {
             return false; // no change, so false
         }
 
         const perms = channel.permissionsFor(member);
-        if (!perms || !perms.hasPermission(Discord.Permissions.FLAGS.MANAGE_WEBHOOKS as Discord.PermissionResolvable)) {
+        if (!perms || !perms.has(Discord.Permissions.FLAGS.MANAGE_WEBHOOKS as Discord.PermissionResolvable)) {
             // Missing permissions, so just reject it
             throw new Error("You do not have permission to manage webhooks in this channel");
         }
 
-        this.pendingRequests[channelId](allow);
+        this.pendingRequests.get(channelId)!(allow);
         return true; // replied, so true
     }
 }
diff --git a/test/mocks/channel.ts b/test/mocks/channel.ts
index 4b3097e329ed4fa0e0d50fed06ef2b5ebd285225..274169c5d01b791cf777176ff1de34719811c446 100644
--- a/test/mocks/channel.ts
+++ b/test/mocks/channel.ts
@@ -1,5 +1,6 @@
 import {MockMember} from "./member";
 import {MockCollection} from "./collection";
+import {Permissions, PermissionResolvable} from "discord.js";
 
 // we are a test file and thus need those
 /* tslint:disable:no-unused-expression max-file-line-count no-any */
@@ -14,7 +15,12 @@ export class MockChannel {
         public name: string = "",
         public topic: string = "",
     ) { }
+
     public async send(data: any): Promise<any> {
         return data;
     }
+
+    public permissionsFor(member: MockMember) {
+        return new Permissions(Permissions.FLAGS.MANAGE_WEBHOOKS as PermissionResolvable);
+    }
 }
diff --git a/test/test_provisioner.ts b/test/test_provisioner.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9c91af039fc51f5803c563fcf9266de1499bd414
--- /dev/null
+++ b/test/test_provisioner.ts
@@ -0,0 +1,60 @@
+import * as Chai from "chai";
+import { Provisioner } from "../src/provisioner";
+import { MockChannel } from "./mocks/channel";
+import { MockMember } from "./mocks/member";
+
+// we are a test file and thus need those
+/* tslint:disable:no-any */
+
+const expect = Chai.expect;
+
+const TIMEOUT_MS = 1000;
+
+describe("Provisioner", () => {
+    describe("AskBridgePermission", () => {
+        it("should fail to bridge a room that timed out", async () => {
+            const p = new Provisioner();
+            const startAt = Date.now();
+            try {
+                await p.AskBridgePermission(
+                    new MockChannel("foo", "bar") as any,
+                    "Mark",
+                    TIMEOUT_MS,
+                );
+                throw Error("Should have thrown an error");
+            } catch (err) {
+                expect(err.message).to.eq("Timed out waiting for a response from the Discord owners");
+                const delay = Date.now() - startAt;
+                if (delay < TIMEOUT_MS) {
+                    throw Error(`Should have waited for timeout before resolving, waited: ${delay}ms`);
+                }
+            }
+        });
+        it("should fail to bridge a room that was declined", async () => {
+            const p = new Provisioner();
+            const promise = p.AskBridgePermission(
+                new MockChannel("foo", "bar") as any,
+                "Mark",
+                TIMEOUT_MS,
+            );
+            await p.MarkApproved(new MockChannel("foo", "bar") as any, new MockMember("abc", "Mark") as any, false);
+            try {
+                await promise;
+                throw Error("Should have thrown an error");
+            } catch (err) {
+                expect(err.message).to.eq("The bridge has been declined by the Discord guild");
+            }
+
+        });
+        it("should bridge a room that was approved", async () => {
+            const p = new Provisioner();
+            const promise = p.AskBridgePermission(
+                new MockChannel("foo", "bar") as any,
+                "Mark",
+                TIMEOUT_MS,
+            );
+            await p.MarkApproved(new MockChannel("foo", "bar") as any, new MockMember("abc", "Mark") as any, true);
+            expect(await promise).to.eq("Approved");
+        });
+    });
+});