irc-dcc
Version:
DCC Support for Node-IRC
716 lines (711 loc) • 27 kB
JavaScript
/* global afterEach, before, after, it, describe */
// Copyright(C) 2016 Alexander Walters
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.If not, see <http://www.gnu.org/licenses/>.
var assert = require("assert");
var simple = require("simple-mock");
var DCC = require("../lib/dcc");
var netUtil = require("../lib/net-util");
var ip = require("ip");
var net = require("net");
describe("DCC.parseDCC()", function () {
it("should return null", function () {
var args = DCC.parseDCC("This is not a dcc message");
assert.equal(args, null);
});
it("should return a send", function () {
var args = DCC.parseDCC("DCC SEND foo_bar.pdf 3232235876 6500 95871");
var expected = {
type: "send",
long: 3232235876,
host: "192.168.1.100",
port: 6500,
filename: "foo_bar.pdf",
length: 95871
};
assert.deepEqual(args, expected);
});
it("should return a chat", function () {
var args = DCC.parseDCC("DCC CHAT chat 3232235876 6500");
var expected = {
type: "chat",
long: 3232235876,
host: "192.168.1.100",
port: 6500,
};
assert.deepEqual(args, expected);
});
it("should return an accept", function () {
var args = DCC.parseDCC("DCC ACCEPT foo_bar.pdf 6500 31565");
var expected = {
type: "accept",
port: 6500,
filename: "foo_bar.pdf",
position: 31565
};
assert.deepEqual(args, expected);
});
it("should return a resume", function () {
var args = DCC.parseDCC("DCC RESUME foo_bar.pdf 6500 31565");
var expected = {
type: "resume",
port: 6500,
filename: "foo_bar.pdf",
position: 31565
};
assert.deepEqual(args, expected);
});
});
describe("DCC", () => {
describe(".acceptAndConnectFile", () => {
var fakeCon;
before(() => {
simple.mock(net, "connect");
fakeCon = {
on: simple.stub(),
write: simple.stub()
};
net.connect.loop = false;
fakeCon.on.loop = false;
fakeCon.write.loop = false;
});
afterEach(() => {
net.connect.reset();
fakeCon.on.reset();
fakeCon.write.reset();
});
after(() => {
simple.restore();
});
it("should callback with filename and connection", (done) => {
var expected = [null, "filename", fakeCon];
net.connect.returnWith(fakeCon);
fakeCon.on.callbackWith()
var opt = {
host: "192.168.1.100",
port: 2000,
localAddress: "0.0.0.0"
};
DCC.acceptAndConnectFile(opt, "filename", (err, filename, connection) => {
assert.deepEqual([err, filename, connection], expected);
done();
});
});
it("should send data length", (done) => {
var buf = new Buffer(4);
buf.writeUInt32BE(4, 0);
var expected = [buf];
net.connect.returnWith(fakeCon);
fakeCon.on.callbackWith().callbackWith("data");
var opt = {
host: "192.168.1.100",
port: 2000,
localAddress: "0.0.0.0"
};
DCC.acceptAndConnectFile(opt, "filename", done);
assert.deepEqual(fakeCon.write.lastCall.args, expected);
});
});
describe("(constructor)", () => {
var fakeClient;
before(() => {
fakeClient = {
opt: {},
addListener: simple.stub(),
removeListener: simple.stub(),
emit: simple.stub(),
ctcp: simple.stub(),
once: simple.stub()
};
});
afterEach(() => {
fakeClient.addListener.reset();
fakeClient.removeListener.reset();
fakeClient.emit.reset();
fakeClient.ctcp.reset();
fakeClient.once.reset();
});
after(() => {
simple.restore();
});
describe("Client.addListener", () => {
it("should addListener once", () => {
new DCC(fakeClient);
assert.equal(fakeClient.addListener.callCount, 1);
});
it("should addListener ctcp-privmsg", () => {
new DCC(fakeClient);
assert.equal(fakeClient.addListener.lastCall.args[0], "ctcp-privmsg");
});
});
describe("Client.emit", () => {
it("should emit DCC SEND", () => {
new DCC(fakeClient);
var hnd = fakeClient.addListener.lastCall.args[1];
var expected = [
"dcc-send",
"user",
{
type: "send",
long: 3232235876,
host: "192.168.1.100",
port: 6500,
filename: "foo_bar.pdf",
length: 95871
},
"message"
];
hnd("user", null, "DCC SEND foo_bar.pdf 3232235876 6500 95871", "message");
assert.deepEqual(fakeClient.emit.lastCall.args, expected);
});
it("should not emit anything", () => {
new DCC(fakeClient);
var hnd = fakeClient.addListener.lastCall.args[1];
hnd("user", null, "PING 12345678", "message");
assert.equal(fakeClient.emit.callCount, 0);
});
});
describe("Attributes", () => {
it("should set the ports to 0 (automatic)", () => {
var dcc = new DCC(fakeClient);
assert.equal(dcc.ports, 0);
});
it("should set the ports to [2000, 2020] (from options)", () => {
var dcc = new DCC(fakeClient, { ports: [2000, 2020] });
assert.deepEqual(dcc.ports, [2000, 2020]);
});
it("should set localAddress to 192.168.1.100 (automatic)", () => {
simple.mock(ip, "address").returnWith("192.168.1.100");
var dcc = new DCC(fakeClient);
assert.equal(dcc.localAddress, "192.168.1.100");
});
it("should set localAddress to 192.168.1.100 (from client)", () => {
var temp = {
opt: { localAddress: "192.168.1.100" },
addListener: simple.stub(),
emit: simple.stub(),
};
simple.mock(ip, "address").returnWith("INVALID");
var dcc = new DCC(temp);
assert.equal(dcc.localAddress, "192.168.1.100");
});
it("should set localAddress to 192.168.1.100 (from options)", () => {
simple.mock(ip, "address").returnWith("INVALID");
var dcc = new DCC(fakeClient, { localAddress: "192.168.1.100" });
assert.equal(dcc.localAddress, "192.168.1.100");
});
});
});
describe("#getIP", () => {
var fakeClient;
before(() => {
fakeClient = {
opt: {},
addListener: simple.stub(),
removeListener: simple.stub(),
emit: simple.stub(),
ctcp: simple.stub(),
once: simple.stub()
};
});
after(() => {
simple.restore();
});
it("should callback with 192.168.1.100", (done) => {
var dcc = new DCC(fakeClient, {});
simple.mock(netUtil, "getMyIP").callbackWith("192.168.1.100");
dcc.getIP((host) => {
assert.equal(host, "192.168.1.100");
done();
});
});
});
describe("#getPort", () => {
var fakeClient;
before(() => {
fakeClient = {
opt: {},
addListener: simple.stub(),
removeListener: simple.stub(),
emit: simple.stub(),
ctcp: simple.stub(),
once: simple.stub()
};
});
after(() => {
simple.restore();
});
it("should callback with 0 (options.ports: 0)", (done) => {
var dcc = new DCC(fakeClient, {ports: 0});
simple.mock(netUtil, "getUnusedPort").callbackWith(99999);
dcc.getPort((port) => {
assert.equal(port, 0);
done();
});
});
it("should callback with 2000", (done) => {
var dcc = new DCC(fakeClient, { ports: [2000, 2020] });
simple.mock(netUtil, "getUnusedPort").callbackWith(2000);
dcc.getPort((port) => {
assert.equal(port, 2000);
done();
});
});
});
describe("#getPortIP", () => {
var fakeClient;
before(() => {
fakeClient = {
opt: {},
addListener: simple.stub(),
removeListener: simple.stub(),
emit: simple.stub(),
ctcp: simple.stub(),
once: simple.stub()
};
});
after(() => {
simple.restore();
});
it("should callback with 192.168.1.100:2000", (done) => {
var dcc = new DCC(fakeClient, { ports: [2000, 2020] });
simple.mock(netUtil, "getUnusedPort").callbackWith(2000);
simple.mock(netUtil, "getMyIP").callbackWith("192.168.1.100");
dcc.getPortIP((details) => {
var expected = {
host: "192.168.1.100",
long: 3232235876,
port: 2000
};
assert.deepEqual(details, expected);
done();
});
});
});
describe("#acceptFile", () => {
var fakeClient;
before(() => {
simple.mock(DCC, "acceptAndConnectFile");
fakeClient = {
opt: {},
addListener: simple.stub(),
emit: simple.stub(),
ctcp: simple.stub(),
once: simple.stub()
};
DCC.acceptAndConnectFile.loop = false;
fakeClient.addListener.loop = false;
fakeClient.emit.loop = false;
fakeClient.ctcp.loop = false;
fakeClient.once.loop = false;
});
afterEach(() => {
DCC.acceptAndConnectFile.reset();
fakeClient.addListener.reset();
fakeClient.emit.reset();
fakeClient.ctcp.reset();
fakeClient.once.reset();
});
after(() => {
simple.restore();
});
it("should call acceptAndConnectFile (no position)", () => {
var fakeCB = () => { };
var expected = [
{
host: "192.168.1.100",
port: 2000,
localAddress: "0.0.0.0"
},
"filename",
fakeCB
];
var dcc = new DCC(fakeClient, {localAddress: "0.0.0.0"});
dcc.acceptFile("nick", "192.168.1.100", 2000, "filename", 1234567, fakeCB);
assert.deepEqual(DCC.acceptAndConnectFile.lastCall.args, expected);
});
it("should call acceptAndConnectFile (with position)", () => {
var fakeCB = () => { };
var expected = [
{
host: "192.168.1.100",
port: 2000,
localAddress: "0.0.0.0"
},
"filename",
fakeCB
];
fakeClient.once.callbackWith("nick", {filename: "filename", port: 2000});
var dcc = new DCC(fakeClient, { localAddress: "0.0.0.0" });
dcc.acceptFile("nick", "192.168.1.100", 2000, "filename", 1234567, 1234, fakeCB);
assert.deepEqual(DCC.acceptAndConnectFile.lastCall.args, expected);
});
it("should not call acceptAndConnectFile (with position)", () => {
var fakeCB = () => { };
var expected = [
{
host: "192.168.1.100",
port: 2000,
localAddress: "0.0.0.0"
},
"filename",
fakeCB
];
fakeClient.once.callbackWith("nick", { filename: "filename", port: 9999 });
var dcc = new DCC(fakeClient, { localAddress: "0.0.0.0" });
dcc.acceptFile("nick", "192.168.1.100", 2000, "filename", 1234567, 1234, fakeCB);
assert(!DCC.acceptAndConnectFile.called);
});
it("should call irc.Client.ctcp (with position)", () => {
var fakeCB = () => { };
var expected = ["nick", "privmsg", "DCC RESUME filename 2000 1234"];
var dcc = new DCC(fakeClient, { localAddress: "0.0.0.0" });
dcc.acceptFile("nick", "192.168.1.100", 2000, "filename", 1234567, 1234, fakeCB);
assert.deepEqual(fakeClient.ctcp.lastCall.args, expected);
});
});
describe("#sendFile", () => {
var fakeClient;
var fakeCon;
var fakeServer;
before(() => {
simple.mock(net, "createServer");
simple.mock(DCC.prototype, "getPortIP");
fakeClient = {
opt: {},
addListener: simple.stub(),
removeListener: simple.stub(),
emit: simple.stub(),
ctcp: simple.stub(),
once: simple.stub()
};
fakeServer = {
close: simple.stub(),
listen: simple.stub(),
address: simple.stub(),
on: simple.stub()
};
fakeCon = {
addListener: simple.stub(),
};
net.createServer.loop = false;
DCC.prototype.getPortIP.loop = false;
fakeClient.addListener.loop = false;
fakeClient.removeListener.loop = false;
fakeClient.emit.loop = false;
fakeClient.ctcp.loop = false;
fakeClient.once.loop = false;
fakeServer.close.loop = false;
fakeServer.listen.loop = false;
fakeServer.address.loop = false;
fakeServer.on.loop = false;
fakeCon.addListener.loop = false;
});
afterEach(() => {
net.createServer.reset();
DCC.prototype.getPortIP.reset();
fakeClient.addListener.reset();
fakeClient.removeListener.reset();
fakeClient.emit.reset();
fakeClient.ctcp.reset();
fakeClient.once.reset();
fakeServer.close.reset();
fakeServer.listen.reset();
fakeServer.address.reset();
fakeServer.on.reset();
fakeCon.addListener.reset();
});
after(() => {
simple.restore();
});
it("should callback with a fake connection", (done) => {
DCC.prototype.getPortIP.callbackWith({
host: "192.168.1.100",
long: 3232235876,
port: 2000
});
net.createServer.returnWith(fakeServer);
fakeServer.on.callbackWith(fakeCon);
var dcc = new DCC(fakeClient, {});
dcc.sendFile("to", "filename", 0, (err, con) => {
assert.equal(fakeCon, con);
done();
});
});
it("should remove listener", (done) => {
DCC.prototype.getPortIP.callbackWith({
host: "192.168.1.100",
long: 3232235876,
port: 2000
});
net.createServer.returnWith(fakeServer);
fakeServer.on.callbackWith(fakeCon);
var dcc = new DCC(fakeClient, {});
dcc.sendFile("to", "filename", 0, () => { done(); });
assert(fakeClient.removeListener.called);
});
it("should close the server", (done) => {
DCC.prototype.getPortIP.callbackWith({
host: "192.168.1.100",
long: 3232235876,
port: 2000
});
net.createServer.returnWith(fakeServer);
fakeServer.on.callbackWith(fakeCon);
var dcc = new DCC(fakeClient, {});
dcc.sendFile("to", "filename", 0, () => { done(); });
assert(fakeServer.close.called);
});
it("should send a DCC SEND message", (done) => {
DCC.prototype.getPortIP.callbackWith({
host: "192.168.1.100",
long: 3232235876,
port: 2000
});
net.createServer.returnWith(fakeServer);
fakeServer.on.callbackWith(fakeCon);
fakeServer.address.returnWith({ port: 2000 });
fakeServer.listen.callbackWith();
var dcc = new DCC(fakeClient, {});
dcc.sendFile("to", "filename", 0, () => { done(); });
assert.deepEqual(
fakeClient.ctcp.lastCall.args,
["to", "privmsg", "DCC SEND filename 3232235876 2000 0"]
);
});
it("should callback with pos == 0", (done) => {
DCC.prototype.getPortIP.callbackWith({
host: "192.168.1.100",
long: 3232235876,
port: 2000
});
net.createServer.returnWith(fakeServer);
fakeServer.on.callbackWith(fakeCon);
fakeServer.address.returnWith({ port: 2000 });
fakeServer.listen.callbackWith();
var dcc = new DCC(fakeClient, {});
dcc.sendFile("to", "filename", 0, (err, con, pos) => {
assert.equal(pos, 0);
done();
});
});
it("should not resume", (done) => {
DCC.prototype.getPortIP.callbackWith({
host: "192.168.1.100",
long: 3232235876,
port: 2000
});
net.createServer.returnWith(fakeServer);
fakeServer.address.returnWith({ port: 2000 });
fakeServer.listen.callbackWith();
fakeClient.once.callbackWith(
"to",
{
filename: "filename2",
port: 2000,
position: 50
}
);
fakeServer.on.callbackWith(fakeCon);
var dcc = new DCC(fakeClient, {});
dcc.sendFile("to", "filename", 0, () => {
done();
});
assert.deepEqual(
fakeClient.ctcp.lastCall.args,
["to", "privmsg", "DCC SEND filename 3232235876 2000 0"]
);
});
it("should resume", (done) => {
DCC.prototype.getPortIP.callbackWith({
host: "192.168.1.100",
long: 3232235876,
port: 2000
});
net.createServer.returnWith(fakeServer);
fakeServer.address.returnWith({ port: 2000 });
fakeServer.listen.callbackWith();
fakeClient.once.callbackWith(
"to",
{
filename: "filename",
port: 2000,
position: 50
}
);
fakeServer.on.callbackWith(fakeCon);
var dcc = new DCC(fakeClient, {});
dcc.sendFile("to", "filename", 0, () => {
done();
});
assert.deepEqual(
fakeClient.ctcp.lastCall.args,
["to", "privmsg", "DCC ACCEPT filename 2000 50"]
);
});
it("should callback with pos == 50", (done) => {
DCC.prototype.getPortIP.callbackWith({
host: "192.168.1.100",
long: 3232235876,
port: 2000
});
net.createServer.returnWith(fakeServer);
fakeServer.address.returnWith({ port: 2000 });
fakeServer.listen.callbackWith();
fakeClient.once.callbackWith(
"to",
{
filename: "filename",
port: 2000,
position: 50
}
);
fakeServer.on.callbackWith(fakeCon);
var dcc = new DCC(fakeClient, {});
dcc.sendFile("to", "filename", 0, (err, con, pos) => {
assert.equal(pos, 50);
done();
});
});
});
describe("#acceptChat", () => {
var fakeClient;
var fakeCon;
before(() => {
fakeCon = {
on: simple.stub(),
addListener: simple.stub()
};
fakeClient = {
opt: {},
addListener: simple.stub(),
emit: simple.stub(),
};
simple.mock(net, "connect");
net.createServer.loop = false;
fakeCon.addListener.loop = false;
fakeCon.on.loop = false;
});
afterEach(() => {
net.connect.reset();
fakeCon.addListener.reset();
fakeCon.on.reset();
});
after(() => {
simple.restore();
});
it("should attempt to connect", () => {
var dcc = new DCC(fakeClient, { localAddress: "192.168.1.50" });
dcc.acceptChat("192.168.1.100", 1234, () => { });
var expected = {
host: "192.168.1.100",
port: 1234,
localAddress: "192.168.1.50"
};
assert.deepEqual(expected, net.connect.lastCall.args[0]);
});
it("should callback with Chat of con", (done) => {
net.connect.returnWith(fakeCon);
fakeCon.on.callbackWith();
var dcc = new DCC(fakeClient, { localAddress: "192.168.1.50" });
dcc.acceptChat("192.168.1.100", 1234, (err, chat) => {
assert.equal(chat.connection, fakeCon);
done();
});
});
});
describe("#sendChat", () => {
var fakeClient;
var fakeCon;
var fakeServer;
before(() => {
simple.mock(net, "createServer");
simple.mock(DCC.prototype, "getPortIP");
fakeClient = {
opt: {},
addListener: simple.stub(),
emit: simple.stub(),
ctcp: simple.stub(),
};
fakeCon = {
on: simple.stub(),
addListener: simple.stub()
};
fakeServer = {
listen: simple.stub(),
address: simple.stub(),
on: simple.stub(),
close: simple.stub()
};
net.createServer.loop = false;
DCC.prototype.getPortIP.loop = false;
fakeClient.ctcp.loop = false;
fakeServer.listen.loop = false;
fakeServer.address.loop = false;
fakeServer.on.loop = false;
});
afterEach(() => {
net.createServer.reset();
DCC.prototype.getPortIP.reset();
fakeClient.ctcp.reset();
fakeServer.listen.reset();
fakeServer.address.reset();
fakeServer.on.reset();
});
after(() => {
simple.restore();
});
it("should send a DCC CHAT", (done) => {
var expected = ["to", "privmsg", "DCC CHAT chat 3232235876 2000"];
DCC.prototype.getPortIP.callbackWith({
host: "192.168.1.100",
long: 3232235876,
port: 2000
});
net.createServer.returnWith(fakeServer);
fakeServer.listen.callbackWith();
fakeServer.address.returnWith({ port: 2000 });
fakeServer.on.callbackWith(fakeCon);
var dcc = new DCC(fakeClient, {});
dcc.sendChat("to", () => { done(); });
assert.deepEqual(fakeClient.ctcp.lastCall.args, expected);
});
it("should close the server", (done) => {
DCC.prototype.getPortIP.callbackWith({
host: "192.168.1.100",
long: 3232235876,
port: 2000
});
net.createServer.returnWith(fakeServer);
fakeServer.on.callbackWith(fakeCon);
var dcc = new DCC(fakeClient, {});
dcc.sendChat("to", () => { done(); });
assert(fakeServer.close.called);
});
it("should callback with Chat of con", (done) => {
DCC.prototype.getPortIP.callbackWith({
host: "192.168.1.100",
long: 3232235876,
port: 2000
});
net.createServer.returnWith(fakeServer);
fakeServer.on.callbackWith(fakeCon);
var dcc = new DCC(fakeClient, {});
dcc.sendChat("to", (err, chat) => {
assert.equal(chat.connection, fakeCon);
done();
});
});
});
});