mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-13 15:14:52 +09:00
Merge pull request #962 from bbassie/feature/plugin-system-tests
Add integration tests for the plugin system
This commit is contained in:
9 files changed
+242
No files matched your search
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
const { Command, CommandTree } = require("@clusterio/lib");
|
||||
const { BaseCtlPlugin } = require("@clusterio/ctl");
|
||||
|
||||
const testPluginCommands = new CommandTree({
|
||||
name: "test-plugin", description: "Test plugin commands",
|
||||
});
|
||||
testPluginCommands.add(new Command({
|
||||
definition: ["echo <message>", "Print the message given to it", (yargs) => {
|
||||
yargs.positional("message", { describe: "message to print", type: "string" });
|
||||
}],
|
||||
handler: async function(args, control) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(args.message);
|
||||
},
|
||||
}));
|
||||
|
||||
class CtlPlugin extends BaseCtlPlugin {
|
||||
async addCommands(rootCommand) {
|
||||
rootCommand.add(testPluginCommands);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
CtlPlugin,
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
"use strict";
|
||||
const { BaseControllerPlugin } = require("@clusterio/controller");
|
||||
|
||||
const { ControllerEcho, HostEcho } = require("./messages");
|
||||
|
||||
class ControllerPlugin extends BaseControllerPlugin {
|
||||
async init() {
|
||||
this.logger.info("test_plugin controller loaded");
|
||||
this.controller.handle(ControllerEcho, this.handleControllerEcho.bind(this));
|
||||
}
|
||||
|
||||
async handleControllerEcho(request) {
|
||||
this.logger.info(`test_plugin controller echo ${request.text}`);
|
||||
this.broadcastEventToHosts(new HostEcho(request.text));
|
||||
return request.text;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ControllerPlugin,
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
const { BaseHostPlugin } = require("@clusterio/host");
|
||||
|
||||
const { HostEcho, HostEchoReceived } = require("./messages");
|
||||
|
||||
class HostPlugin extends BaseHostPlugin {
|
||||
async init() {
|
||||
this.logger.info("test_plugin host loaded");
|
||||
this.receivedEchoes = new Set();
|
||||
this.host.handle(HostEcho, this.handleHostEcho.bind(this));
|
||||
this.host.handle(HostEchoReceived, this.handleHostEchoReceived.bind(this));
|
||||
}
|
||||
|
||||
async handleHostEcho(event) {
|
||||
this.logger.info(`test_plugin host echo ${event.text}`);
|
||||
this.receivedEchoes.add(event.text);
|
||||
}
|
||||
|
||||
async handleHostEchoReceived(request) {
|
||||
return this.receivedEchoes.has(request.text);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
HostPlugin,
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
"use strict";
|
||||
// Plugin exercising the plugin system in the integration tests, see #342.
|
||||
// Written as plain JavaScript so that it does not need to be built.
|
||||
|
||||
const { ControllerEcho, HostEcho, HostEchoReceived } = require("./messages");
|
||||
|
||||
module.exports = {
|
||||
plugin: {
|
||||
name: "test_plugin",
|
||||
title: "Test Plugin",
|
||||
description: "Plugin used to test the plugin system.",
|
||||
|
||||
controllerEntrypoint: "controller",
|
||||
hostEntrypoint: "host",
|
||||
instanceEntrypoint: "instance",
|
||||
ctlEntrypoint: "control",
|
||||
|
||||
messages: [
|
||||
ControllerEcho,
|
||||
HostEcho,
|
||||
HostEchoReceived,
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
"use strict";
|
||||
const { BaseInstancePlugin } = require("@clusterio/host");
|
||||
|
||||
class InstancePlugin extends BaseInstancePlugin {
|
||||
async init() {
|
||||
this.logger.info("test_plugin instance loaded");
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
InstancePlugin,
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
"use strict";
|
||||
const { JsonBoolean, JsonString } = require("@clusterio/lib");
|
||||
|
||||
// Sent from ctl to the controller, which answers with the text it was given
|
||||
// and broadcasts a HostEcho to the hosts.
|
||||
class ControllerEcho {
|
||||
static type = "request";
|
||||
static src = "control";
|
||||
static dst = "controller";
|
||||
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); }
|
||||
}
|
||||
|
||||
// Broadcast from the controller to the hosts by broadcastEventToHosts.
|
||||
class HostEcho {
|
||||
static type = "event";
|
||||
static src = "controller";
|
||||
static dst = "host";
|
||||
static plugin = "test_plugin";
|
||||
static permission = null;
|
||||
|
||||
constructor(text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
static jsonSchema = { type: "string" };
|
||||
toJSON() { return this.text; }
|
||||
static fromJSON(json) { return new this(json); }
|
||||
}
|
||||
|
||||
// Sent from ctl to a host, which answers with whether it received the given
|
||||
// text via a HostEcho.
|
||||
class HostEchoReceived {
|
||||
static type = "request";
|
||||
static src = "control";
|
||||
static dst = "host";
|
||||
static plugin = "test_plugin";
|
||||
static permission = null;
|
||||
static Response = JsonBoolean;
|
||||
|
||||
constructor(text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
static jsonSchema = { type: "string" };
|
||||
toJSON() { return this.text; }
|
||||
static fromJSON(json) { return new this(json); }
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ControllerEcho,
|
||||
HostEcho,
|
||||
HostEchoReceived,
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "test_plugin",
|
||||
"version": "0.0.1",
|
||||
"description": "Plugin used to test the plugin system, see #342",
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
"keywords": [
|
||||
"clusterio-plugin"
|
||||
]
|
||||
}
|
||||
@@ -16,6 +16,7 @@ const {
|
||||
spawnNode, instancesDir, factorioDir, databaseDir, controllerConfigPath,
|
||||
requiresFactorio, hasFactorio,
|
||||
} = require("./index");
|
||||
const { ControllerEcho, HostEchoReceived } = require("../file/test_plugin/messages");
|
||||
|
||||
|
||||
/** @returns {Promise<Map<number, lib.InstanceDetails>>} */
|
||||
@@ -386,6 +387,64 @@ describe("Integration of Clusterio", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("plugin system", function() {
|
||||
// Exercised with test/file/test_plugin, which is a plugin that exists
|
||||
// only to be loaded by these tests, see #342.
|
||||
async function pluginDetails(address) {
|
||||
const plugins = await getControl().sendTo(address, new lib.PluginListRequest());
|
||||
const details = plugins.find(plugin => plugin.name === "test_plugin");
|
||||
assert(details, `test_plugin missing from the plugin list of ${JSON.stringify(address)}`);
|
||||
return details;
|
||||
}
|
||||
|
||||
it("should load the plugin on the controller", async function() {
|
||||
const details = await pluginDetails("controller");
|
||||
assert(details.enabled, "test_plugin is not enabled on the controller");
|
||||
assert(details.loaded, "test_plugin is not loaded on the controller");
|
||||
});
|
||||
|
||||
it("should load the plugin on the host", async function() {
|
||||
const details = await pluginDetails({ hostId: 4 });
|
||||
assert(details.enabled, "test_plugin is not enabled on the host");
|
||||
assert(details.loaded, "test_plugin is not loaded on the host");
|
||||
});
|
||||
|
||||
it("should report the plugin's title and version", async function() {
|
||||
const details = await pluginDetails("controller");
|
||||
assert.equal(details.title, "Test Plugin");
|
||||
assert.equal(details.version, "0.0.1");
|
||||
});
|
||||
|
||||
it("should add the plugin's command to clusterioctl", async function() {
|
||||
const { stdout } = await execCtlProcess("test-plugin echo works");
|
||||
assert(stdout.includes("works"), `plugin command did not print its message, got ${stdout}`);
|
||||
});
|
||||
|
||||
it("should route a plugin request from control to the controller", async function() {
|
||||
const response = await getControl().sendTo("controller", new ControllerEcho("to-controller"));
|
||||
assert.equal(response, "to-controller");
|
||||
});
|
||||
|
||||
it("should route a plugin request from control to a host", async function() {
|
||||
const received = await getControl().sendTo({ hostId: 4 }, new HostEchoReceived("never-broadcast"));
|
||||
assert.equal(received, false);
|
||||
});
|
||||
|
||||
it("should deliver a plugin event sent with broadcastEventToHosts", async function() {
|
||||
await getControl().sendTo("controller", new ControllerEcho("to-hosts"));
|
||||
|
||||
// The broadcast is sent without waiting for the hosts to receive it.
|
||||
let received = false;
|
||||
for (let attempt = 0; attempt < 100 && !received; attempt++) {
|
||||
received = await getControl().sendTo({ hostId: 4 }, new HostEchoReceived("to-hosts"));
|
||||
if (!received) {
|
||||
await lib.wait(20);
|
||||
}
|
||||
}
|
||||
assert(received, "host did not receive the event broadcast to it");
|
||||
});
|
||||
});
|
||||
|
||||
describe("clusterioctl", function() {
|
||||
describe("controller config list", function() {
|
||||
it("runs", async function() {
|
||||
|
||||
@@ -350,6 +350,7 @@ before(async function() {
|
||||
await execController("config set controller.tls_private_key ../../test/file/tls/key.pem");
|
||||
|
||||
console.log("Setting Controller Plugins");
|
||||
await execCtlProcess("plugin add ../../test/file/test_plugin");
|
||||
await execCtlProcess("plugin add ../../plugins/global_chat");
|
||||
await execCtlProcess("plugin add ../../plugins/research_sync");
|
||||
await execCtlProcess("plugin add ../../plugins/statistics_exporter");
|
||||
|
||||
Reference in new issue
Block a user