Files
bbassieandClaude Fable 5.1 4b0238b64b Cover UDP error paths and sendUdp in tests
Codecov flagged the sendUdp methods on Instance and BaseInstancePlugin,
the bind, socket error, malformed packet and send failure paths in
FactorioServer, and the --enable-lua-udp branch of startScenario.  Add
unit tests for each and start the integration scenario test with UDP
enabled on Factorio 2.0.59+.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-15 16:38:34 +00:00

92 lines
3.7 KiB
JavaScript

import assert from "node:assert/strict";
import * as mock from "../mock.js";
import * as host from "@clusterio/host";
describe("host/src/BaseInstancePlugin", function() {
describe("class BaseInstancePlugin", function() {
const info = { name: "test" };
let instancePlugin;
it("should be constructible", async function() {
instancePlugin = new host.BaseInstancePlugin(
info, new mock.MockInstance(), new mock.MockHost(), new mock.MockLogger()
);
await instancePlugin.init();
assert.equal(instancePlugin.instance.hooks.size, 0);
});
it("should be constructible from a load context", async function() {
const instance = new mock.MockInstance();
const mockHost = new mock.MockHost();
const logger = new mock.MockLogger();
const plugin = host.BaseInstancePlugin.fromContext({ plugin: info, instance, host: mockHost, logger });
assert.equal(plugin.info, info);
assert.equal(plugin.instance, instance);
assert.equal(plugin.host, mockHost);
assert.equal(plugin.logger, logger);
});
it("should attach overridden hooks", async function() {
let calls = [];
class InstancePlugin extends host.BaseInstancePlugin {
async onStart() { calls.push(["start", this]); }
onExit() { calls.push(["exit"]); }
async onOutput(parsed, line) { calls.push(["output", parsed, line]); }
}
const instance = new mock.MockInstance();
const plugin = new InstancePlugin(info, instance, new mock.MockHost(), new mock.MockLogger());
assert.deepEqual([...instance.hooks.attached], ["test"]);
assert.equal(instance.hooks.stop.size, 0);
await instance.hooks.start.invoke();
await instance.hooks.exit.invoke();
await instance.hooks.output.invoke({ type: "generic" }, "line");
assert.deepEqual(calls, [["start", plugin], ["exit"], ["output", { type: "generic" }, "line"]]);
});
it("should detach hooks", async function() {
class InstancePlugin extends host.BaseInstancePlugin {
async onStart() { }
}
const instance = new mock.MockInstance();
const plugin = new InstancePlugin(info, instance, new mock.MockHost(), new mock.MockLogger());
assert.equal(instance.hooks.size, 1);
plugin.detachHooks();
assert.equal(instance.hooks.size, 0);
});
describe("sendRcon", function() {
it("should send commands out of order", async function() {
instancePlugin.instance.server.rconCommandResults.set("a", { time: 100, response: "a" });
instancePlugin.instance.server.rconCommandResults.set("b", { time: 50, response: "b" });
let a = instancePlugin.sendRcon("a");
let b = instancePlugin.sendRcon("b");
let result = await Promise.race([a, b]);
assert.equal(result, "b");
});
it("should propagate errors", async function() {
instancePlugin.instance.server.rconCommandResults.set("a", new Error("ref"));
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" });
instancePlugin.instance.server.rconCommandResults.set("b", { time: 50, response: "b" });
let a = instancePlugin.sendOrderedRcon("a");
let b = instancePlugin.sendOrderedRcon("b");
let result = await Promise.race([a, b]);
assert.equal(result, "a");
});
it("should propagate errors", async function() {
instancePlugin.instance.server.rconCommandResults.set("a", new Error("ref"));
await assert.rejects(instancePlugin.sendOrderedRcon("a"), new Error("ref"));
});
});
});
});