Skip to content
Extraits de code Groupes Projets
Sélectionner une révision Git
  • 367e76d235f5f37fa2d6aa305791d7387502c831
  • develop par défaut protégée
  • upgrade-appservice
  • baguette-custom-fixes
  • fix-discord-reply-edited
  • update-readme-badges
6 résultats

test_matrixroomhandler.ts

Blame
  • test_matrixroomhandler.ts 22,10 Kio
    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;
            });
        });
    });