diff --git a/test/host/BaseInstancePlugin.js b/test/host/BaseInstancePlugin.js index 10808b40..9e7dbd30 100644 --- a/test/host/BaseInstancePlugin.js +++ b/test/host/BaseInstancePlugin.js @@ -66,6 +66,12 @@ describe("host/src/BaseInstancePlugin", function() { await assert.rejects(instancePlugin.sendRcon("a"), new Error("ref")); }); }); + describe("sendUdp", function() { + it("should send message to the instance", async function() { + await instancePlugin.sendUdp("a"); + assert.deepEqual(instancePlugin.instance.server.udpMessages, ["a"]); + }); + }); describe("sendOrderedRcon", function() { it("should send commands in order", async function() { instancePlugin.instance.server.rconCommandResults.set("a", { time: 100, response: "a" }); diff --git a/test/host/Instance.js b/test/host/Instance.js index f396c105..9d4562a5 100644 --- a/test/host/Instance.js +++ b/test/host/Instance.js @@ -240,6 +240,18 @@ describe("class Instance", function() { }); }); + describe(".sendUdp()", function() { + it("should throw when Lua UDP is disabled", async function() { + await assert.rejects(instance.sendUdp("data"), /Lua UDP is disabled/); + assert.deepEqual(instance.server.udpMessages, []); + }); + it("should send to the server when enabled", async function() { + instance.config.set("factorio.enable_lua_udp", true); + await instance.sendUdp("data"); + assert.deepEqual(instance.server.udpMessages, ["data"]); + }); + }); + describe("._recordPlayerJoin()", function() { it("should add player to playersOnline", function() { instance._recordPlayerJoin("player"); diff --git a/test/host/server.js b/test/host/server.js index 75b5d9c7..b6cc542d 100644 --- a/test/host/server.js +++ b/test/host/server.js @@ -399,6 +399,71 @@ describe("host/server", function() { assert.equal(server._udpSocket, null); assert.equal(server.hostUdpPort, undefined); }); + it("should warn on packets not in IPC format", async function() { + server.luaUdpPort = udpServer.address().port; + await server._bindUdpSocket(); + const logger = server._logger; + let warnings = []; + server._logger = { warn: msg => warnings.push(msg) }; + try { + udpServer.send(Buffer.from("not ipc"), server.hostUdpPort, "127.0.0.1"); + while (warnings.length < 1) { + await wait(1); + } + assert.equal(warnings[0], "Ignoring UDP packet not in IPC format: not ipc"); + } finally { + server._logger = logger; + server._resetState(); + } + }); + it("should emit error on malformed IPC packets", async function() { + server.luaUdpPort = udpServer.address().port; + await server._bindUdpSocket(); + try { + let waiter = events.once(server, "error"); + udpServer.send(Buffer.from("\f$ipc:no_separator"), server.hostUdpPort, "127.0.0.1"); + let [err] = await waiter; + assert.equal(err.message, 'Malformed IPC line "\f$ipc:no_separator"'); + } finally { + server._resetState(); + } + }); + it("should forward socket errors", async function() { + await server._bindUdpSocket(); + try { + let waiter = events.once(server, "error"); + server._udpSocket.emit("error", new Error("socket failed")); + let [err] = await waiter; + assert.equal(err.message, "socket failed"); + } finally { + server._resetState(); + } + }); + it("should reject sendUdp when sending fails", async function() { + server.luaUdpPort = udpServer.address().port; + await server._bindUdpSocket(); + server._state = "running"; + try { + // Larger than the maximum size of a UDP datagram + await assert.rejects(server.sendUdp(Buffer.alloc(70000)), { code: "EMSGSIZE" }); + } finally { + server._resetState(); + } + }); + it("should close the socket if binding fails", async function() { + const createSocket = dgram.createSocket; + dgram.createSocket = (...args) => { + const socket = createSocket(...args); + socket.bind = () => process.nextTick(() => socket.emit("error", new Error("bind failed"))); + return socket; + }; + try { + await assert.rejects(server._bindUdpSocket(), new Error("bind failed")); + } finally { + dgram.createSocket = createSocket; + } + assert.equal(server._udpSocket, null); + }); }); describe(".stop()", function() { diff --git a/test/integration/server.js b/test/integration/server.js index b74af0e9..e4e9ade2 100644 --- a/test/integration/server.js +++ b/test/integration/server.js @@ -179,6 +179,8 @@ describe("Integration of host/src/server", function() { slowTest(this); log(".startScenario()"); + // --enable-lua-udp was added in Factorio 2.0.59 + server.enableLuaUdp = lib.integerFullVersion(server.version) >= lib.integerFullVersion("2.0.59"); let pass = false; function filter(output) { if (output.message === "test_scenario init") { @@ -191,6 +193,7 @@ describe("Integration of host/src/server", function() { log(".stop()"); await server.stop(); + server.enableLuaUdp = false; server.off("output", filter); assert(pass, "server did not output line from test scenario"); diff --git a/test/mock.js b/test/mock.js index 265e869a..9b2d9ef6 100644 --- a/test/mock.js +++ b/test/mock.js @@ -95,6 +95,11 @@ export class MockServer extends events.EventEmitter { reset() { this.rconCommands = []; this.rconCommandResults = new Map(); + this.udpMessages = []; + } + + async sendUdp(message) { + this.udpMessages.push(message); } async sendRcon(command) { @@ -144,6 +149,10 @@ export class MockInstance extends lib.Link { async sendRcon(command, expectEmpty, plugin) { return await this.server.sendRcon(command); } + + async sendUdp(message) { + await this.server.sendUdp(message); + } } export class MockHost extends lib.Link {