mirror of
https://github.com/thelounge/thelounge
synced 2024-11-25 05:20:21 +00:00
566 lines
17 KiB
TypeScript
566 lines
17 KiB
TypeScript
import {expect} from "chai";
|
|
import sinon from "ts-sinon";
|
|
import Chan from "../../server/models/chan";
|
|
import {ChanType} from "../../shared/types/chan";
|
|
import Msg from "../../server/models/msg";
|
|
import User from "../../server/models/user";
|
|
import Network from "../../server/models/network";
|
|
import Config from "../../server/config";
|
|
import STSPolicies from "../../server/plugins/sts";
|
|
import ClientCertificate from "../../server/plugins/clientCertificate";
|
|
|
|
describe("Network", function () {
|
|
let stsPoliciesRefreshStub: sinon.SinonStub<unknown[], void>;
|
|
|
|
before(function () {
|
|
stsPoliciesRefreshStub = sinon.stub(STSPolicies, "refresh");
|
|
});
|
|
|
|
after(function () {
|
|
stsPoliciesRefreshStub.restore();
|
|
});
|
|
|
|
describe("Network(attr)", function () {
|
|
it("should generate uuid (v4) for each network", function () {
|
|
const network1 = new Network();
|
|
const network2 = new Network();
|
|
|
|
expect(network1.uuid).to.have.lengthOf(36);
|
|
expect(network2.uuid).to.have.lengthOf(36);
|
|
expect(network1.uuid).to.not.equal(network2.uuid);
|
|
});
|
|
|
|
it("should keep the lobby at the top", function () {
|
|
const network = new Network({
|
|
name: "Super Nice Network",
|
|
channels: [
|
|
new Chan({name: "AAAA!", type: ChanType.QUERY}),
|
|
new Chan({name: "#thelounge"}),
|
|
new Chan({name: "&foobar"}),
|
|
],
|
|
});
|
|
network.channels.push(new Chan({name: "#swag"}));
|
|
|
|
expect(network.channels[0].name).to.equal("Super Nice Network");
|
|
expect(network.channels[0].type).to.equal(ChanType.LOBBY);
|
|
});
|
|
|
|
it("should maintain channel reference", function () {
|
|
const chan = new Chan({
|
|
name: "#506-bug-fix",
|
|
messages: [
|
|
new Msg({
|
|
text: "message in constructor",
|
|
}),
|
|
],
|
|
});
|
|
|
|
const network = new Network({
|
|
name: "networkName",
|
|
channels: [chan],
|
|
});
|
|
|
|
chan.messages.push(
|
|
new Msg({
|
|
text: "message in original instance",
|
|
})
|
|
);
|
|
|
|
network.channels[1].messages.push(
|
|
new Msg({
|
|
text: "message after network creation",
|
|
})
|
|
);
|
|
|
|
expect(network.channels[1].messages).to.have.lengthOf(3);
|
|
expect(network.channels[1].messages[0].text).to.equal("message in constructor");
|
|
expect(network.channels[1].messages[1].text).to.equal("message in original instance");
|
|
expect(network.channels[1].messages[2].text).to.equal("message after network creation");
|
|
});
|
|
});
|
|
|
|
describe("#export()", function () {
|
|
it("should produce an valid object", function () {
|
|
const network = new Network({
|
|
uuid: "hello world",
|
|
awayMessage: "I am away",
|
|
name: "networkName",
|
|
sasl: "plain",
|
|
saslAccount: "testaccount",
|
|
saslPassword: "testpassword",
|
|
channels: [
|
|
new Chan({name: "#thelounge", key: "", muted: false}),
|
|
new Chan({name: "&foobar", key: "", muted: false}),
|
|
new Chan({name: "#secret", key: "foo", muted: false}),
|
|
new Chan({name: "&secure", key: "bar", muted: true}),
|
|
new Chan({name: "Channel List", type: ChanType.SPECIAL}),
|
|
new Chan({name: "PrivateChat", type: ChanType.QUERY, muted: true}),
|
|
],
|
|
});
|
|
network.setNick("chillin`");
|
|
|
|
expect(network.export()).to.deep.equal({
|
|
uuid: "hello world",
|
|
awayMessage: "I am away",
|
|
name: "networkName",
|
|
host: "",
|
|
port: 6667,
|
|
tls: false,
|
|
userDisconnected: false,
|
|
rejectUnauthorized: false,
|
|
password: "",
|
|
username: "",
|
|
realname: "",
|
|
leaveMessage: "",
|
|
sasl: "plain",
|
|
saslAccount: "testaccount",
|
|
saslPassword: "testpassword",
|
|
commands: [],
|
|
nick: "chillin`",
|
|
proxyEnabled: false,
|
|
proxyHost: "",
|
|
proxyPort: 1080,
|
|
proxyPassword: "",
|
|
proxyUsername: "",
|
|
channels: [
|
|
{name: "#thelounge", key: "", muted: false},
|
|
{name: "&foobar", key: "", muted: false},
|
|
{name: "#secret", key: "foo", muted: false},
|
|
{name: "&secure", key: "bar", muted: true},
|
|
{name: "PrivateChat", type: "query", muted: true},
|
|
],
|
|
ignoreList: [],
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("#validate()", function () {
|
|
it("should set correct defaults", function () {
|
|
Config.values.defaults.nick = "";
|
|
|
|
const network = new Network({
|
|
host: "localhost",
|
|
});
|
|
|
|
expect(network.validate({} as any)).to.be.true;
|
|
expect(network.nick).to.equal("thelounge");
|
|
expect(network.username).to.equal("thelounge");
|
|
expect(network.realname).to.equal("thelounge");
|
|
expect(network.port).to.equal(6667);
|
|
|
|
const network2 = new Network({
|
|
host: "localhost",
|
|
nick: "@Invalid Nick?",
|
|
});
|
|
expect(network2.validate({} as any)).to.be.true;
|
|
expect(network2.username).to.equal("InvalidNick");
|
|
});
|
|
|
|
it("should enforce lockNetwork", function () {
|
|
Config.values.lockNetwork = true;
|
|
|
|
// Make sure we lock in private mode
|
|
Config.values.public = false;
|
|
|
|
const network = new Network({
|
|
host: "",
|
|
port: 1337,
|
|
tls: false,
|
|
rejectUnauthorized: false,
|
|
});
|
|
expect(network.validate({} as any)).to.be.true;
|
|
expect(network.host).to.equal("irc.example.com");
|
|
expect(network.port).to.equal(6697);
|
|
expect(network.tls).to.be.true;
|
|
expect(network.rejectUnauthorized).to.be.true;
|
|
|
|
// Make sure we lock in public mode (also resets public=true for other tests)
|
|
Config.values.public = true;
|
|
|
|
const network2 = new Network({
|
|
host: "some.fake.tld",
|
|
});
|
|
expect(network2.validate({} as any)).to.be.true;
|
|
expect(network2.host).to.equal("irc.example.com");
|
|
|
|
Config.values.lockNetwork = false;
|
|
});
|
|
|
|
it("realname should be set to nick only if realname is empty", function () {
|
|
const network = new Network({
|
|
host: "localhost",
|
|
nick: "dummy",
|
|
});
|
|
|
|
expect(network.validate({} as any)).to.be.true;
|
|
expect(network.nick).to.equal("dummy");
|
|
expect(network.realname).to.equal("dummy");
|
|
|
|
const network2 = new Network({
|
|
host: "localhost",
|
|
nick: "dummy",
|
|
realname: "notdummy",
|
|
});
|
|
|
|
expect(network2.validate({} as any)).to.be.true;
|
|
expect(network2.nick).to.equal("dummy");
|
|
expect(network2.realname).to.equal("notdummy");
|
|
});
|
|
|
|
it("should apply STS policies iff they match", function () {
|
|
const client = {idMsg: 1, emit() {}} as any;
|
|
STSPolicies.update("irc.example.com", 7000, 3600);
|
|
expect(STSPolicies.get("irc.example.com")).to.not.be.null;
|
|
|
|
let network = new Network({
|
|
host: "irc.example.com",
|
|
port: 1337,
|
|
tls: false,
|
|
});
|
|
|
|
expect(network.validate(client)).to.be.true;
|
|
expect(network.port).to.equal(7000);
|
|
expect(network.tls).to.be.true;
|
|
|
|
network = new Network({
|
|
host: "irc2.example.com",
|
|
port: 1337,
|
|
tls: false,
|
|
});
|
|
|
|
expect(network.validate(client)).to.be.true;
|
|
expect(network.port).to.equal(1337);
|
|
expect(network.tls).to.be.false;
|
|
|
|
STSPolicies.update("irc.example.com", 7000, 0); // Cleanup
|
|
expect(STSPolicies.get("irc.example.com")).to.be.null;
|
|
});
|
|
|
|
it("should not remove client certs if TLS is disabled", function () {
|
|
Config.values.public = false;
|
|
|
|
const client = {idMsg: 1, emit() {}, messageStorage: []};
|
|
|
|
const network = new Network({host: "irc.example.com", sasl: "external"});
|
|
(network as any).createIrcFramework(client);
|
|
expect(network.irc).to.not.be.null;
|
|
|
|
const client_cert = network.irc?.options?.client_certificate;
|
|
expect(client_cert).to.not.be.null;
|
|
expect(ClientCertificate.get(network.uuid)).to.deep.equal(client_cert);
|
|
|
|
expect(network.validate(client as any)).to.be.true;
|
|
|
|
expect(ClientCertificate.get(network.uuid)).to.deep.equal(client_cert); // Should be unchanged
|
|
|
|
ClientCertificate.remove(network.uuid);
|
|
Config.values.public = true;
|
|
});
|
|
|
|
it("should not remove client certs if there is a STS policy", function () {
|
|
Config.values.public = false;
|
|
|
|
const client = {idMsg: 1, emit() {}, messageStorage: []};
|
|
STSPolicies.update("irc.example.com", 7000, 3600);
|
|
expect(STSPolicies.get("irc.example.com")).to.not.be.null;
|
|
|
|
const network = new Network({host: "irc.example.com", sasl: "external"});
|
|
(network as any).createIrcFramework(client);
|
|
expect(network.irc).to.not.be.null;
|
|
|
|
const client_cert = network.irc?.options?.client_certificate;
|
|
expect(client_cert).to.not.be.null;
|
|
expect(ClientCertificate.get(network.uuid)).to.deep.equal(client_cert);
|
|
|
|
expect(network.validate(client as any)).to.be.true;
|
|
|
|
expect(ClientCertificate.get(network.uuid)).to.deep.equal(client_cert); // Should be unchanged
|
|
|
|
ClientCertificate.remove(network.uuid);
|
|
Config.values.public = true;
|
|
|
|
STSPolicies.update("irc.example.com", 7000, 0); // Cleanup
|
|
expect(STSPolicies.get("irc.example.com")).to.be.null;
|
|
});
|
|
});
|
|
|
|
describe("#createIrcFramework(client)", function () {
|
|
it("should generate and use a client certificate when using SASL external", function () {
|
|
Config.values.public = false;
|
|
|
|
const client = {idMsg: 1, emit() {}};
|
|
STSPolicies.update("irc.example.com", 7000, 3600);
|
|
expect(STSPolicies.get("irc.example.com")).to.not.be.null;
|
|
|
|
let network: any = new Network({host: "irc.example.com"});
|
|
network.createIrcFramework(client);
|
|
expect(network.irc).to.not.be.null;
|
|
expect(network.irc.options.client_certificate).to.be.null;
|
|
|
|
network = new Network({host: "irc.example.com", sasl: "external"});
|
|
network.createIrcFramework(client);
|
|
expect(network.irc).to.not.be.null;
|
|
expect(network.irc.options.client_certificate).to.not.be.null;
|
|
|
|
ClientCertificate.remove(network.uuid);
|
|
Config.values.public = true;
|
|
|
|
STSPolicies.update("irc.example.com", 7000, 0); // Cleanup
|
|
expect(STSPolicies.get("irc.example.com")).to.be.null;
|
|
});
|
|
});
|
|
|
|
describe("#edit(client, args)", function () {
|
|
it("should enforce correct types", function () {
|
|
let saveCalled = false;
|
|
let nameEmitCalled = false;
|
|
|
|
const network = new Network();
|
|
(network as any).edit(
|
|
{
|
|
emit(name, data) {
|
|
if (name === "network:name") {
|
|
nameEmitCalled = true;
|
|
expect(data.uuid).to.equal(network.uuid);
|
|
expect(data.name).to.equal("Lounge Test Network");
|
|
}
|
|
},
|
|
save() {
|
|
saveCalled = true;
|
|
},
|
|
},
|
|
{
|
|
nick: "newNick",
|
|
host: "new.tld",
|
|
name: "Lounge Test Network",
|
|
port: "1337",
|
|
tls: undefined,
|
|
rejectUnauthorized: undefined,
|
|
username: 1234,
|
|
password: 4567,
|
|
realname: 8901,
|
|
sasl: "something",
|
|
saslAccount: 1337,
|
|
saslPassword: 1337,
|
|
commands: "/command 1 2 3\r\n/ping HELLO\r\r\r\r/whois test\r\n\r\n",
|
|
ip: "newIp",
|
|
hostname: "newHostname",
|
|
uuid: "newuuid",
|
|
}
|
|
);
|
|
|
|
expect(saveCalled).to.be.true;
|
|
expect(nameEmitCalled).to.be.true;
|
|
expect(network.uuid).to.not.equal("newuuid");
|
|
|
|
// @ts-expect-error Property 'ip' does not exist on type 'Network'.
|
|
expect(network.ip).to.be.undefined;
|
|
// @ts-expect-error Property 'hostname' does not exist on type 'Network'.
|
|
expect(network.hostname).to.be.undefined;
|
|
|
|
expect(network.name).to.equal("Lounge Test Network");
|
|
expect(network.channels[0].name).to.equal("Lounge Test Network");
|
|
|
|
expect(network.nick).to.equal("newNick");
|
|
expect(network.host).to.equal("new.tld");
|
|
expect(network.port).to.equal(1337);
|
|
expect(network.tls).to.be.false;
|
|
expect(network.rejectUnauthorized).to.be.false;
|
|
expect(network.username).to.equal("1234");
|
|
expect(network.password).to.equal("4567");
|
|
expect(network.realname).to.equal("8901");
|
|
expect(network.sasl).to.equal("");
|
|
expect(network.saslAccount).to.equal("1337");
|
|
expect(network.saslPassword).to.equal("1337");
|
|
expect(network.commands).to.deep.equal([
|
|
"/command 1 2 3",
|
|
"/ping HELLO",
|
|
"/whois test",
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe("#getFilteredClone(lastActiveChannel, lastMessage)", function () {
|
|
it("should filter channels", function () {
|
|
const chan = new Chan();
|
|
chan.setUser(new User({nick: "test"}));
|
|
|
|
const network = new Network({
|
|
channels: [chan],
|
|
});
|
|
|
|
expect(network.channels[0].users).to.be.empty;
|
|
});
|
|
|
|
it("should keep necessary properties", function () {
|
|
const network = new Network();
|
|
const clone = network.getFilteredClone();
|
|
|
|
expect(clone)
|
|
.to.be.an("object")
|
|
.that.has.all.keys("channels", "status", "nick", "name", "serverOptions", "uuid");
|
|
|
|
expect(clone.status).to.be.an("object").that.has.all.keys("connected", "secure");
|
|
});
|
|
});
|
|
|
|
describe("#addChannel(newChan)", function () {
|
|
it("should add channel", function () {
|
|
const chan = new Chan({name: "#thelounge"});
|
|
|
|
const network = new Network({
|
|
channels: [chan],
|
|
});
|
|
// Lobby and initial channel
|
|
expect(network.channels.length).to.equal(2);
|
|
|
|
const newChan = new Chan({name: "#foo"});
|
|
network.addChannel(newChan);
|
|
|
|
expect(network.channels.length).to.equal(3);
|
|
});
|
|
|
|
it("should add channel alphabetically", function () {
|
|
const chan1 = new Chan({name: "#abc"});
|
|
const chan2 = new Chan({name: "#thelounge"});
|
|
const chan3 = new Chan({name: "#zero"});
|
|
|
|
const network = new Network({
|
|
channels: [chan1, chan2, chan3],
|
|
name: "foo",
|
|
});
|
|
|
|
const newChan = new Chan({name: "#foo"});
|
|
network.addChannel(newChan);
|
|
|
|
expect(network.channels[0].name).to.equal("foo");
|
|
expect(network.channels[1]).to.equal(chan1);
|
|
expect(network.channels[2]).to.equal(newChan);
|
|
expect(network.channels[3]).to.equal(chan2);
|
|
expect(network.channels[4]).to.equal(chan3);
|
|
});
|
|
|
|
it("should sort case-insensitively", function () {
|
|
const chan1 = new Chan({name: "#abc"});
|
|
const chan2 = new Chan({name: "#THELOUNGE"});
|
|
|
|
const network = new Network({
|
|
channels: [chan1, chan2],
|
|
});
|
|
|
|
const newChan = new Chan({name: "#foo"});
|
|
network.addChannel(newChan);
|
|
|
|
expect(network.channels[1]).to.equal(chan1);
|
|
expect(network.channels[2]).to.equal(newChan);
|
|
expect(network.channels[3]).to.equal(chan2);
|
|
});
|
|
|
|
it("should sort users separately from channels", function () {
|
|
const chan1 = new Chan({name: "#abc"});
|
|
const chan2 = new Chan({name: "#THELOUNGE"});
|
|
|
|
const network = new Network({
|
|
channels: [chan1, chan2],
|
|
});
|
|
|
|
const newUser = new Chan({name: "mcinkay", type: ChanType.QUERY});
|
|
network.addChannel(newUser);
|
|
|
|
expect(network.channels[1]).to.equal(chan1);
|
|
expect(network.channels[2]).to.equal(chan2);
|
|
expect(network.channels[3]).to.equal(newUser);
|
|
});
|
|
|
|
it("should sort users alphabetically", function () {
|
|
const chan1 = new Chan({name: "#abc"});
|
|
const chan2 = new Chan({name: "#THELOUNGE"});
|
|
const user1 = new Chan({name: "astorije", type: ChanType.QUERY});
|
|
const user2 = new Chan({name: "xpaw", type: ChanType.QUERY});
|
|
|
|
const network = new Network({
|
|
channels: [chan1, chan2, user1, user2],
|
|
});
|
|
|
|
const newUser = new Chan({name: "mcinkay", type: ChanType.QUERY});
|
|
network.addChannel(newUser);
|
|
|
|
expect(network.channels[1]).to.equal(chan1);
|
|
expect(network.channels[2]).to.equal(chan2);
|
|
expect(network.channels[3]).to.equal(user1);
|
|
expect(network.channels[4]).to.equal(newUser);
|
|
expect(network.channels[5]).to.equal(user2);
|
|
});
|
|
|
|
it("should not sort special channels", function () {
|
|
const chan1 = new Chan({name: "#abc"});
|
|
const chan2 = new Chan({name: "#THELOUNGE"});
|
|
const user1 = new Chan({name: "astorije", type: ChanType.QUERY});
|
|
const user2 = new Chan({name: "xpaw", type: ChanType.QUERY});
|
|
|
|
const network = new Network({
|
|
channels: [chan1, chan2, user1, user2],
|
|
});
|
|
|
|
const newBanlist = new Chan({name: "Banlist for #THELOUNGE", type: ChanType.SPECIAL});
|
|
network.addChannel(newBanlist);
|
|
|
|
expect(network.channels[1]).to.equal(chan1);
|
|
expect(network.channels[2]).to.equal(chan2);
|
|
expect(network.channels[3]).to.equal(user1);
|
|
expect(network.channels[4]).to.equal(user2);
|
|
expect(network.channels[5]).to.equal(newBanlist);
|
|
});
|
|
|
|
it("should not compare against special channels", function () {
|
|
const chan1 = new Chan({name: "#abc"});
|
|
const chan2 = new Chan({name: "#THELOUNGE"});
|
|
const user1 = new Chan({name: "astorije", type: ChanType.QUERY});
|
|
|
|
const network = new Network({
|
|
channels: [chan1, chan2, user1],
|
|
});
|
|
|
|
const newBanlist = new Chan({name: "Banlist for #THELOUNGE", type: ChanType.SPECIAL});
|
|
network.addChannel(newBanlist);
|
|
const newUser = new Chan({name: "mcinkay", type: ChanType.QUERY});
|
|
network.addChannel(newUser);
|
|
|
|
expect(network.channels[1]).to.equal(chan1);
|
|
expect(network.channels[2]).to.equal(chan2);
|
|
expect(network.channels[3]).to.equal(user1);
|
|
expect(network.channels[4]).to.equal(newUser);
|
|
expect(network.channels[5]).to.equal(newBanlist);
|
|
});
|
|
|
|
it("should insert before first special channel", function () {
|
|
const banlist = new Chan({name: "Banlist for #THELOUNGE", type: ChanType.SPECIAL});
|
|
const chan1 = new Chan({name: "#thelounge"});
|
|
const user1 = new Chan({name: "astorije", type: ChanType.QUERY});
|
|
|
|
const network = new Network({
|
|
channels: [banlist, chan1, user1],
|
|
});
|
|
|
|
const newChan = new Chan({name: "#foo"});
|
|
network.addChannel(newChan);
|
|
|
|
expect(network.channels[1]).to.equal(newChan);
|
|
expect(network.channels[2]).to.equal(banlist);
|
|
expect(network.channels[3]).to.equal(chan1);
|
|
expect(network.channels[4]).to.equal(user1);
|
|
});
|
|
|
|
it("should never add something in front of the lobby", function () {
|
|
const network = new Network({
|
|
name: "foo",
|
|
channels: [],
|
|
});
|
|
|
|
const newUser = new Chan({name: "astorije"});
|
|
network.addChannel(newUser);
|
|
|
|
expect(network.channels[1]).to.equal(newUser);
|
|
});
|
|
});
|
|
});
|