From 0c7cc85184d9f90987000ffcddfa2b9581bb96cb Mon Sep 17 00:00:00 2001 From: Val Lorentz Date: Tue, 14 Mar 2023 17:25:26 +0100 Subject: [PATCH] Fix load of channels from user config Network.export() only writes the "type" key if it's a ChanType.QUERY; so the config on disk has no "type". This causes it to be undefined when loading, which breaks various other checks, and then drops it the next time the config is saved. --- server/client.ts | 16 ++++--- test/client.ts | 110 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 7 deletions(-) create mode 100644 test/client.ts diff --git a/server/client.ts b/server/client.ts index d5ffe84e..f09a5328 100644 --- a/server/client.ts +++ b/server/client.ts @@ -6,7 +6,7 @@ import crypto from "crypto"; import colors from "chalk"; import log from "./log"; -import Chan, {Channel, ChanType} from "./models/chan"; +import Chan, {ChanConfig, Channel, ChanType} from "./models/chan"; import Msg, {MessageType, UserInMessage} from "./models/msg"; import Config from "./config"; import {condensedTypes} from "../shared/irc"; @@ -251,11 +251,13 @@ class Client { let channels: Chan[] = []; if (Array.isArray(args.channels)) { - let badName = false; + let badChanConf = false; - args.channels.forEach((chan: Chan) => { - if (!chan.name) { - badName = true; + args.channels.forEach((chan: ChanConfig) => { + const type = ChanType[(chan.type || "channel").toUpperCase()]; + + if (!chan.name || !type) { + badChanConf = true; return; } @@ -263,13 +265,13 @@ class Client { client.createChannel({ name: chan.name, key: chan.key || "", - type: chan.type, + type: type, muted: chan.muted, }) ); }); - if (badName && client.name) { + if (badChanConf && client.name) { log.warn( "User '" + client.name + diff --git a/test/client.ts b/test/client.ts new file mode 100644 index 00000000..622f47df --- /dev/null +++ b/test/client.ts @@ -0,0 +1,110 @@ +import {expect} from "chai"; +import {NetworkConfig} from "../server/models/network"; +import {ChanConfig, ChanType} from "../server/models/chan"; +import ClientManager from "../server/clientManager"; +import Client from "../server/client"; +import log from "../server/log"; + +import sinon from "ts-sinon"; + +describe("Client", function () { + const commonNetworkConfig: NetworkConfig = { + uuid: "67363f03-d903-498b-8e52-031ebb912791", + awayMessage: "", + name: "Super Nice Network", + nick: "thelounge0001", + host: "example.org", + port: 6667, + tls: false, + userDisconnected: false, + rejectUnauthorized: true, + password: "", + username: "thelounge", + realname: "thelounge26", + leaveMessage: "", + sasl: "", + saslAccount: "", + saslPassword: "", + commands: [], + ignoreList: [], + proxyHost: "", + proxyPort: 1080, + proxyUsername: "", + proxyEnabled: false, + proxyPassword: "", + channels: [], + }; + let logWarnStub: sinon.SinonStub; + + before(function () { + logWarnStub = sinon.stub(log, "warn"); + }); + + after(function () { + logWarnStub.restore(); + }); + + it("should parse channel configuration", function () { + const manager = new ClientManager(); + const channel: ChanConfig = {name: "AAAA!", type: "query"}; + const networkConfig: NetworkConfig = { + ...commonNetworkConfig, + channels: [{name: "AAAA!", type: "query"}, {name: "#thelounge"}, {name: "&foobar"}], + }; + const client = new Client(manager, "test", { + log: false, + password: "foo", + sessions: {}, + clientSettings: {}, + networks: [networkConfig], + }); + + // The client would normally do it as part of client.connect(); + // but this avoids the need to mock the irc-framework connection + const network = client.networkFromConfig(networkConfig); + + sinon.assert.notCalled(logWarnStub); + + expect(network.channels[0].name).to.equal("Super Nice Network"); + expect(network.channels[0].type).to.equal(ChanType.LOBBY); + expect(network.channels[1].name).to.equal("AAAA!"); + expect(network.channels[1].type).to.equal(ChanType.QUERY); + expect(network.channels[2].name).to.equal("#thelounge"); + expect(network.channels[2].type).to.equal(ChanType.CHANNEL); + expect(network.channels[3].name).to.equal("&foobar"); + expect(network.channels[3].type).to.equal(ChanType.CHANNEL); + }); + + it("should ignore invalid channel types", function () { + const manager = new ClientManager(); + const channel: ChanConfig = {name: "AAAA!", type: "query"}; + const networkConfig: NetworkConfig = { + ...commonNetworkConfig, + channels: [ + {name: "AAAA!", type: "query"}, + {name: "#thelounge", type: "wrongtype"}, + {name: "&foobar"}, + ], + }; + const client = new Client(manager, "test", { + log: false, + password: "foo", + sessions: {}, + clientSettings: {}, + networks: [networkConfig], + }); + + // The client would normally do it as part of client.connect(); + // but this avoids the need to mock the irc-framework connection + const network = client.networkFromConfig(networkConfig); + + sinon.assert.calledOnce(logWarnStub); + + expect(network.channels[0].name).to.equal("Super Nice Network"); + expect(network.channels[0].type).to.equal(ChanType.LOBBY); + expect(network.channels[1].name).to.equal("AAAA!"); + expect(network.channels[1].type).to.equal(ChanType.QUERY); + expect(network.channels[2].name).to.equal("&foobar"); + expect(network.channels[2].type).to.equal(ChanType.CHANNEL); + }); +});