mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-11 15:04:52 +09:00
Finish the plugin system integration tests
Adds the last three checks from #342 to test_plugin: an InstanceEcho request handled on a running instance, a Lua module with a remote interface that the request calls over RCON, and log tagging checks for the plugin field and host/instance ids on the controller, host and instance log lines. test_plugin now has a module, so the create_scenario and patch unit tests that relied on it having none use global_chat instead. Closes #342 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
35125a28ab
commit
9ab36151bd
8 files changed
+100
-4
No files matched your search
@@ -1,7 +1,7 @@
|
||||
// Plugin exercising the plugin system in the integration tests, see #342.
|
||||
// Written as plain JavaScript so that it does not need to be built.
|
||||
|
||||
import { ControllerEcho, HostEcho, HostEchoReceived } from "./messages.js";
|
||||
import { ControllerEcho, HostEcho, HostEchoReceived, InstanceEcho } from "./messages.js";
|
||||
|
||||
export const plugin = {
|
||||
name: "test_plugin",
|
||||
@@ -17,5 +17,6 @@ export const plugin = {
|
||||
ControllerEcho,
|
||||
HostEcho,
|
||||
HostEchoReceived,
|
||||
InstanceEcho,
|
||||
],
|
||||
};
|
||||
@@ -1,3 +1,7 @@
|
||||
import * as lib from "@clusterio/lib";
|
||||
|
||||
import { InstanceEcho } from "./messages.js";
|
||||
|
||||
export default async function(context) {
|
||||
const { instance, plugin, logger } = context;
|
||||
logger.info("test_plugin instance loaded");
|
||||
@@ -5,4 +9,11 @@ export default async function(context) {
|
||||
instance.hooks.start.attach(plugin.name, async () => {
|
||||
logger.info("test_plugin instance started");
|
||||
});
|
||||
|
||||
instance.handle(InstanceEcho, async (request) => {
|
||||
logger.info(`test_plugin instance echo ${request.text}`);
|
||||
const text = lib.escapeString(request.text);
|
||||
const response = await instance.sendRcon(`/sc rcon.print(remote.call("test_plugin", "echo", "${text}"))`);
|
||||
return response.replace(/\n$/, "");
|
||||
});
|
||||
}
|
||||
@@ -54,3 +54,22 @@ export class HostEchoReceived {
|
||||
toJSON() { return this.text; }
|
||||
static fromJSON(json) { return new this(json); }
|
||||
}
|
||||
|
||||
// Sent from ctl to an instance, which answers with the text echoed back by
|
||||
// the remote interface of the plugin's Lua module.
|
||||
export class InstanceEcho {
|
||||
static type = "request";
|
||||
static src = "control";
|
||||
static dst = "instance";
|
||||
static plugin = "test_plugin";
|
||||
static permission = null;
|
||||
static Response = JsonString;
|
||||
|
||||
constructor(text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
static jsonSchema = { type: "string" };
|
||||
toJSON() { return this.text; }
|
||||
static fromJSON(json) { return new this(json); }
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "test_plugin",
|
||||
"require": ["test_plugin.lua"]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
remote.add_interface("test_plugin", {
|
||||
echo = function(text)
|
||||
return text
|
||||
end,
|
||||
})
|
||||
@@ -154,7 +154,7 @@ describe("host/create_scenario", function() {
|
||||
await fs.cp(scenario, freeplay, { recursive: true });
|
||||
pluginInfos = await lib.loadPluginInfos(new Map([
|
||||
["research_sync", path.resolve("plugins/research_sync")],
|
||||
["test_plugin", path.resolve("test/file/test_plugin")],
|
||||
["global_chat", path.resolve("plugins/global_chat")],
|
||||
]));
|
||||
lib.addPluginConfigFields(pluginInfos);
|
||||
hostConfig = new lib.HostConfig("host", { "host.factorio_directory": factorioDir });
|
||||
|
||||
+1
-1
@@ -241,7 +241,7 @@ describe("host/patch", function() {
|
||||
});
|
||||
it("should skip plugins without a module", async function() {
|
||||
const pluginInfos = await lib.loadPluginInfos(
|
||||
new Map([["test_plugin", path.resolve("test/file/test_plugin")]])
|
||||
new Map([["global_chat", path.resolve("plugins/global_chat")]])
|
||||
);
|
||||
const modules = await patch.loadModules(pluginInfos);
|
||||
assert.deepEqual([...modules.keys()], ["clusterio"]);
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
} from "./index.js";
|
||||
import { externalTest, slowTest } from "../common.js";
|
||||
import { getTestRconPort } from "./rcon_port.js";
|
||||
import { ControllerEcho, HostEchoReceived } from "../file/test_plugin/messages.js";
|
||||
import { ControllerEcho, HostEchoReceived, InstanceEcho } from "../file/test_plugin/messages.js";
|
||||
|
||||
|
||||
/** @returns {Promise<Map<number, lib.InstanceDetails>>} */
|
||||
@@ -1002,6 +1002,62 @@ describe("Integration of Clusterio", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("test_plugin on a running instance", function() {
|
||||
requiresFactorio(this);
|
||||
before(async function() {
|
||||
this.timeout(20000);
|
||||
if ((await getInstances()).get(44).status !== "running") {
|
||||
await execCtl("instance start test");
|
||||
}
|
||||
});
|
||||
|
||||
async function findLog(filter, message) {
|
||||
// Host and instance logs are forwarded to the controller asynchronously.
|
||||
for (let attempt = 0; attempt < 100; attempt++) {
|
||||
const { log } = await getControl().send(
|
||||
new lib.LogQueryRequest(false, filter.controller ?? false, filter.hostIds ?? [],
|
||||
filter.instanceIds ?? [], undefined, 100, "desc")
|
||||
);
|
||||
const entry = log.find(info => info.message === message);
|
||||
if (entry) {
|
||||
return entry;
|
||||
}
|
||||
await wait(20);
|
||||
}
|
||||
throw new Error(`log entry "${message}" not found`);
|
||||
}
|
||||
|
||||
it("should route a plugin request from control to an instance", async function() {
|
||||
const response = await getControl().sendTo({ instanceId: 44 }, new InstanceEcho("to-instance"));
|
||||
assert.equal(response, "to-instance");
|
||||
});
|
||||
|
||||
it("should patch the plugin's Lua module into the save", async function() {
|
||||
const result = await sendRcon(44, "/sc rcon.print(remote.interfaces.test_plugin.echo)");
|
||||
assert.equal(result, "true\n");
|
||||
});
|
||||
|
||||
it("should tag plugin log entries with the plugin and where they came from", async function() {
|
||||
await getControl().sendTo("controller", new ControllerEcho("log-tagging"));
|
||||
await getControl().sendTo({ instanceId: 44 }, new InstanceEcho("log-tagging"));
|
||||
|
||||
const controllerEntry = await findLog({ controller: true }, "test_plugin controller echo log-tagging");
|
||||
assert.equal(controllerEntry.plugin, "test_plugin");
|
||||
assert.equal(controllerEntry.host_id, undefined);
|
||||
assert.equal(controllerEntry.instance_id, undefined);
|
||||
|
||||
const hostEntry = await findLog({ hostIds: [4] }, "test_plugin host echo log-tagging");
|
||||
assert.equal(hostEntry.plugin, "test_plugin");
|
||||
assert.equal(hostEntry.host_id, 4);
|
||||
assert.equal(hostEntry.instance_id, undefined);
|
||||
|
||||
const instanceEntry = await findLog({ instanceIds: [44] }, "test_plugin instance echo log-tagging");
|
||||
assert.equal(instanceEntry.plugin, "test_plugin");
|
||||
assert.equal(instanceEntry.host_id, 4);
|
||||
assert.equal(instanceEntry.instance_id, 44);
|
||||
});
|
||||
});
|
||||
|
||||
describe("instance config set-prop", function() {
|
||||
requiresFactorio(this);
|
||||
it("applies factorio settings while running", async function() {
|
||||
|
||||
Reference in new issue
Block a user