thelounge/test/models/network.ts
Reto Brunner 30e9f45fac Use nick as a realname fallback
Currently the realname is set to an advertisement if it isn't explicitly
set by the user.
Some clients started to show the realname as a display name in their
UI, which makes this tedious as you'll end up with gazillion "The Lounge
User" entries.

To avoid this, set the realname to the nick on first connect, so that
it is useful.
Note that this isn't done on nick changes, but only on the initial
connect step.

Fixes: https://github.com/thelounge/thelounge/issues/4527
2022-08-28 11:21:54 +02:00

567 lines
17 KiB
TypeScript

/* eslint-disable @typescript-eslint/no-empty-function */
import {expect} from "chai";
import sinon from "ts-sinon";
import Chan, {ChanType} from "../../server/models/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("lobby should be 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);
});
});
});