mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-15 23:24:52 +09:00
Merge pull request #778 from Cooldude2606/refactor/test-exec-ctl
Refactor execCtl and Ctl to speed up unit testing
This commit is contained in:
12 files changed
+188
-143
No files matched your search
+61
-28
@@ -143,7 +143,19 @@ interface CtlArguments {
|
||||
pluginList: string,
|
||||
}
|
||||
|
||||
async function startControl() {
|
||||
interface InitializeParameters {
|
||||
args: CtlArguments;
|
||||
shouldRun: boolean;
|
||||
ctlPlugins?: Map<string, BaseCtlPlugin>;
|
||||
rootCommands?: lib.CommandTree;
|
||||
controlConfig?: lib.ControlConfig;
|
||||
}
|
||||
|
||||
export async function initialize(
|
||||
argv: string | string[],
|
||||
ctlPlugins?: Map<string, BaseCtlPlugin>,
|
||||
noLoggerTransport?: boolean,
|
||||
): Promise<InitializeParameters> {
|
||||
yargs
|
||||
.scriptName("clusterioctl")
|
||||
.usage("$0 <command> [options]")
|
||||
@@ -175,38 +187,41 @@ async function startControl() {
|
||||
|
||||
// Parse the args first to get the configured plugin list.
|
||||
// eslint-disable-next-line node/no-sync
|
||||
let args = yargs.parseSync() as CtlArguments;
|
||||
let args = yargs.parseSync(argv) as CtlArguments;
|
||||
|
||||
// Log stream for the ctl session.
|
||||
logger.add(
|
||||
new ConsoleTransport({
|
||||
errorLevels: Object.keys(levels),
|
||||
level: args.logLevel,
|
||||
format: new lib.TerminalFormat(),
|
||||
})
|
||||
);
|
||||
lib.handleUnhandledErrors();
|
||||
|
||||
logger.verbose(`Loading available plugins from ${args.pluginList}`);
|
||||
let pluginList = await lib.loadPluginList(args.pluginList);
|
||||
|
||||
// If the command is plugin management we don't try to load plugins
|
||||
if (args._[0] === "plugin") {
|
||||
await lib.handlePluginCommand(args, pluginList, args.pluginList);
|
||||
return;
|
||||
// Log stream for the ctl session. Skipped in testing.
|
||||
if (!noLoggerTransport) {
|
||||
logger.add(
|
||||
new ConsoleTransport({
|
||||
errorLevels: Object.keys(levels),
|
||||
level: args.logLevel,
|
||||
format: new lib.TerminalFormat(),
|
||||
})
|
||||
);
|
||||
lib.handleUnhandledErrors();
|
||||
}
|
||||
|
||||
logger.verbose("Loading Plugins");
|
||||
let ctlPlugins = await loadPlugins(pluginList);
|
||||
// Discover and load plugins. This check exists to allow tests to inject plugins.
|
||||
if (!ctlPlugins || args._[0] === "plugin") {
|
||||
logger.verbose(`Loading available plugins from ${args.pluginList}`);
|
||||
const pluginList = await lib.loadPluginList(args.pluginList);
|
||||
|
||||
// Add all cluster management commands including ones from plugins
|
||||
let rootCommands = await commands.registerCommands(ctlPlugins, yargs);
|
||||
// If the command is plugin management we don't try to load plugins
|
||||
if (args._[0] === "plugin") {
|
||||
await lib.handlePluginCommand(args, pluginList, args.pluginList);
|
||||
return { args, shouldRun: false };
|
||||
}
|
||||
|
||||
// Reparse after commands have been added with help and strict checking.
|
||||
logger.verbose("Loading Plugins");
|
||||
ctlPlugins = await loadPlugins(pluginList);
|
||||
}
|
||||
|
||||
// Add all commands including from plugins and reparse with help and strict checking.
|
||||
const rootCommands = await commands.registerCommands(ctlPlugins, yargs);
|
||||
args = yargs
|
||||
.help()
|
||||
.strict()
|
||||
.parse() as CtlArguments
|
||||
.parse(argv) as CtlArguments
|
||||
;
|
||||
|
||||
let controlConfig;
|
||||
@@ -233,18 +248,34 @@ async function startControl() {
|
||||
// Handle the control-config command before trying to connect.
|
||||
if (args._[0] === "control-config") {
|
||||
await lib.handleConfigCommand(args, controlConfig);
|
||||
return;
|
||||
return { args, controlConfig, ctlPlugins, rootCommands, shouldRun: false };
|
||||
}
|
||||
|
||||
// Determine which command is being executed.
|
||||
return { args, controlConfig, ctlPlugins, rootCommands, shouldRun: true };
|
||||
}
|
||||
|
||||
export function selectTargetCommand(args: CtlArguments, rootCommands: lib.CommandTree): lib.Command {
|
||||
let commandPath = [...args._] as string[];
|
||||
let targetCommand: lib.CommandTree | lib.Command = rootCommands;
|
||||
while (commandPath.length && targetCommand instanceof lib.CommandTree) {
|
||||
targetCommand = targetCommand.get(commandPath.shift()!)!;
|
||||
}
|
||||
assert(targetCommand instanceof lib.Command);
|
||||
return targetCommand;
|
||||
}
|
||||
|
||||
async function startControl() {
|
||||
const {
|
||||
args,
|
||||
shouldRun,
|
||||
ctlPlugins,
|
||||
rootCommands,
|
||||
controlConfig,
|
||||
} = await initialize(process.argv.slice(2));
|
||||
if (!shouldRun || !ctlPlugins || !rootCommands || !controlConfig) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The remaining commands require connecting to the controller.
|
||||
if (!controlConfig.get("control.controller_url") || !controlConfig.get("control.controller_token")) {
|
||||
logger.error("Missing URL and/or token to connect with. See README.md for setting up access.");
|
||||
process.exitCode = 1;
|
||||
@@ -263,6 +294,7 @@ async function startControl() {
|
||||
tlsCa,
|
||||
controlConfig.get("control.controller_token")!,
|
||||
);
|
||||
|
||||
let control = new Control(controlConnector, controlConfig, tlsCa, ctlPlugins);
|
||||
try {
|
||||
await controlConnector.connect();
|
||||
@@ -284,6 +316,7 @@ async function startControl() {
|
||||
});
|
||||
|
||||
try {
|
||||
const targetCommand = selectTargetCommand(args, rootCommands);
|
||||
await targetCommand.run(args, control);
|
||||
|
||||
} catch (err) {
|
||||
|
||||
@@ -26,7 +26,6 @@ describe("global_chat plugin", function() {
|
||||
let instancePlugin;
|
||||
|
||||
before(async function() {
|
||||
lib.Link.register(ChatEvent);
|
||||
instancePlugin = new instance.InstancePlugin(info, new mock.MockInstance(), new mock.MockHost());
|
||||
await instancePlugin.init();
|
||||
});
|
||||
|
||||
@@ -13,7 +13,8 @@ const { wait } = lib;
|
||||
const testStrings = require("../lib/factorio/test_strings");
|
||||
const {
|
||||
TestControl, TestControlConnector, url, controlToken, slowTest,
|
||||
exec, execCtl, sendRcon, getControl, spawnNode, instancesDir, factorioDir,
|
||||
exec, execCtl, execCtlProcess, execHost, sendRcon, getControl,
|
||||
spawnNode, instancesDir, factorioDir,
|
||||
} = require("./index");
|
||||
|
||||
|
||||
@@ -39,11 +40,11 @@ async function startAltHost() {
|
||||
await fs.remove(path.join("temp", "test", "alt-instances"));
|
||||
await fs.remove(path.join("temp", "test", "alt-mods"));
|
||||
await execCtl(`host create-config --id 5 --name alt-host --generate-token --output ${config}`);
|
||||
await exec(`node ../../packages/host --config ${config} config set host.tls_ca ../../test/file/tls/cert.pem`);
|
||||
await exec(`node ../../packages/host --config ${config} config set host.instances_directory alt-instances`);
|
||||
await execHost(`--config ${config} config set host.tls_ca ../../test/file/tls/cert.pem`);
|
||||
await execHost(`--config ${config} config set host.instances_directory alt-instances`);
|
||||
const dir = path.isAbsolute(factorioDir) ? factorioDir : path.join("..", "..", factorioDir);
|
||||
await exec(`node ../../packages/host --config ${config} config set host.factorio_directory ${dir}`);
|
||||
await exec(`node ../../packages/host --config ${config} config set host.mods_directory alt-mods`);
|
||||
await execHost(`--config ${config} config set host.factorio_directory ${dir}`);
|
||||
await execHost(`--config ${config} config set host.mods_directory alt-mods`);
|
||||
return await spawnAltHost(config);
|
||||
}
|
||||
|
||||
@@ -259,9 +260,9 @@ describe("Integration of Clusterio", function() {
|
||||
|
||||
describe("controller config set", function() {
|
||||
it("sets given config option", async function() {
|
||||
await execCtl('controller config set controller.name "Test Cluster"');
|
||||
await execCtl("controller config set controller.name Test-Cluster");
|
||||
let result = await getControl().send(new lib.ControllerConfigGetRequest());
|
||||
assert.equal(result["controller.name"], "Test Cluster");
|
||||
assert.equal(result["controller.name"], "Test-Cluster");
|
||||
});
|
||||
it("should not allow setting auth_secret", async function() {
|
||||
await assert.rejects(execCtl("controller config set controller.auth_secret root"));
|
||||
@@ -277,9 +278,9 @@ describe("Integration of Clusterio", function() {
|
||||
describe("controller plugin update", function() {
|
||||
it("runs", async function() {
|
||||
// In dev plugins have no npm package, so best we can do is get an error from the controller
|
||||
assert.rejects(
|
||||
await assert.rejects(
|
||||
execCtl("controller plugin update foo"),
|
||||
"Plugin foo is not installed on this machine"
|
||||
/Plugin foo is not installed on this machine/
|
||||
);
|
||||
});
|
||||
// Update always fails, we can not test restart option
|
||||
@@ -289,9 +290,9 @@ describe("Integration of Clusterio", function() {
|
||||
it("runs", async function() {
|
||||
// Default is to disallow updates, changing this value would require a restart
|
||||
// Additionally, it can not be changed via ctl, so best we can do is get an error from the controller
|
||||
assert.rejects(
|
||||
await assert.rejects(
|
||||
execCtl("controller plugin install foo"),
|
||||
"Plugin installs are disabled on this machine"
|
||||
/Plugin installs are disabled on this machine/
|
||||
);
|
||||
});
|
||||
// Install always fails, we can not test restart option
|
||||
@@ -303,9 +304,9 @@ describe("Integration of Clusterio", function() {
|
||||
});
|
||||
it("accepts --restart", async function() {
|
||||
// We cannot restart the controller, so we check for controller error instead
|
||||
assert.rejects(
|
||||
await assert.rejects(
|
||||
execCtl("controller update --restart"),
|
||||
"Cannot restart, controller does not have a process monitor to restart it."
|
||||
/Cannot restart, controller does not have a process monitor to restart it./
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -313,9 +314,9 @@ describe("Integration of Clusterio", function() {
|
||||
describe("controller restart", function() {
|
||||
it("runs", async function() {
|
||||
// We cannot restart the controller, so we check for controller error instead
|
||||
assert.rejects(
|
||||
await assert.rejects(
|
||||
execCtl("controller restart"),
|
||||
"Cannot restart, controller does not have a process monitor to restart it."
|
||||
/Cannot restart, controller does not have a process monitor to restart it./
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -329,7 +330,7 @@ describe("Integration of Clusterio", function() {
|
||||
describe("host config", function() {
|
||||
it("changes host config", async function() {
|
||||
await execCtl("host config set 4 host.name My-Host");
|
||||
const result = await execCtl("host config list 4");
|
||||
const result = await execCtlProcess("host config list 4");
|
||||
assert(/My-Host/.test(result.stdout), "New host name not in config output");
|
||||
});
|
||||
it("should not allow setting host.controller_token", async function() {
|
||||
@@ -359,9 +360,9 @@ describe("Integration of Clusterio", function() {
|
||||
describe("host plugin update", function() {
|
||||
it("runs", async function() {
|
||||
// In dev plugins have no npm package, so best we can do is get an error from the host
|
||||
assert.rejects(
|
||||
await assert.rejects(
|
||||
execCtl("host plugin update 4 foo"),
|
||||
"Plugin foo is not installed on this machine"
|
||||
/Plugin foo is not installed on this machine/
|
||||
);
|
||||
});
|
||||
// Update always fails, we can not test restart option
|
||||
@@ -371,9 +372,9 @@ describe("Integration of Clusterio", function() {
|
||||
it("runs", async function() {
|
||||
// Default is to disallow updates, changing this value would require a restart
|
||||
// Additionally, it can not be changed via ctl, so best we can do is get an error from the host
|
||||
assert.rejects(
|
||||
await assert.rejects(
|
||||
execCtl("host plugin install 4 foo"),
|
||||
"Plugin installs are disabled on this machine"
|
||||
/Plugin installs are disabled on this machine/
|
||||
);
|
||||
});
|
||||
// Install always fails, we can not test restart option
|
||||
@@ -385,9 +386,9 @@ describe("Integration of Clusterio", function() {
|
||||
});
|
||||
it("accepts --restart", async function() {
|
||||
// We cannot restart the host, so we check for host error instead
|
||||
assert.rejects(
|
||||
await assert.rejects(
|
||||
execCtl("host update 4 --restart"),
|
||||
"Cannot restart, host does not have a process monitor to restart it."
|
||||
/Cannot restart, host does not have a process monitor to restart it./
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -395,9 +396,9 @@ describe("Integration of Clusterio", function() {
|
||||
describe("host restart", function() {
|
||||
it("runs", async function() {
|
||||
// We cannot restart the host, so we check for host error instead
|
||||
assert.rejects(
|
||||
await assert.rejects(
|
||||
execCtl("host restart 4"),
|
||||
"Cannot restart, host does not have a process monitor to restart it."
|
||||
/Cannot restart, host does not have a process monitor to restart it./
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -416,9 +417,12 @@ describe("Integration of Clusterio", function() {
|
||||
slowTest(this);
|
||||
let sawDisconnected;
|
||||
await runWithAltHost(async () => {
|
||||
getControl().hostUpdates = [];
|
||||
const control = getControl();
|
||||
control.hostUpdates = [];
|
||||
await wait(50); // Allow for host to update instances
|
||||
await execCtl("host revoke-token 5");
|
||||
for (let update of getControl().hostUpdates) {
|
||||
await wait(50); // Allow controller to notify control
|
||||
for (let update of control.hostUpdates) {
|
||||
if (update.name !== "alt-host") {
|
||||
continue;
|
||||
}
|
||||
@@ -448,10 +452,8 @@ describe("Integration of Clusterio", function() {
|
||||
assert.equal(instances.get(44).status, "unassigned", "incorrect instance status");
|
||||
|
||||
// Make sure the following tests does not fail due to not having internet
|
||||
let value = JSON.stringify({ lan: true, public: false }).replace(
|
||||
/"/g, process.platform === "win32" ? '""' : '\\"'
|
||||
);
|
||||
await execCtl(`instance config set-prop test factorio.settings visibility "${value}"`);
|
||||
const value = jsonArg({ lan: true, public: false });
|
||||
await execCtlProcess(`instance config set-prop test factorio.settings visibility ${value}`);
|
||||
await execCtl("instance config set-prop test factorio.settings require_user_verification false");
|
||||
});
|
||||
it("can clone an instance", async function() {
|
||||
@@ -466,7 +468,7 @@ describe("Integration of Clusterio", function() {
|
||||
});
|
||||
it("errors when cloning an invalid instance", async function() {
|
||||
slowTest(this);
|
||||
assert.rejects(
|
||||
await assert.rejects(
|
||||
execCtl("instance create testClone --id 440 --from 441"),
|
||||
new lib.RequestError("Instance with ID 441 does not exist")
|
||||
);
|
||||
@@ -500,7 +502,7 @@ describe("Integration of Clusterio", function() {
|
||||
describe("instance save list", function() {
|
||||
it("lists the created save", async function() {
|
||||
slowTest(this);
|
||||
let result = await execCtl("instance save list test");
|
||||
let result = await execCtlProcess("instance save list test");
|
||||
assert(/world\.zip/.test(result.stdout), "world.zip not present in list save output");
|
||||
});
|
||||
});
|
||||
@@ -615,7 +617,7 @@ describe("Integration of Clusterio", function() {
|
||||
assert(r1.log.some(info => /\[COMMAND\]/.test(info.message)), "Command was not sent");
|
||||
await execCtl("instance config set test factorio.enable_script_commands false");
|
||||
|
||||
assert.rejects(
|
||||
await assert.rejects(
|
||||
execCtl("instance send-rcon test /c"),
|
||||
new Error(
|
||||
"Attempted to use script command while disabled. " +
|
||||
@@ -698,8 +700,8 @@ describe("Integration of Clusterio", function() {
|
||||
];
|
||||
|
||||
for (let [prop, value] of testConfigs) {
|
||||
let args = `test factorio.settings ${prop} ${jsonArg(value)}`;
|
||||
await execCtl(`instance config set-prop ${args}`);
|
||||
const args = `test factorio.settings ${prop} ${jsonArg(value)}`;
|
||||
await execCtlProcess(`instance config set-prop ${args}`);
|
||||
}
|
||||
|
||||
// Do this afterwards to leave enough to time for the
|
||||
@@ -909,6 +911,7 @@ describe("Integration of Clusterio", function() {
|
||||
await execCtl("instance config set 44 research_sync.load_plugin false");
|
||||
await execCtl("instance config set 44 statistics_exporter.load_plugin false");
|
||||
await execCtl("instance config set 44 subspace_storage.load_plugin false");
|
||||
await execCtl("instance config set 44 inventory_sync.load_plugin false");
|
||||
|
||||
await execCtl("instance start 44");
|
||||
const instance = (await getInstances()).get(44);
|
||||
@@ -1188,7 +1191,7 @@ describe("Integration of Clusterio", function() {
|
||||
});
|
||||
it("should allow setting all fields", async function() {
|
||||
const color = { r: 1, g: 0, b: 1, a: 0 };
|
||||
await execCtl(
|
||||
await execCtlProcess(
|
||||
"mod-pack create full-pack 0.17.59 " +
|
||||
"--description Description " +
|
||||
"--mods empty_mod:1.0.0 " +
|
||||
@@ -1220,7 +1223,7 @@ describe("Integration of Clusterio", function() {
|
||||
describe("mod-pack list", function() {
|
||||
let result = null;
|
||||
it("runs", async function() {
|
||||
result = await execCtl("mod-pack list");
|
||||
result = await execCtlProcess("mod-pack list");
|
||||
});
|
||||
it("contains defaults", function() {
|
||||
assert(result !== null, "Failed to return a value");
|
||||
@@ -1249,7 +1252,7 @@ describe("Integration of Clusterio", function() {
|
||||
reference.settings["runtime-global"].set("MyDouble", { value: 12.25 });
|
||||
reference.settings["runtime-per-user"].set("MyString", { value: "a-string" });
|
||||
await execCtl(`mod-pack import ${reference.toModPackString()}`);
|
||||
const result = await execCtl("mod-pack export imported-pack");
|
||||
const result = await execCtlProcess("mod-pack export imported-pack");
|
||||
const roundtrip = lib.ModPack.fromModPackString(result.stdout.trim());
|
||||
roundtrip.id = reference.id;
|
||||
assert.deepEqual(roundtrip, reference);
|
||||
@@ -1308,7 +1311,7 @@ describe("Integration of Clusterio", function() {
|
||||
|
||||
describe("mod show", function() {
|
||||
it("gives details of a mod", async function() {
|
||||
let result = await execCtl("mod show empty_mod 1.0.0");
|
||||
let result = await execCtlProcess("mod show empty_mod 1.0.0");
|
||||
let hash = await lib.hashFile(path.join("temp", "test", "mods", "empty_mod_1.0.0.zip"));
|
||||
let stat = await fs.stat(path.join("temp", "test", "mods", "empty_mod_1.0.0.zip"));
|
||||
assert.equal(
|
||||
@@ -1333,14 +1336,14 @@ describe("Integration of Clusterio", function() {
|
||||
|
||||
describe("mod list", function() {
|
||||
it("shows the list of mods", async function() {
|
||||
let result = await execCtl("mod list");
|
||||
let result = await execCtlProcess("mod list");
|
||||
assert(result.stdout.indexOf("empty_mod") !== -1, "empty_mod is not in the list");
|
||||
});
|
||||
});
|
||||
|
||||
describe("mod search", function() {
|
||||
it("searches the list of mods", async function() {
|
||||
let result = await execCtl("mod search 1.1 name:empty_mod");
|
||||
let result = await execCtlProcess("mod search 1.1 name:empty_mod");
|
||||
assert(result.stdout.indexOf("empty_mod") !== -1, "empty_mod is not in the result");
|
||||
});
|
||||
});
|
||||
@@ -1415,7 +1418,7 @@ describe("Integration of Clusterio", function() {
|
||||
describe("role edit", function() {
|
||||
it("should modify the given role", async function() {
|
||||
let args = "--name new --description \"A new role\" --set-perms";
|
||||
await execCtl(`role edit temp ${args}`);
|
||||
await execCtlProcess(`role edit temp ${args}`);
|
||||
let roles = await getControl().send(new lib.RoleListRequest());
|
||||
let newRole = roles.find(role => role.name === "new");
|
||||
if (newRole) { newRole.updatedAtMs = 0; }
|
||||
@@ -1497,7 +1500,7 @@ describe("Integration of Clusterio", function() {
|
||||
describe("user set-roles", function() {
|
||||
it("should set the roles on the user", async function() {
|
||||
getControl().userUpdates = [];
|
||||
await execCtl('user set-roles temp "Cluster Admin"');
|
||||
await execCtlProcess('user set-roles temp "Cluster Admin"');
|
||||
let users = await getControl().send(new lib.UserListRequest());
|
||||
let tempUser = users.find(user => user.id === "temp");
|
||||
assert.deepEqual(tempUser.roleIds, new Set([0]));
|
||||
@@ -1505,7 +1508,7 @@ describe("Integration of Clusterio", function() {
|
||||
});
|
||||
|
||||
it("should restrict actions based on roles", async function() {
|
||||
await execCtl('user set-roles temp "Player"');
|
||||
await execCtl("user set-roles temp Player");
|
||||
let tempControl;
|
||||
try {
|
||||
let tlsCa = await fs.readFile("test/file/tls/cert.pem");
|
||||
@@ -1650,7 +1653,7 @@ describe("Integration of Clusterio", function() {
|
||||
);
|
||||
});
|
||||
it("should reject multiple provided options", async function() {
|
||||
assert.rejects(execCtl("user import --bans --admins import-admins.json"));
|
||||
await assert.rejects(execCtl("user import --bans --admins import-admins.json"));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1659,7 +1662,6 @@ describe("Integration of Clusterio", function() {
|
||||
await execCtl("user create export_control");
|
||||
});
|
||||
it("should export users", async function() {
|
||||
slowTest(this);
|
||||
await execCtl("user set-admin export_user --create");
|
||||
await execCtl("user set-whitelisted export_user");
|
||||
await execCtl("user export --users user-export.json");
|
||||
@@ -1671,7 +1673,6 @@ describe("Integration of Clusterio", function() {
|
||||
});
|
||||
});
|
||||
it("should export bans", async function() {
|
||||
slowTest(this);
|
||||
await execCtl("user set-banned export_ban --create");
|
||||
await execCtl("user set-banned export_ban_reason --create --reason banned");
|
||||
await execCtl("user export --bans ban-export.json");
|
||||
@@ -1781,7 +1782,7 @@ describe("Integration of Clusterio", function() {
|
||||
assert(data.indexOf("restore_control") >= 0, "restore_control is missing");
|
||||
});
|
||||
it("should reject multiple provided options", async function() {
|
||||
assert.rejects(execCtl("user restore --bans --admins restore-admins.json"));
|
||||
await assert.rejects(execCtl("user restore --bans --admins restore-admins.json"));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+40
-11
@@ -11,6 +11,8 @@ const events = require("events");
|
||||
const lib = require("@clusterio/lib");
|
||||
const { LineSplitter, ConsoleTransport, logger } = lib;
|
||||
|
||||
const { selectTargetCommand, initialize: initializeCtl } = require("@clusterio/ctl");
|
||||
|
||||
// Make sure permissions from plugins are loaded
|
||||
require("../../plugins/global_chat/dist/node/index");
|
||||
require("../../plugins/player_auth/dist/node/index");
|
||||
@@ -84,6 +86,10 @@ class TestControl extends lib.Link {
|
||||
async handleUserUpdatesEvent(event) {
|
||||
this.userUpdates.push(...event.updates);
|
||||
}
|
||||
|
||||
async setLogSubscriptions(args) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
class TestControlConnector extends lib.WebSocketClientConnector {
|
||||
@@ -185,11 +191,27 @@ async function execHost(...args) {
|
||||
return await exec(...args);
|
||||
}
|
||||
|
||||
async function execCtl(...args) {
|
||||
async function execCtlProcess(...args) {
|
||||
args[0] = `node --enable-source-maps ../../packages/ctl ${args[0]}`;
|
||||
return await exec(...args);
|
||||
}
|
||||
|
||||
async function execCtl(command) {
|
||||
process.chdir("temp/test");
|
||||
try {
|
||||
const initArgs = await initializeCtl(command, control.plugins, true);
|
||||
if (!initArgs.shouldRun) {
|
||||
return;
|
||||
}
|
||||
|
||||
control.config = initArgs.controlConfig;
|
||||
const targetCommand = selectTargetCommand(initArgs.args, initArgs.rootCommands);
|
||||
await targetCommand.run(initArgs.args, control);
|
||||
} finally {
|
||||
process.chdir("../..");
|
||||
}
|
||||
}
|
||||
|
||||
async function sendRcon(instanceId, command) {
|
||||
return await control.sendTo({ instanceId }, new lib.InstanceSendRconRequest(command));
|
||||
}
|
||||
@@ -291,33 +313,38 @@ before(async function() {
|
||||
await execController("config set controller.tls_private_key ../../test/file/tls/key.pem");
|
||||
|
||||
console.log("Setting Controller Plugins");
|
||||
await execCtl("plugin add ../../plugins/global_chat");
|
||||
await execCtl("plugin add ../../plugins/research_sync");
|
||||
await execCtl("plugin add ../../plugins/statistics_exporter");
|
||||
await execCtl("plugin add ../../plugins/subspace_storage");
|
||||
await execCtl("plugin add ../../plugins/player_auth");
|
||||
await execCtlProcess("plugin add ../../plugins/global_chat");
|
||||
await execCtlProcess("plugin add ../../plugins/research_sync");
|
||||
await execCtlProcess("plugin add ../../plugins/statistics_exporter");
|
||||
await execCtlProcess("plugin add ../../plugins/subspace_storage");
|
||||
await execCtlProcess("plugin add ../../plugins/inventory_sync");
|
||||
await execCtlProcess("plugin add ../../plugins/player_auth");
|
||||
|
||||
console.log("Bootstrapping");
|
||||
await execController("bootstrap create-admin test");
|
||||
await execController("bootstrap create-ctl-config test");
|
||||
await execCtl("control-config set control.tls_ca ../../test/file/tls/cert.pem");
|
||||
await execCtlProcess("control-config set control.tls_ca ../../test/file/tls/cert.pem");
|
||||
|
||||
controllerProcess = await spawnNode("controller:", "../../packages/controller run", /Started controller/);
|
||||
|
||||
await execCtl("host create-config --id 4 --name host --generate-token");
|
||||
|
||||
const relativeFactorioDir = path.isAbsolute(factorioDir) ? factorioDir : path.join("..", "..", factorioDir);
|
||||
await execCtlProcess("host create-config --id 4 --name host --generate-token");
|
||||
await execHost(`config set host.factorio_directory ${relativeFactorioDir}`);
|
||||
await execHost("config set host.tls_ca ../../test/file/tls/cert.pem");
|
||||
|
||||
hostProcess = await spawnNode("host:", "../../packages/host run", /Started host/);
|
||||
|
||||
let tlsCa = await fs.readFile("test/file/tls/cert.pem");
|
||||
let controlConnector = new TestControlConnector(url, 2, tlsCa);
|
||||
const tlsCa = await fs.readFile("test/file/tls/cert.pem");
|
||||
const controlConnector = new TestControlConnector(url, 2, tlsCa);
|
||||
controlConnector.token = controlToken;
|
||||
control = new TestControl(controlConnector);
|
||||
await controlConnector.connect();
|
||||
|
||||
const initArgs = await initializeCtl(`--plugin-list ${pluginListPath} control-config list`, undefined, true);
|
||||
control.config = initArgs.controlConfig;
|
||||
control.plugins = initArgs.ctlPlugins;
|
||||
control.tlsCa = tlsCa;
|
||||
|
||||
const testPack = lib.ModPack.fromJSON({});
|
||||
testPack.id = 12;
|
||||
testPack.name = "subspace_storage-pack";
|
||||
@@ -358,10 +385,12 @@ module.exports = {
|
||||
TestControl,
|
||||
TestControlConnector,
|
||||
TestHostConnector,
|
||||
execCtlProcess,
|
||||
slowTest,
|
||||
get,
|
||||
exec,
|
||||
execCtl,
|
||||
execHost,
|
||||
sendRcon,
|
||||
getControl,
|
||||
spawn,
|
||||
|
||||
@@ -3,7 +3,7 @@ const assert = require("assert").strict;
|
||||
const lib = require("@clusterio/lib");
|
||||
|
||||
const {
|
||||
slowTest, exec, execCtl, sendRcon, getControl,
|
||||
slowTest, exec, execCtl, execCtlProcess, sendRcon, getControl,
|
||||
} = require("./index");
|
||||
|
||||
const instId = 48;
|
||||
@@ -31,12 +31,12 @@ describe("Clusterio Instance", function() {
|
||||
// Create a new instance
|
||||
await execCtl(`instance create ${instName} --id ${instId}`);
|
||||
await execCtl(`instance config set ${instName} factorio.enable_whitelist true`);
|
||||
await execCtl(`instance config set-prop ${instName} factorio.settings visibility "${visibility}"`);
|
||||
await execCtlProcess(`instance config set-prop ${instName} factorio.settings visibility "${visibility}"`);
|
||||
await execCtl(`instance config set-prop ${instName} factorio.settings require_user_verification false`);
|
||||
await execCtl(`instance assign ${instName} 4`);
|
||||
|
||||
for (const plugin of [
|
||||
"global_chat", "research_sync", "statistics_exporter", "subspace_storage", "player_auth",
|
||||
"global_chat", "research_sync", "statistics_exporter", "subspace_storage", "player_auth", "inventory_sync",
|
||||
]) {
|
||||
await execCtl(`instance config set ${instName} ${plugin}.load_plugin false`);
|
||||
}
|
||||
@@ -44,7 +44,7 @@ describe("Clusterio Instance", function() {
|
||||
// Create a new alt instance
|
||||
await execCtl(`instance create ${instAltName} --id ${instAltId}`);
|
||||
await execCtl(`instance config set ${instAltName} factorio.enable_whitelist true`);
|
||||
await execCtl(`instance config set-prop ${instAltName} factorio.settings visibility "${visibility}"`);
|
||||
await execCtlProcess(`instance config set-prop ${instAltName} factorio.settings visibility "${visibility}"`);
|
||||
await execCtl(`instance config set-prop ${instAltName} factorio.settings require_user_verification false`);
|
||||
await execCtl(`instance assign ${instAltName} 4`);
|
||||
await execCtl(`instance start ${instAltName}`);
|
||||
@@ -75,12 +75,12 @@ describe("Clusterio Instance", function() {
|
||||
if (savePatchingEnabled) {
|
||||
// IPC expects save patching to be enabled
|
||||
it("should do nothing for an unknown type", async function() {
|
||||
assert.rejects(getUser("DoesNotExist"), "User should not exist");
|
||||
await assert.rejects(getUser("DoesNotExist"), "User should not exist");
|
||||
await sendRcon(instId,
|
||||
`/sc ${requireApi} api.send_json("player_event",` +
|
||||
"{ type='invalid type', name='DoesNotExist' })"
|
||||
);
|
||||
assert.rejects(getUser("DoesNotExist"), "User was created");
|
||||
await assert.rejects(getUser("DoesNotExist"), "User was created");
|
||||
});
|
||||
it("should respond to a player join and leave event", async function() {
|
||||
await sendRcon(instId,
|
||||
|
||||
@@ -201,32 +201,16 @@ describe("lib/config/classes", function() {
|
||||
const filepath = path.join("temp", "test", "config_test.json");
|
||||
it("should throw for file and json errors", async function() {
|
||||
await fs.writeFile(filepath, "abc");
|
||||
assert.rejects(
|
||||
TestConfig.fromFile("local", filepath),
|
||||
new Error(
|
||||
`${filepath}: SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data`
|
||||
)
|
||||
);
|
||||
assert.rejects(
|
||||
TestConfig.fromFile("local", `${filepath}.notExist`),
|
||||
);
|
||||
await assert.rejects(TestConfig.fromFile("local", filepath));
|
||||
await assert.rejects(TestConfig.fromFile("local", `${filepath}.notExist`));
|
||||
});
|
||||
it("should throw on incorrect input passed", async function() {
|
||||
await fs.writeFile(filepath, "null");
|
||||
await assert.rejects(
|
||||
TestConfig.fromFile("local", filepath),
|
||||
new Error("Invalid config")
|
||||
);
|
||||
await assert.rejects(TestConfig.fromFile("local", filepath));
|
||||
await fs.writeFile(filepath, "undefined");
|
||||
assert.rejects(
|
||||
TestConfig.fromFile("local", filepath),
|
||||
new Error("Invalid config")
|
||||
);
|
||||
await assert.rejects(TestConfig.fromFile("local", filepath));
|
||||
await fs.writeFile(filepath, "[]");
|
||||
assert.rejects(
|
||||
TestConfig.fromFile("local", filepath),
|
||||
new Error("Invalid config")
|
||||
);
|
||||
await assert.rejects(TestConfig.fromFile("local", filepath));
|
||||
});
|
||||
it("should load defaults for missing fields", async function() {
|
||||
await fs.writeJSON(filepath, {
|
||||
|
||||
+7
-7
@@ -22,9 +22,9 @@ describe("rce_ops", function() {
|
||||
await lib.handlePluginUpdate("foo", [{ npmPackage: "foo" }]);
|
||||
});
|
||||
it("rejects when plugin not installed", async function() {
|
||||
assert.rejects(
|
||||
await assert.rejects(
|
||||
lib.handlePluginUpdate("foo", []),
|
||||
"Plugin foo is not installed on this machine"
|
||||
/Plugin foo is not installed on this machine/
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -50,16 +50,16 @@ describe("rce_ops", function() {
|
||||
});
|
||||
it("rejects when name too long", async function() {
|
||||
const pluginName = "a".repeat(215);
|
||||
assert.rejects(
|
||||
await assert.rejects(
|
||||
lib.handlePluginInstall(pluginName),
|
||||
`Invalid plugin name: ${pluginName}`
|
||||
{ message: `Invalid plugin name: ${pluginName}` }
|
||||
);
|
||||
});
|
||||
it("rejects when invalid symbol present", async function() {
|
||||
const pluginName = "?";
|
||||
assert.rejects(
|
||||
await assert.rejects(
|
||||
lib.handlePluginInstall(pluginName),
|
||||
`Invalid plugin name: ${pluginName}`
|
||||
{ message: `Invalid plugin name: ${pluginName}` },
|
||||
);
|
||||
});
|
||||
it("rejects unregistered packages", async function() {
|
||||
@@ -69,7 +69,7 @@ describe("rce_ops", function() {
|
||||
return { ok: false };
|
||||
};
|
||||
|
||||
assert.rejects(lib.handlePluginInstall("foo"), "Unknown plugin: foo");
|
||||
await assert.rejects(lib.handlePluginInstall("foo"), /Unknown plugin: foo/);
|
||||
assert.equal(calledWith, "https://registry.npmjs.com/foo");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -240,20 +240,20 @@ describe("lib/subscriptions", function() {
|
||||
subscriptions.handle(RegisteredEvent);
|
||||
});
|
||||
|
||||
it("should not accept subscriptions to unregistered events", function() {
|
||||
it("should not accept subscriptions to unregistered events", async function() {
|
||||
const request = new lib.SubscriptionRequest(RegisteredEvent.name, true);
|
||||
request.eventName = UnregisteredEvent.name;
|
||||
assert.rejects(
|
||||
await assert.rejects(
|
||||
subscriptions.handleRequest(getLink(0), request, addr({ controlId: 0 }), addr("controller")),
|
||||
new Error(`Unregistered Event class ${UnregisteredEvent.name}`)
|
||||
new Error(`Event ${UnregisteredEvent.name} is not a registered event`)
|
||||
);
|
||||
});
|
||||
it("should not accept subscriptions to events not handled by the class", function() {
|
||||
it("should not accept subscriptions to events not handled by the class", async function() {
|
||||
const request = new lib.SubscriptionRequest(RegisteredEvent.name, true);
|
||||
request.eventName = StringPermissionEvent.name;
|
||||
assert.rejects(
|
||||
await assert.rejects(
|
||||
subscriptions.handleRequest(getLink(0), request, addr({ controlId: 0 }), addr("controller")),
|
||||
new Error(`Event ${StringPermissionEvent.eventName} is not a registered as subscribable`)
|
||||
new Error(`Event ${StringPermissionEvent.name} is not a registered as subscribable`)
|
||||
);
|
||||
});
|
||||
it("should accept a subscription", async function() {
|
||||
|
||||
@@ -28,9 +28,9 @@ describe("messages/controller", function() {
|
||||
});
|
||||
it("rejects if updates are disabled", async function() {
|
||||
controller.config.set("controller.allow_remote_updates", false);
|
||||
assert.rejects(
|
||||
await assert.rejects(
|
||||
controlConnection.handleControllerUpdateRequest(new lib.ControllerUpdateRequest()),
|
||||
"Remote updates are disabled on this machine"
|
||||
/Remote updates are disabled on this machine/
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,9 +24,9 @@ describe("messages/controller", function() {
|
||||
});
|
||||
it("rejects if updates are disabled", async function() {
|
||||
host.config.set("host.allow_remote_updates", false);
|
||||
assert.rejects(
|
||||
await assert.rejects(
|
||||
host.handleHostUpdateRequest(new lib.HostUpdateRequest()),
|
||||
"Remote updates are disabled on this machine"
|
||||
/Remote updates are disabled on this machine/
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -115,9 +115,9 @@ describe("messages/plugin", function() {
|
||||
});
|
||||
it("rejects if updates are disabled on the controller", async function() {
|
||||
controller.config.set("controller.allow_plugin_updates", false);
|
||||
assert.rejects(
|
||||
await assert.rejects(
|
||||
controlConnection.handlePluginUpdateRequest(new lib.PluginUpdateRequest("foo")),
|
||||
"Plugin updates are disabled on this machine"
|
||||
/Plugin updates are disabled on this machine/
|
||||
);
|
||||
});
|
||||
it("runs on a host", async function() {
|
||||
@@ -127,9 +127,9 @@ describe("messages/plugin", function() {
|
||||
});
|
||||
it("rejects if updates are disabled on the host", async function() {
|
||||
host.config.set("host.allow_plugin_updates", false);
|
||||
assert.rejects(
|
||||
await assert.rejects(
|
||||
host.handlePluginUpdateRequest(new lib.PluginUpdateRequest("foo")),
|
||||
"Plugin updates are disabled on this machine"
|
||||
/Plugin updates are disabled on this machine/
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -154,22 +154,22 @@ describe("messages/plugin", function() {
|
||||
controller.config.set("controller.allow_plugin_install", true);
|
||||
await controlConnection.handlePluginInstallRequest(new lib.PluginInstallRequest("foo"));
|
||||
});
|
||||
it("rejects if updates are disabled on the controller", async function() {
|
||||
it("rejects if installs are disabled on the controller", async function() {
|
||||
controller.config.set("controller.allow_plugin_install", false);
|
||||
assert.rejects(
|
||||
await assert.rejects(
|
||||
controlConnection.handlePluginInstallRequest(new lib.PluginInstallRequest("foo")),
|
||||
"Plugin updates are disabled on this machine"
|
||||
/Plugin installs are disabled on this machine/
|
||||
);
|
||||
});
|
||||
it("runs on a host", async function() {
|
||||
host.config.set("host.allow_plugin_install", true);
|
||||
await host.handlePluginInstallRequest(new lib.PluginInstallRequest("foo"));
|
||||
});
|
||||
it("rejects if updates are disabled on the host", async function() {
|
||||
it("rejects if installs are disabled on the host", async function() {
|
||||
host.config.set("host.allow_plugin_install", false);
|
||||
assert.rejects(
|
||||
await assert.rejects(
|
||||
host.handlePluginInstallRequest(new lib.PluginInstallRequest("foo")),
|
||||
"Plugin updates are disabled on this machine"
|
||||
/Plugin installs are disabled on this machine/
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -218,7 +218,6 @@ class MockController {
|
||||
}
|
||||
|
||||
async function createControllerPlugin(ControllerPluginClass, info) {
|
||||
lib.registerPluginMessages([info]);
|
||||
let controller = new MockController();
|
||||
let metrics = {};
|
||||
let logger = new MockLogger();
|
||||
|
||||
Reference in new issue
Block a user