From 374344dc26a00c9d7ff5f84f95b8771cd3b39eeb Mon Sep 17 00:00:00 2001
From: Will Hunt <will@half-shot.uk>
Date: Mon, 28 Jan 2019 00:20:37 +0000
Subject: [PATCH] Test fixes and linting

---
 src/channelsyncroniser.ts       |  2 +-
 src/db/roomstore.ts             | 38 +++++++++++---
 src/matrixroomhandler.ts        |  2 +-
 src/provisioner.ts              |  6 +--
 src/store.ts                    |  1 -
 test/db/test_roomstore.ts       | 48 +++++++++---------
 test/test_channelsyncroniser.ts | 90 ++++++++++++++++-----------------
 test/test_discordbot.ts         |  4 +-
 test/test_matrixroomhandler.ts  | 27 ++++++----
 tools/addRoomsToDirectory.ts    | 11 ++--
 tools/chanfix.ts                | 22 +++-----
 tools/ghostfix.ts               | 10 +---
 12 files changed, 138 insertions(+), 123 deletions(-)

diff --git a/src/channelsyncroniser.ts b/src/channelsyncroniser.ts
index f0b3e7a..9759dfa 100644
--- a/src/channelsyncroniser.ts
+++ b/src/channelsyncroniser.ts
@@ -279,7 +279,7 @@ export class ChannelSyncroniser {
         const options = this.config.channel.deleteOptions;
         const plumbed = entry.remote!.get("plumbed");
 
-        this.roomStore.upsertEntry(entry);
+        await this.roomStore.upsertEntry(entry);
         if (options.ghostsLeave) {
             for (const member of channel.members.array()) {
                 try {
diff --git a/src/db/roomstore.ts b/src/db/roomstore.ts
index 26e467a..303a11a 100644
--- a/src/db/roomstore.ts
+++ b/src/db/roomstore.ts
@@ -1,3 +1,18 @@
+/*
+Copyright 2018 matrix-appservice-discord
+
+Licensed 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.
+*/
 import { Log } from "../log";
 import { IDatabaseConnector } from "./connector";
 
@@ -11,18 +26,23 @@ const log = new Log("DbRoomStore");
  * that accesses the database instead.
  */
 
-interface IRemoteRoomData {
+interface IRemoteRoomData extends IRemoteRoomDataLazy {
     discord_guild: string;
     discord_channel: string;
+}
+
+interface IRemoteRoomDataLazy  {
+    discord_guild?: string;
+    discord_channel?: string;
     discord_name?: string|null;
     discord_topic?: string|null;
     discord_type?: string|null;
     discord_iconurl?: string|null;
     discord_iconurl_mxc?: string|null;
-    update_name?: number|null;
-    update_topic?: number|null;
-    update_icon?: number|null;
-    plumbed?: number|null;
+    update_name?: number|boolean|null;
+    update_topic?: number|boolean|null;
+    update_icon?: number|boolean|null;
+    plumbed?: number|boolean|null;
 }
 
 export class RemoteStoreRoom {
@@ -78,7 +98,7 @@ export class DbRoomStore {
 
     public async upsertEntry(entry: IRoomStoreEntry) {
         const promises: Promise<void>[] = [];
-        // N.b. Sqlite and postgres don't really have a easy way to do upserts.
+
         const row = (await this.db.Get("SELECT * FROM room_entries WHERE id = $id", {id: entry.id})) || {};
 
         if (!row.id) {
@@ -103,6 +123,9 @@ export class DbRoomStore {
         const rmIdDifferent = remoteId !== row.remote_id;
         // Did the room ids change?
         if (row.id && (mxIdDifferent || rmIdDifferent)) {
+            if (matrixId) {
+                this.entriesMatrixIdCache.delete(matrixId);
+            }
             const items: string[] = [];
 
             if (mxIdDifferent) {
@@ -123,7 +146,6 @@ export class DbRoomStore {
         }
 
         // Matrix room doesn't store any data.
-
         if (entry.remote) {
             await this.upsertRoom(entry.remote);
         }
@@ -203,7 +225,7 @@ export class DbRoomStore {
         // This no-ops, because we don't store anything interesting.
     }
 
-    public async getEntriesByRemoteRoomData(data: {[key: string]: string}): Promise<IRoomStoreEntry[]> {
+    public async getEntriesByRemoteRoomData(data: IRemoteRoomDataLazy): Promise<IRoomStoreEntry[]> {
         const whereClaues = Object.keys(data).map((key) => {
             return `${key} = $${key}`;
         }).join(" AND ");
diff --git a/src/matrixroomhandler.ts b/src/matrixroomhandler.ts
index 137ed9b..75a9e1b 100644
--- a/src/matrixroomhandler.ts
+++ b/src/matrixroomhandler.ts
@@ -345,7 +345,7 @@ export class MatrixRoomHandler {
                 });
 
                 await this.provisioner.AskBridgePermission(channel, event.sender);
-                this.provisioner.BridgeMatrixRoom(channel, event.room_id);
+                await this.provisioner.BridgeMatrixRoom(channel, event.room_id);
                 return this.bridge.getIntent().sendMessage(event.room_id, {
                     body: "I have bridged this room to your channel",
                     msgtype: "m.notice",
diff --git a/src/provisioner.ts b/src/provisioner.ts
index d2d066c..31682a3 100644
--- a/src/provisioner.ts
+++ b/src/provisioner.ts
@@ -33,7 +33,7 @@ export class Provisioner {
         this.roomStore = roomStore;
     }
 
-    public BridgeMatrixRoom(channel: Discord.TextChannel, roomId: string) {
+    public async BridgeMatrixRoom(channel: Discord.TextChannel, roomId: string) {
         const remote = new RemoteRoom(`discord_${channel.guild.id}_${channel.id}_bridged`);
         remote.set("discord_type", "text");
         remote.set("discord_guild", channel.guild.id);
@@ -41,10 +41,10 @@ export class Provisioner {
         remote.set("plumbed", true);
 
         const local = new MatrixRoom(roomId);
-        this.roomStore.linkRooms(local, remote);
+        return this.roomStore.linkRooms(local, remote);
     }
 
-    public UnbridgeRoom(remoteRoom: RemoteRoom) {
+    public async UnbridgeRoom(remoteRoom: RemoteRoom) {
         return this.roomStore.removeEntriesByRemoteRoomId(remoteRoom.getId());
     }
 
diff --git a/src/store.ts b/src/store.ts
index 0a506a4..4e5dd9d 100644
--- a/src/store.ts
+++ b/src/store.ts
@@ -97,7 +97,6 @@ export class DiscordStore {
             const schemaClass = require(`./db/schema/v${version}.js`).Schema;
             let schema: IDbSchema;
             if (version === SCHEMA_ROOM_STORE_REQUIRED) { // 8 requires access to the roomstore.
-                console.log(roomStore);
                 schema = (new schemaClass(roomStore) as IDbSchema);
             } else {
                 schema = (new schemaClass() as IDbSchema);
diff --git a/test/db/test_roomstore.ts b/test/db/test_roomstore.ts
index 734c6fc..4437945 100644
--- a/test/db/test_roomstore.ts
+++ b/test/db/test_roomstore.ts
@@ -26,7 +26,7 @@ const expect = Chai.expect;
 
 // const assert = Chai.assert;
 let store: DiscordStore;
-describe("DiscordStore", () => {
+describe("RoomStore", () => {
     before(async () => {
         store = new DiscordStore(":memory:");
         await store.init();
@@ -82,38 +82,40 @@ describe("DiscordStore", () => {
         });
         it("will replace data on an existing entry", async () => {
             await store.roomStore.upsertEntry({
-                id: "test3",
-                matrix: new MatrixStoreRoom("test3_m"),
-                remote: new RemoteStoreRoom("test3_r", {discord_guild: "123", discord_channel: "456"}),
+                id: "test3.1",
+                matrix: new MatrixStoreRoom("test3.1_m"),
+                remote: new RemoteStoreRoom("test3.1_r", {discord_guild: "123", discord_channel: "456"}),
             });
             await store.roomStore.upsertEntry({
-                id: "test3",
-                matrix: new MatrixStoreRoom("test3_m"),
-                remote: new RemoteStoreRoom("test3_r", {discord_guild: "-100", discord_channel: "seventythousand"}),
+                id: "test3.1",
+                matrix: new MatrixStoreRoom("test3.1_m"),
+                remote: new RemoteStoreRoom("test3.1_r", {discord_guild: "-100", discord_channel: "seventythousand"}),
             });
-            const entry = (await store.roomStore.getEntriesByMatrixId("test3_m"))[0];
-            expect(entry.id).to.equal("test3");
-            expect(entry.matrix!.roomId).to.equal("test3_m");
-            expect(entry.remote!.roomId).to.equal("test3_r");
+            const entry = (await store.roomStore.getEntriesByMatrixId("test3.1_m"))[0];
+            expect(entry.id).to.equal("test3.1");
+            expect(entry.matrix!.roomId).to.equal("test3.1_m");
+            expect(entry.remote!.roomId).to.equal("test3.1_r");
             expect(entry.remote!.get("discord_guild")).to.equal("-100");
             expect(entry.remote!.get("discord_channel")).to.equal("seventythousand");
         });
         it("will delete data on an existing entry", async () => {
             await store.roomStore.upsertEntry({
-                id: "test3",
-                matrix: new MatrixStoreRoom("test3_m"),
-                remote: new RemoteStoreRoom("test3_r", {discord_guild: "123", discord_channel: "456"}),
+                id: "test3.2",
+                matrix: new MatrixStoreRoom("test3.2_m"),
+                remote: new RemoteStoreRoom("test3.2_r", {
+                    discord_channel: "456", discord_guild: "123",  update_icon: true,
+                }),
             });
             await store.roomStore.upsertEntry({
-                id: "test3",
-                matrix: new MatrixStoreRoom("test3_m"),
-                remote: new RemoteStoreRoom("test3_r", {discord_guild: "123", discord_channel: "456"}),
+                id: "test3.2",
+                matrix: new MatrixStoreRoom("test3.2_m"),
+                remote: new RemoteStoreRoom("test3.2_r", {discord_guild: "123", discord_channel: "456"}),
             });
-            const entry = (await store.roomStore.getEntriesByMatrixId("test3_m"))[0];
-            expect(entry.id).to.equal("test3");
-            expect(entry.matrix!.roomId).to.equal("test3_m");
-            expect(entry.remote!.roomId).to.equal("test3_r");
-            expect(entry.remote!.get("baz")).to.be.undefined;
+            const entry = (await store.roomStore.getEntriesByMatrixId("test3.2_m"))[0];
+            expect(entry.id).to.equal("test3.2");
+            expect(entry.matrix!.roomId).to.equal("test3.2_m");
+            expect(entry.remote!.roomId).to.equal("test3.2_r");
+            expect(entry.remote!.get("update_icon")).to.be.eq(0);
         });
     });
     describe("getEntriesByMatrixIds", () => {
@@ -150,7 +152,7 @@ describe("DiscordStore", () => {
         });
     });
     describe("getEntriesByRemoteRoomData", () => {
-        it("will link a room", async () => {
+        it("will get an entry", async () => {
             await store.roomStore.upsertEntry({
                 id: "test6",
                 matrix: new MatrixStoreRoom("test6_m"),
diff --git a/test/test_channelsyncroniser.ts b/test/test_channelsyncroniser.ts
index e4dc618..c54aa2f 100644
--- a/test/test_channelsyncroniser.ts
+++ b/test/test_channelsyncroniser.ts
@@ -26,7 +26,6 @@ import { MatrixEventProcessor, MatrixEventProcessorOpts } from "../src/matrixeve
 import { DiscordBridgeConfig } from "../src/config";
 import { MockChannel } from "./mocks/channel";
 import { Bridge, MatrixRoom, RemoteRoom } from "matrix-appservice-bridge";
-
 // we are a test file and thus need those
 /* tslint:disable:no-unused-expression max-file-line-count no-any */
 
@@ -109,50 +108,48 @@ function CreateChannelSync(remoteChannels: any[] = []): ChannelSyncroniser {
                 },
             };
         },
-        getRoomStore: () => {
-            REMOTECHANNEL_SET = false;
-            REMOTECHANNEL_REMOVED = false;
-            return {
-                getEntriesByMatrixId: (roomid) => {
-                    const entries: any[] = [];
-                    remoteChannels.forEach((c) => {
-                        const mxid = c.matrix.getId();
-                        if (roomid === mxid) {
-                            entries.push(c);
-                        }
-                    });
-                    return entries;
-                },
-                getEntriesByMatrixIds: (roomids) => {
-                    const entries = {};
-                    remoteChannels.forEach((c) => {
-                        const mxid = c.matrix.getId();
-                        if (roomids.includes(mxid)) {
-                            if (!entries[mxid]) {
-                                entries[mxid] = [];
-                            }
-                            entries[mxid].push(c);
-                        }
-                    });
-                    return entries;
-                },
-                getEntriesByRemoteRoomData: (data) => {
-                    return remoteChannels.filter((c) => {
-                        for (const d of Object.keys(data)) {
-                            if (c.remote.get(d) !== data[d]) {
-                                return false;
-                            }
-                        }
-                        return true;
-                    });
-                },
-                removeEntriesByMatrixRoomId: (room) => {
-                    REMOTECHANNEL_REMOVED = true;
-                },
-                upsertEntry: (room) => {
-                    REMOTECHANNEL_SET = true;
-                },
-            };
+    };
+    REMOTECHANNEL_REMOVED = false;
+    REMOTECHANNEL_SET = false;
+    const roomStore = {
+        getEntriesByMatrixId: (roomid) => {
+            const entries: any[] = [];
+            remoteChannels.forEach((c) => {
+                const mxid = c.matrix.getId();
+                if (roomid === mxid) {
+                    entries.push(c);
+                }
+            });
+            return entries;
+        },
+        getEntriesByMatrixIds: (roomids) => {
+            const entries = {};
+            remoteChannels.forEach((c) => {
+                const mxid = c.matrix.getId();
+                if (roomids.includes(mxid)) {
+                    if (!entries[mxid]) {
+                        entries[mxid] = [];
+                    }
+                    entries[mxid].push(c);
+                }
+            });
+            return entries;
+        },
+        getEntriesByRemoteRoomData: (data) => {
+            return remoteChannels.filter((c) => {
+                for (const d of Object.keys(data)) {
+                    if (c.remote.get(d) !== data[d]) {
+                        return false;
+                    }
+                }
+                return true;
+            });
+        },
+        removeEntriesByMatrixRoomId: (room) => {
+            REMOTECHANNEL_REMOVED = true;
+        },
+        upsertEntry: (room) => {
+            REMOTECHANNEL_SET = true;
         },
     };
     const discordbot: any = {
@@ -161,7 +158,8 @@ function CreateChannelSync(remoteChannels: any[] = []): ChannelSyncroniser {
     const config = new DiscordBridgeConfig();
     config.bridge.domain = "localhost";
     config.channel.namePattern = "[Discord] :guild :name";
-    return new ChannelSync(bridge as Bridge, config, discordbot);
+    const cs = new ChannelSync(bridge as Bridge, config, discordbot, roomStore) as ChannelSyncroniser;
+    return cs;
 }
 
 describe("ChannelSyncroniser", () => {
diff --git a/test/test_discordbot.ts b/test/test_discordbot.ts
index a051813..d757a73 100644
--- a/test/test_discordbot.ts
+++ b/test/test_discordbot.ts
@@ -90,7 +90,7 @@ describe("DiscordBot", () => {
         it("should resolve when ready.", async () => {
             discordBot = new modDiscordBot.DiscordBot(
                 config,
-                null,
+                { },
             );
             discordBot.setBridge(mockBridge);
             await discordBot.run();
@@ -101,7 +101,7 @@ describe("DiscordBot", () => {
         beforeEach( async () => {
             discordBot = new modDiscordBot.DiscordBot(
                 config,
-                null,
+                { },
             );
             discordBot.setBridge(mockBridge);
             await discordBot.run();
diff --git a/test/test_matrixroomhandler.ts b/test/test_matrixroomhandler.ts
index 807c0d0..39ce63e 100644
--- a/test/test_matrixroomhandler.ts
+++ b/test/test_matrixroomhandler.ts
@@ -97,13 +97,6 @@ function createRH(opts: any = {}) {
                 unban: async () => { USERSUNBANNED++; },
             };
         },
-        getRoomStore: () => {
-            return {
-                removeEntriesByMatrixRoomId: () => {
-
-                },
-            };
-        },
     };
     const us = {
         JoinRoom: async () => { USERSJOINED++; },
@@ -202,7 +195,20 @@ function createRH(opts: any = {}) {
         },
     };
     const handler = new RoomHandler(bot as any, config, "@botuser:localhost", provisioner as any);
-    handler.setBridge(bridge);
+    handler.setBridge(bridge, {
+        getEntriesByMatrixId: (matrixId) => {
+            return [{
+                matrix: {},
+                remote: {},
+            }];
+        },
+        linkRooms: () => {
+
+        },
+        removeEntriesByMatrixRoomId: () => {
+
+        },
+    });
     return handler;
 }
 
@@ -740,12 +746,11 @@ describe("MatrixRoomHandler", () => {
         });
     });
     describe("createMatrixRoom", () => {
-        it("will return an object", () => {
+        it("will return an object", async () => {
             const handler: any = createRH({});
             const channel = new MockChannel("123", new MockGuild("456"));
-            const roomOpts = handler.createMatrixRoom(channel, "#test:localhost");
+            const roomOpts = await handler.createMatrixRoom(channel, "#test:localhost");
             expect(roomOpts.creationOpts).to.exist;
-            expect(roomOpts.remote).to.exist;
         });
     });
     describe("HandleDiscordCommand", () => {
diff --git a/tools/addRoomsToDirectory.ts b/tools/addRoomsToDirectory.ts
index d127226..200bebb 100644
--- a/tools/addRoomsToDirectory.ts
+++ b/tools/addRoomsToDirectory.ts
@@ -27,6 +27,7 @@ import * as usage from "command-line-usage";
 import { DiscordBridgeConfig } from "../src/config";
 import { Log } from "../src/log";
 import { Util } from "../src/util";
+import { DiscordStore } from "../src/store";
 const log = new Log("AddRoomsToDirectory");
 const optionDefinitions = [
     {
@@ -89,20 +90,20 @@ const bridge = new Bridge({
     domain: "rubbish",
     homeserverUrl: true,
     registration: true,
-    roomStore: options.store,
 });
 
+const discordstore = new DiscordStore(config.database ? config.database.filename : "discord.db");
+
 async function run() {
     try {
-        await bridge.loadDatabases();
+        await discordstore.init();
     } catch (e) {
         log.error(`Failed to load database`, e);
     }
-    // This will be broken.
-    let rooms = await bridge.getRoomStore().getEntriesByRemoteRoomData({
+    let rooms = await discordstore.roomStore.getEntriesByRemoteRoomData({
         discord_type: "text",
     });
-    rooms = rooms.filter((r) => r.remote.get("plumbed") !== true );
+    rooms = rooms.filter((r) => r.remote && r.remote.get("plumbed") !== true );
     const client = clientFactory.getClientAs();
     log.info(`Got ${rooms.length} rooms to set`);
     try {
diff --git a/tools/chanfix.ts b/tools/chanfix.ts
index 1fdc910..ac39a97 100644
--- a/tools/chanfix.ts
+++ b/tools/chanfix.ts
@@ -101,16 +101,11 @@ const bridge = new Bridge({
     userStore: config.database.userStorePath,
 });
 
-// Broken
-//provisioner.setStore(null);
 discordbot.setBridge(bridge);
 
 async function run() {
-    try {
-        await bridge.loadDatabases();
-    } catch (e) {
-        await discordstore.init();
-    }
+    await discordstore.init();
+    provisioner.setStore(discordstore.roomStore);
     bridge._clientFactory = clientFactory;
     bridge._botClient = bridge._clientFactory.getClientAs();
     bridge._botIntent = new Intent(bridge._botClient, bridge._botClient, { registered: true });
@@ -118,24 +113,23 @@ async function run() {
     const client = await discordbot.ClientFactory.getClient();
 
     // first set update_icon to true if needed
-    // This will be broken
-    const mxRoomEntries = await bridge.getRoomStore().getEntriesByRemoteRoomData({
+    // first set update_icon to true if needed
+    const mxRoomEntries = await discordstore.roomStore.getEntriesByRemoteRoomData({
         update_name: true,
         update_topic: true,
     });
 
     const promiseList: Promise<void>[] = [];
     mxRoomEntries.forEach((entry) => {
-        if (entry.remote.get("plumbed")) {
+        if (entry.remote && entry.remote.get("plumbed")) {
             return; // skipping plumbed rooms
         }
-        const updateIcon = entry.remote.get("update_icon");
+        const updateIcon = entry.remote!.get("update_icon");
         if (updateIcon !== undefined && updateIcon !== null) {
             return; // skipping because something was set manually
         }
-        entry.remote.set("update_icon", true);
-        // This will be broken
-        promiseList.push(bridge.getRoomStore().upsertEntry(entry));
+        entry.remote!.set("update_icon", true);
+        promiseList.push(discordstore.roomStore.upsertEntry(entry));
     });
     await Promise.all(promiseList);
 
diff --git a/tools/ghostfix.ts b/tools/ghostfix.ts
index b634282..2da601b 100644
--- a/tools/ghostfix.ts
+++ b/tools/ghostfix.ts
@@ -113,17 +113,11 @@ const bridge = new Bridge({
     roomStore: config.database.roomStorePath,
     userStore: config.database.userStorePath,
 });
-
-// This will not work.
-//provisioner.SetBridge(bridge);
 discordbot.setBridge(bridge);
 
 async function run() {
-    try {
-        await bridge.loadDatabases();
-    } catch (e) {
-        await discordstore.init();
-    }
+    await discordstore.init();
+    provisioner.setStore(discordstore.roomStore);
     const userSync = new UserSyncroniser(bridge, config, discordbot);
     bridge._clientFactory = clientFactory;
     await discordbot.ClientFactory.init();
-- 
GitLab