Address review on the test harness

- The generic parts move to test/ in the repository root so other plugins can
  reuse them: the factorio and clusterio stubs, the test framework, the
  fengari runner, and clusterio's testMatrix and round trip helpers. A plugin
  composes them from its own env.lua, which adds its stubs and fixtures.
- Tests are declared with Test.test(name, fn) and every test function
  receives a fresh environment, so nothing carries over between them. A test
  which errors is reported as a failure rather than aborting the file.
- Every stub raises on properties it does not implement, which mirrors the
  game api. game.player is the one property allowed to read as nil.
- Test.deep_eq compares tables recursively with keys checked from both sides.
- The message records round trip through the same testMatrix and
  testRoundTripJsonSerialisable helpers the clusterio tests use, covering
  every optional field combination of the records, events and requests.
- controller.test.js and instance.test.js cover the node side of the plugin
  against faked controller and instance internals: property creation and
  sweeping, record building, broadcasts, subscription replay, assignment
  validation, auto assignment and its blocking role, seeding, the initialise
  payload, sync mode gating, and the rejection rollback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
bbassie
2026-08-22 16:45:51 +00:00
co-authored by Claude Fable 5
parent 59a0e0be13
commit 24d36db601
14 changed files with 1106 additions and 434 deletions
+64 -12
View File
@@ -1,31 +1,83 @@
"use strict";
const t = require("tap");
const { Value } = require("@sinclair/typebox/value");
const messages = require("../dist/node/messages");
const { testMatrix, testRoundTripJsonSerialisable } = require("../../test/common");
function roundTrip(subtest, Record, record) {
const json = record.toJSON();
subtest.ok(Value.Check(Record.jsonSchema, json), "json matches the schema");
subtest.strictSame(Record.fromJSON(json), record, "the record round trips");
}
const fullMeta = new messages.RoleMetaRecord(
7, 3, 1, "Mod", "[Mod]", new messages.RoleColor(1, 2, 3), 3600000, true, 12345, false,
);
t.test("RoleColor", subtest => {
testRoundTripJsonSerialisable(messages.RoleColor, testMatrix(
[0, 255], // r
[0, 128], // g
[0, 1], // b
));
subtest.pass("round trips");
subtest.end();
});
t.test("RoleMetaRecord", subtest => {
roundTrip(subtest, messages.RoleMetaRecord, new messages.RoleMetaRecord(7, 3));
roundTrip(subtest, messages.RoleMetaRecord, new messages.RoleMetaRecord(
7, 3, 1, "Mod", "[Mod]", new messages.RoleColor(1, 2, 3), 3600000, true, 12345, false,
testRoundTripJsonSerialisable(messages.RoleMetaRecord, testMatrix(
[7], // id
[3], // order
[0, 1], // priority
["", "Mod"], // shortHand
["", "[Mod]"], // tag
[null, new messages.RoleColor(1, 2, 3)], // color
[null, 3600000], // autoAssignOnlineTimeMs
[false, true], // blockAutoAssign
[0, 12345], // updatedAtMs
[false, true], // isDeleted
));
subtest.pass("round trips");
subtest.end();
});
t.test("RoleRecord", subtest => {
roundTrip(subtest, messages.RoleRecord, new messages.RoleRecord(
7, "Moderator", ["exp_scenario.command.kill"], new messages.RoleMetaRecord(7, 3), true, 12345,
testRoundTripJsonSerialisable(messages.RoleRecord, testMatrix(
[7], // id
["Moderator"], // name
[[], ["a.b", "c.d"]], // permissions
[new messages.RoleMetaRecord(7, 3), fullMeta], // meta
[false, true], // isDefault
[0, 12345], // updatedAtMs
[false, true], // isDeleted
));
subtest.pass("round trips");
subtest.end();
});
t.test("AssignmentRecord", subtest => {
roundTrip(subtest, messages.AssignmentRecord, new messages.AssignmentRecord("alice", new Set([5, 6]), 12345));
testRoundTripJsonSerialisable(messages.AssignmentRecord, testMatrix(
["alice"], // name
[new Set(), new Set([5, 6])], // roleIds
[0, 12345], // updatedAtMs
[false, true], // isDeleted
));
subtest.pass("round trips");
subtest.end();
});
t.test("update events and requests", subtest => {
const record = new messages.RoleRecord(7, "Moderator", ["a.b"], fullMeta, true, 12345);
const assignment = new messages.AssignmentRecord("alice", new Set([5]), 12345);
testRoundTripJsonSerialisable(messages.RoleUpdatedEvent, testMatrix(
[[], [record]], // updates
));
testRoundTripJsonSerialisable(messages.AssignmentUpdatedEvent, testMatrix(
[[], [assignment]], // updates
));
testRoundTripJsonSerialisable(messages.RoleMetaUpdateRequest, testMatrix(
[new messages.RoleMetaRecord(7, 3), fullMeta], // meta
));
testRoundTripJsonSerialisable(messages.AssignmentUpdateRequest, testMatrix(
["alice"], // name
[[], [5]], // assign
[[], [6]], // unassign
));
subtest.pass("round trips");
subtest.end();
});