Merge branch 'master' into feature/plugin-install

This commit is contained in:
Cooldude2606 authored and GitHub committed 2026-09-12 22:10:52 +01:00
commit 512f62c22b
277 files changed
+2557 -2079

No files matched your search

+4 -10
View File
@@ -1,6 +1,5 @@
"use strict";
const assert = require("assert").strict;
const { compile } = require("@clusterio/lib");
import assert from "node:assert/strict";
import { compile } from "@clusterio/lib";
/**
* Generate a flat array of tests from a matrix of inputs.
@@ -10,7 +9,7 @@ const { compile } = require("@clusterio/lib");
* @returns {Array<{ [K in keyof T]: T[K][number] }>}
* An array of tuples, each containing one value from each input array.
*/
function testMatrix(...arrays) {
export function testMatrix(...arrays) {
return arrays.reduce((acc, curr) => acc.flatMap(a => curr.map(b => [...a, b])), [[]]);
}
@@ -21,7 +20,7 @@ function testMatrix(...arrays) {
* @param {{new(...args: T): object}} Class - The class which has toJSON and fromJSON methods.
* @param {T[]} tests - The tests inputs to pass to the class constructor.
*/
function testRoundTripJsonSerialisable(Class, tests) {
export function testRoundTripJsonSerialisable(Class, tests) {
const validate = compile(Class.jsonSchema);
for (const test of tests) {
const original = new Class(...test);
@@ -34,8 +33,3 @@ function testRoundTripJsonSerialisable(Class, tests) {
}
}
}
module.exports = {
testMatrix,
testRoundTripJsonSerialisable,
};
+2 -3
View File
@@ -1,6 +1,5 @@
"use strict";
const mock = require("../mock");
const controller = require("@clusterio/controller");
import * as mock from "../mock.js";
import * as controller from "@clusterio/controller";
describe("controller/src/BaseControllerPlugin", function() {
describe("class BaseControllerPlugin", function() {
+2 -3
View File
@@ -1,6 +1,5 @@
"use strict";
const assert = require("assert").strict;
const { ControlConnection } = require("@clusterio/controller");
import assert from "node:assert/strict";
import { ControlConnection } from "@clusterio/controller";
describe("controller/src/ControlConnection", function() {
describe(".handleControllerRestartRequest()", function() {
+22 -17
View File
@@ -1,16 +1,16 @@
"use strict";
const path = require("node:path");
const assert = require("assert").strict;
const { Controller, HostRecord, InstanceRecord, UserRecord } = require("@clusterio/controller");
const { EventEmitter } = require("stream");
const events = require("node:events");
const http = require("node:http");
const express = require("express");
import path from "node:path";
import assert from "node:assert/strict";
import { Controller, HostRecord, InstanceRecord, UserRecord } from "@clusterio/controller";
import events, { EventEmitter } from "node:events";
import http from "node:http";
import express from "express";
import { fileURLToPath } from "node:url";
const {
import {
ControllerConfig, Address, RequestError,
InstanceConfig, SystemInfo, Role, ModPack,
} = require("@clusterio/lib");
addPluginConfigFields,
} from "@clusterio/lib";
class MockEvent {}
@@ -32,10 +32,12 @@ class MockInstanceConfig extends EventEmitter {
}
describe("controller/src/Controller", function() {
describe("class Controller", function() {
describe("class Controller", async function() {
/** @type {Controller} */
let controller, mockInstanceConfig;
const controllerVersion = require("@clusterio/controller/package.json").version;
const controllerVersion = (
await import("@clusterio/controller/package.json", { with: { type: "json" }})
).default.version;
before(async function() {
const controllerConfig = new ControllerConfig("controller", { "controller.version": controllerVersion });
controller = new Controller({}, [], controllerConfig);
@@ -234,14 +236,17 @@ describe("controller/src/Controller", function() {
});
});
describe(".checkRestartRequired()", function() {
before(function() {
addPluginConfigFields([{ name: "restart_test" }]);
});
beforeEach(function() {
// Would not be needed if controller setup was beforeEach
controller.config.restartRequired = false;
controller.config.set("controller.version", controllerVersion);
controller.config.set("global_chat.load_plugin", true);
controller.config.set("restart_test.load_plugin", true);
controller.config.restartRequired = false;
controller.pluginInfos[0] = {
name: "global_chat",
requirePath: path.dirname(require.resolve("@clusterio/controller/package.json")),
name: "restart_test",
packagePath: fileURLToPath(import.meta.resolve("@clusterio/controller/package.json")),
version: controllerVersion,
};
});
@@ -256,7 +261,7 @@ describe("controller/src/Controller", function() {
controller.pluginInfos[0].version = "0.0.0";
controller.pluginInfos[0].webEntrypoint = true;
controller.pluginInfos[0].controllerEntrypoint = true;
controller.config.set("global_chat.load_plugin", false);
controller.config.set("restart_test.load_plugin", false);
controller.config.restartRequired = false; // Setting load_plugin requires a restart
const result = await controller.checkRestartRequired();
assert.equal(result, false);
+3 -4
View File
@@ -1,7 +1,6 @@
"use strict";
const assert = require("assert").strict;
const lib = require("@clusterio/lib");
const { User } = require("@clusterio/controller");
import assert from "node:assert/strict";
import * as lib from "@clusterio/lib";
import { User } from "@clusterio/controller";
describe("controller/src/User", function() {
describe("class User", function() {
+4 -5
View File
@@ -1,9 +1,8 @@
"use strict";
const assert = require("assert").strict;
const { HostDetails } = require("@clusterio/lib");
const { HostRecord } = require("@clusterio/controller");
import assert from "node:assert/strict";
import { HostDetails } from "@clusterio/lib";
import { HostRecord } from "@clusterio/controller";
const { testMatrix, testRoundTripJsonSerialisable } = require("../common");
import { testMatrix, testRoundTripJsonSerialisable } from "../common.js";
describe("controller/HostRecord", function () {
const samplePlugins = new Map([
+7 -8
View File
@@ -1,12 +1,11 @@
"use strict";
const assert = require("assert").strict;
const fs = require("node:fs/promises");
const http = require("http");
const https = require("https");
const util = require("util");
const events = require("node:events");
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import http from "node:http";
import https from "node:https";
import util from "node:util";
import events from "node:events";
const HttpCloser = require("@clusterio/controller/dist/node/src/HttpCloser").default;
import HttpCloser from "@clusterio/controller/dist/node/src/HttpCloser.js";
// Time to wait during async operations to ensure they happened in order.
const tick = 20;
+3 -4
View File
@@ -1,8 +1,7 @@
"use strict";
const assert = require("assert").strict;
const lib = require("@clusterio/lib");
import assert from "node:assert/strict";
import * as lib from "@clusterio/lib";
const { Controller, InstanceRecord, InstanceManager } = require("@clusterio/controller");
import { Controller, InstanceRecord, InstanceManager } from "@clusterio/controller";
describe("controller/InstanceManager", function () {
/** @type {Controller} */
+4 -5
View File
@@ -1,9 +1,8 @@
"use strict";
const assert = require("assert").strict;
const lib = require("@clusterio/lib");
const { InstanceRecord } = require("@clusterio/controller");
import assert from "node:assert/strict";
import * as lib from "@clusterio/lib";
import { InstanceRecord } from "@clusterio/controller";
const { testMatrix, testRoundTripJsonSerialisable } = require("../common");
import { testMatrix, testRoundTripJsonSerialisable } from "../common.js";
describe("controller/InstanceRecord", function () {
/** @type {lib.InstanceConfig} */
+3 -4
View File
@@ -1,7 +1,6 @@
"use strict";
const assert = require("assert").strict;
const { Role, SubscribableDatastore } = require("@clusterio/lib");
const { User } = require("@clusterio/controller");
import assert from "node:assert/strict";
import { Role, SubscribableDatastore } from "@clusterio/lib";
import { User } from "@clusterio/controller";
describe("controller/User", function () {
/** @type {SubscribableDatastore<UserRecord>} */
+6 -7
View File
@@ -1,10 +1,9 @@
"use strict";
const path = require("path");
const fs = require("node:fs/promises");
const assert = require("assert").strict;
const jwt = require("jsonwebtoken");
const { Role, SubscribableDatastore, ControllerConfig } = require("@clusterio/lib");
const { UserRecord, UserManager } = require("@clusterio/controller");
import path from "node:path";
import fs from "node:fs/promises";
import assert from "node:assert/strict";
import jwt from "jsonwebtoken";
import { Role, SubscribableDatastore, ControllerConfig } from "@clusterio/lib";
import { UserRecord, UserManager } from "@clusterio/controller";
const TEMP_DIR = path.join("temp", "test", "UserManager");
+4 -5
View File
@@ -1,9 +1,8 @@
"use strict";
const assert = require("assert").strict;
const { UserDetails, PlayerStats } = require("@clusterio/lib");
const { UserRecord } = require("@clusterio/controller");
import assert from "node:assert/strict";
import { UserDetails, PlayerStats } from "@clusterio/lib";
import { UserRecord } from "@clusterio/controller";
const { testMatrix, testRoundTripJsonSerialisable } = require("../common");
import { testMatrix, testRoundTripJsonSerialisable } from "../common.js";
describe("controller/UserRecord", function () {
it("should be round trip serialisable", function () {
+3 -4
View File
@@ -1,7 +1,6 @@
"use strict";
const assert = require("assert").strict;
const { Controller, WsServer } = require("@clusterio/controller");
const { ControllerConfig } = require("@clusterio/lib");
import assert from "node:assert/strict";
import { Controller, WsServer } from "@clusterio/controller";
import { ControllerConfig } from "@clusterio/lib";
describe("controller/src/WsServer", function() {
describe("class WsServer", function() {
+21 -9
View File
@@ -1,12 +1,11 @@
"use strict";
const assert = require("assert").strict;
const events = require("events");
const FormData = require("form-data");
const http = require("http");
import assert from "node:assert/strict";
import events from "node:events";
import FormData from "form-data";
import http from "node:http";
const { wait } = require("@clusterio/lib");
const routes = require("@clusterio/controller/dist/node/src/routes");
const mock = require("../mock");
import { wait } from "@clusterio/lib";
import * as routes from "@clusterio/controller/dist/node/src/routes.js";
import * as mock from "../mock.js";
describe("controller/src/routes", function() {
let controller;
@@ -103,7 +102,7 @@ describe("controller/src/routes", function() {
});
it("should report missing manifest and entrypoint", async function() {
controller.pluginInfos = [
{ name: "foo", version: "1.0.0", npmPackage: "foo" },
{ name: "foo", version: "1.0.0", npmPackage: "foo", webEntrypoint: "dist/web/index.js" },
{ name: "bar", version: "1.0.0", npmPackage: "bar", manifest: {} },
{ name: "baz", version: "1.0.0", npmPackage: "baz", manifest: { "baz.js": "remoteEntry.js" } },
];
@@ -116,6 +115,19 @@ describe("controller/src/routes", function() {
assert.equal(data[1].loaded, false);
assert.equal(data[1].enabled, false);
});
it("should not report a missing manifest for plugins without a web build", async function() {
controller.pluginInfos = [
{ name: "foo", version: "1.0.0", npmPackage: "foo", hostEntrypoint: "dist/node/host.js" },
{ name: "bar", version: "1.0.0", npmPackage: "bar", controllerEntrypoint: "dist/node/controller.js" },
{ name: "baz", version: "1.0.0", npmPackage: "baz", instanceConfigFields: {} },
];
let response = await fetch(endpoint);
assert.equal(response.status, 200);
let data = await response.json();
assert.deepEqual(data[0].web, {});
assert.equal(data[1].web.error, "Missing dist/web/manifest.json");
assert.equal(data[2].web.error, "Missing dist/web/manifest.json");
});
});
describe("/api/cluster-name", function() {
let endpoint;
+1 -2
View File
@@ -1,4 +1,3 @@
"use strict";
if (module === require.main) {
if (import.meta.main) {
process.stdout.write(JSON.stringify(process.argv.slice(2)));
}
+6 -7
View File
@@ -1,17 +1,16 @@
"use strict";
const child_process = require("child_process");
const util = require("util");
const assert = require("assert").strict;
const path = require("path");
import child_process from "node:child_process";
import util from "node:util";
import assert from "node:assert/strict";
import path from "node:path";
const { escapeArg } = require("../../packages/create/escape_arg");
import { escapeArg } from "../../packages/create/escape_arg.js";
const execFile = util.promisify(child_process.execFile);
async function exec(file, args) {
const command = [file, ...args.map(escapeArg)].join(" ");
const { stdout, stderr } = await execFile(
command,
{ shell: true, cwd: __dirname, env: { ...process.env, pct: "%"} }
{ shell: true, cwd: import.meta.dirname, env: { ...process.env, pct: "%"} }
);
return { stdout: JSON.parse(stdout), stderr };
}
+3 -8
View File
@@ -1,6 +1,5 @@
"use strict";
const { Command, CommandTree } = require("@clusterio/lib");
const { BaseCtlPlugin } = require("@clusterio/ctl");
import { Command, CommandTree } from "@clusterio/lib";
import { BaseCtlPlugin } from "@clusterio/ctl";
const testPluginCommands = new CommandTree({
name: "test-plugin", description: "Test plugin commands",
@@ -15,12 +14,8 @@ testPluginCommands.add(new Command({
},
}));
class CtlPlugin extends BaseCtlPlugin {
export class CtlPlugin extends BaseCtlPlugin {
async addCommands(rootCommand) {
rootCommand.add(testPluginCommands);
}
}
module.exports = {
CtlPlugin,
};
+3 -8
View File
@@ -1,9 +1,8 @@
"use strict";
const { BaseControllerPlugin } = require("@clusterio/controller");
import { BaseControllerPlugin } from "@clusterio/controller";
const { ControllerEcho, HostEcho } = require("./messages");
import { ControllerEcho, HostEcho } from "./messages.js";
class ControllerPlugin extends BaseControllerPlugin {
export class ControllerPlugin extends BaseControllerPlugin {
async init() {
this.logger.info("test_plugin controller loaded");
this.controller.handle(ControllerEcho, this.handleControllerEcho.bind(this));
@@ -15,7 +14,3 @@ class ControllerPlugin extends BaseControllerPlugin {
return request.text;
}
}
module.exports = {
ControllerPlugin,
};
+3 -8
View File
@@ -1,9 +1,8 @@
"use strict";
const { BaseHostPlugin } = require("@clusterio/host");
import { BaseHostPlugin } from "@clusterio/host";
const { HostEcho, HostEchoReceived } = require("./messages");
import { HostEcho, HostEchoReceived } from "./messages.js";
class HostPlugin extends BaseHostPlugin {
export class HostPlugin extends BaseHostPlugin {
async init() {
this.logger.info("test_plugin host loaded");
this.receivedEchoes = new Set();
@@ -20,7 +19,3 @@ class HostPlugin extends BaseHostPlugin {
return this.receivedEchoes.has(request.text);
}
}
module.exports = {
HostPlugin,
};
+14 -17
View File
@@ -1,24 +1,21 @@
"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");
import { ControllerEcho, HostEcho, HostEchoReceived } from "./messages.js";
module.exports = {
plugin: {
name: "test_plugin",
title: "Test Plugin",
description: "Plugin used to test the plugin system.",
export const plugin = {
name: "test_plugin",
title: "Test Plugin",
description: "Plugin used to test the plugin system.",
controllerEntrypoint: "controller",
hostEntrypoint: "host",
instanceEntrypoint: "instance",
ctlEntrypoint: "control",
controllerEntrypoint: "controller.js",
hostEntrypoint: "host.js",
instanceEntrypoint: "instance.js",
ctlEntrypoint: "control.js",
messages: [
ControllerEcho,
HostEcho,
HostEchoReceived,
],
},
messages: [
ControllerEcho,
HostEcho,
HostEchoReceived,
],
};
+2 -7
View File
@@ -1,12 +1,7 @@
"use strict";
const { BaseInstancePlugin } = require("@clusterio/host");
import { BaseInstancePlugin } from "@clusterio/host";
class InstancePlugin extends BaseInstancePlugin {
export class InstancePlugin extends BaseInstancePlugin {
async init() {
this.logger.info("test_plugin instance loaded");
}
}
module.exports = {
InstancePlugin,
};
+4 -11
View File
@@ -1,9 +1,8 @@
"use strict";
const { JsonBoolean, JsonString } = require("@clusterio/lib");
import { JsonBoolean, JsonString } from "@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 {
export class ControllerEcho {
static type = "request";
static src = "control";
static dst = "controller";
@@ -21,7 +20,7 @@ class ControllerEcho {
}
// Broadcast from the controller to the hosts by broadcastEventToHosts.
class HostEcho {
export class HostEcho {
static type = "event";
static src = "controller";
static dst = "host";
@@ -39,7 +38,7 @@ class HostEcho {
// Sent from ctl to a host, which answers with whether it received the given
// text via a HostEcho.
class HostEchoReceived {
export class HostEchoReceived {
static type = "request";
static src = "control";
static dst = "host";
@@ -55,9 +54,3 @@ class HostEchoReceived {
toJSON() { return this.text; }
static fromJSON(json) { return new this(json); }
}
module.exports = {
ControllerEcho,
HostEcho,
HostEchoReceived,
};
+1
View File
@@ -3,6 +3,7 @@
"version": "0.0.1",
"description": "Plugin used to test the plugin system, see #342",
"private": true,
"type": "module",
"main": "index.js",
"keywords": [
"clusterio-plugin"
+21 -15
View File
@@ -1,12 +1,11 @@
"use strict";
const assert = require("assert").strict;
const path = require("path");
import assert from "node:assert/strict";
import path from "node:path";
import { fileURLToPath } from "node:url";
const lib = require("@clusterio/lib");
const Host = require("@clusterio/host/dist/node/src/Host").default;
const Instance = require("@clusterio/host/dist/node/src/Instance").default;
const { _discoverInstances } = require("@clusterio/host/dist/node/src/Host");
const { HostConnector } = require("@clusterio/host/dist/node/host");
import * as lib from "@clusterio/lib";
import Host, { _discoverInstances } from "@clusterio/host/dist/node/src/Host.js";
import Instance from "@clusterio/host/dist/node/src/Instance.js";
import { HostConnector } from "@clusterio/host/dist/node/host.js";
describe("Host testing", function() {
describe("discoverInstances()", function() {
@@ -116,14 +115,19 @@ describe("Host testing", function() {
]);
});
});
describe(".checkRestartRequired()", function() {
describe(".checkRestartRequired()", async function() {
let host;
const hostVersion = require("@clusterio/host/package.json").version;
const hostVersion = (
await import("@clusterio/host/package.json", { with: { type: "json" }})
).default.version;
before(function() {
lib.addPluginConfigFields([{ name: "restart_test", hostEntrypoint: "host.js" }]);
});
beforeEach(function() {
// This can be used more generally, but i did not want to interfere with handleSyncUserListsEvent
const pluginInfos = [{
name: "global_chat",
requirePath: path.dirname(require.resolve("@clusterio/host/package.json")),
name: "restart_test",
packagePath: fileURLToPath(import.meta.resolve("@clusterio/host/package.json")),
version: hostVersion,
}];
const hostConfig = new lib.HostConfig("host", { "host.version": hostVersion });
@@ -141,7 +145,7 @@ describe("Host testing", function() {
host.pluginInfos[0].version = "0.0.0";
host.pluginInfos[0].hostEntrypoint = true;
host.pluginInfos[0].instanceEntrypoint = true;
host.config.set("global_chat.load_plugin", false);
host.config.set("restart_test.load_plugin", false);
host.config.restartRequired = false; // Setting load_plugin requires a restart
const result = await host.checkRestartRequired();
assert.equal(result, false);
@@ -174,9 +178,11 @@ describe("Host testing", function() {
assert.equal(host.config.restartRequired, true);
});
});
describe(".checkRestartDowngrade()", function() {
describe(".checkRestartDowngrade()", async function() {
let host;
const hostVersion = require("@clusterio/host/package.json").version;
const hostVersion = (
await import("@clusterio/host/package.json", { with: { type: "json" }})
).default.version;
beforeEach(function() {
const hostConfig = new lib.HostConfig("host", { "host.version": hostVersion });
const hostConnector = new HostConnector(hostConfig, []);
+2 -4
View File
@@ -1,8 +1,6 @@
"use strict";
const assert = require("assert").strict;
const mock = require("../mock");
import * as mock from "../mock.js";
const host = require("@clusterio/host");
import * as host from "@clusterio/host";
describe("host/src/BaseHostPlugin", function() {
describe("class BaseHostPlugin", function() {
+3 -4
View File
@@ -1,8 +1,7 @@
"use strict";
const assert = require("assert").strict;
const mock = require("../mock");
import assert from "node:assert/strict";
import * as mock from "../mock.js";
const host = require("@clusterio/host");
import * as host from "@clusterio/host";
describe("host/src/BaseInstancePlugin", function() {
describe("class BaseInstancePlugin", function() {
+34 -7
View File
@@ -1,11 +1,10 @@
"use strict";
const assert = require("assert").strict;
const path = require("path");
import assert from "node:assert/strict";
import path from "node:path";
const lib = require("@clusterio/lib");
const { PlayerStats, wait } = lib;
const Instance = require("@clusterio/host/dist/node/src/Instance").default;
const { MockConnector, MockServer } = require("../mock");
import * as lib from "@clusterio/lib";
import { PlayerStats, wait } from "@clusterio/lib";
import Instance from "@clusterio/host/dist/node/src/Instance.js";
import { MockConnector, MockServer } from "../mock.js";
const addr = lib.Address.fromShorthand;
@@ -39,6 +38,34 @@ describe("class Instance", function() {
});
});
describe(".checkModPackVersion()", function() {
function modPack(factorioVersion) {
return lib.ModPack.fromJSON({ name: "pack", factorio_version: factorioVersion });
}
it("should accept a mod pack for an older or equal Factorio version", function() {
instance.server.version = "1.1.91";
instance.checkModPackVersion(modPack("1.1"));
instance.checkModPackVersion(modPack("1.1.90"));
instance.checkModPackVersion(modPack("1.1.91"));
});
it("should reject a mod pack for a newer Factorio version", function() {
instance.server.version = "1.1.91";
assert.throws(
() => instance.checkModPackVersion(modPack("1.1.110")),
new lib.RequestError(
"Mod pack pack is for Factorio 1.1.110 which is newer than the Factorio 1.1.91 this instance runs"
)
);
assert.throws(() => instance.checkModPackVersion(modPack("2.0")), lib.RequestError);
});
it("should skip the check when the server version is unknown", function() {
instance.checkModPackVersion(modPack("2.0"));
});
});
describe("._recordPlayerJoin()", function() {
it("should add player to playersOnline", function() {
instance._recordPlayerJoin("player");
+4 -5
View File
@@ -1,9 +1,8 @@
"use strict";
const assert = require("assert").strict;
const path = require("path");
import assert from "node:assert/strict";
import path from "node:path";
const { FactorioServer } = require("@clusterio/host/dist/node/src/server");
const { _exportLocale } = require("@clusterio/host/dist/node/src/export");
import { FactorioServer } from "@clusterio/host/dist/node/src/server.js";
import { _exportLocale } from "@clusterio/host/dist/node/src/export.js";
describe("host/src/export", function() {
+6 -7
View File
@@ -1,11 +1,10 @@
"use strict";
const assert = require("assert").strict;
const fs = require("node:fs/promises");
const JSZip = require("jszip");
const path = require("path");
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import JSZip from "jszip";
import path from "node:path";
const lib = require("@clusterio/lib");
const patch = require("@clusterio/host/dist/node/src/patch");
import * as lib from "@clusterio/lib";
import * as patch from "@clusterio/host/dist/node/src/patch.js";
describe("host/patch", function() {
+9 -11
View File
@@ -1,14 +1,12 @@
"use strict";
const assert = require("assert").strict;
const events = require("events");
const fs = require("node:fs/promises");
const path = require("path");
import assert from "node:assert/strict";
import events from "node:events";
import fs from "node:fs/promises";
import path from "node:path";
const hostServer = require("@clusterio/host/dist/node/src/server");
const lib = require("@clusterio/lib");
const { wait } = lib;
const { testLines } = require("../lib/factorio/lines");
const { slowTest, externalTest } = require("../integration");
import * as hostServer from "@clusterio/host/dist/node/src/server.js";
import { Link, wait } from "@clusterio/lib";
import { testLines } from "../lib/factorio/lines.js";
import { slowTest, externalTest } from "../integration/index.js";
describe("host/server", function() {
@@ -326,7 +324,7 @@ describe("host/server", function() {
static jsonSchema = { type: "number" };
static fromJSON(json) { return new this(json); }
}
let eventFromJSON = lib.Link.eventFromJSON(NumberEvent, "NumberEvent");
let eventFromJSON = Link.eventFromJSON(NumberEvent, "NumberEvent");
ipcServer.handle("bad_event", async () => { eventFromJSON("not a number"); });
ipcServer.emit("ipc-bad_event", {});
await new Promise(resolve => setImmediate(resolve));
+18 -13
View File
@@ -1,22 +1,21 @@
"use strict";
const assert = require("assert").strict;
const fs = require("node:fs/promises");
const jwt = require("jsonwebtoken");
const path = require("path");
const events = require("events");
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import jwt from "jsonwebtoken";
import path from "node:path";
import events from "node:events";
const lib = require("@clusterio/lib");
const libBuildMod = require("@clusterio/lib/build_mod");
const { wait } = lib;
import * as lib from "@clusterio/lib";
import { wait } from "@clusterio/lib";
import * as libBuildMod from "@clusterio/lib/build_mod.js";
const testStrings = require("../lib/factorio/test_strings");
const {
import * as testStrings from "../lib/factorio/test_strings.js";
import {
TestControl, TestControlConnector, url, controlToken, slowTest,
execCtl, execCtlProcess, execController, execHost, sendRcon, getControl,
spawnNode, instancesDir, factorioDir, databaseDir, controllerConfigPath,
requiresFactorio, hasFactorio,
} = require("./index");
const { ControllerEcho, HostEchoReceived } = require("../file/test_plugin/messages");
} from "./index.js";
import { ControllerEcho, HostEchoReceived } from "../file/test_plugin/messages.js";
/** @returns {Promise<Map<number, lib.InstanceDetails>>} */
@@ -522,6 +521,9 @@ describe("Integration of Clusterio", function() {
it("runs", async function() {
await execCtl("controller update");
});
it("accepts --all", async function() {
await execCtl("controller update --all");
});
it("accepts --restart", async function() {
// We cannot restart the controller, so we check for controller error instead
await assert.rejects(
@@ -604,6 +606,9 @@ describe("Integration of Clusterio", function() {
it("runs", async function() {
await execCtl("host update 4");
});
it("accepts --all", async function() {
await execCtl("host update 4 --all");
});
it("accepts --restart", async function() {
// We cannot restart the host, so we check for host error instead
await assert.rejects(
+7 -8
View File
@@ -1,11 +1,10 @@
"use strict";
const assert = require("assert").strict;
const fs = require("node:fs/promises");
const jwt = require("jsonwebtoken");
const path = require("path");
const events = require("events");
const child_process = require("child_process");
const util = require("util");
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import jwt from "jsonwebtoken";
import path from "node:path";
import events from "node:events";
import child_process from "node:child_process";
import util from "node:util";
const cwd = path.join("temp", "test_integration");
+5 -6
View File
@@ -1,11 +1,10 @@
"use strict";
const assert = require("assert").strict;
const path = require("path");
import assert from "node:assert/strict";
import path from "node:path";
const { _exportLocale } = require("@clusterio/host/dist/node/src/export");
const { FactorioServer } = require("@clusterio/host/dist/node/src/server");
import { _exportLocale } from "@clusterio/host/dist/node/src/export.js";
import { FactorioServer } from "@clusterio/host/dist/node/src/server.js";
const { factorioDir, requiresFactorio } = require("./index");
import { factorioDir, requiresFactorio } from "./index.js";
describe("Integration of lib/factorio/export", function() {
+42 -74
View File
@@ -1,27 +1,26 @@
/* eslint-disable no-console */
"use strict";
const path = require("path");
const fs = require("node:fs/promises");
const { readFileSync } = require("node:fs");
const child_process = require("child_process");
const jwt = require("jsonwebtoken");
const util = require("util");
const events = require("events");
import path from "node:path";
import fs from "node:fs/promises";
import { readFileSync } from "node:fs";
import child_process from "node:child_process";
import jwt from "jsonwebtoken";
import util from "node:util";
import events from "node:events";
const lib = require("@clusterio/lib");
const { LineSplitter, ConsoleTransport, logger } = lib;
import * as lib from "@clusterio/lib";
import { LineSplitter, ConsoleTransport, logger } from "@clusterio/lib";
const { selectTargetCommand, initialize: initializeCtl } = require("@clusterio/ctl");
const { _listFactorioVersions } = require("@clusterio/host/dist/node/src/server");
import { selectTargetCommand, initialize as initializeCtl } from "@clusterio/ctl";
import { _listFactorioVersions } from "@clusterio/host/dist/node/src/server.js";
// Make sure permissions from plugins are loaded
require("../../plugins/global_chat/dist/node/index");
require("../../plugins/player_auth/dist/node/index");
require("../../plugins/research_sync/dist/node/index");
require("../../plugins/statistics_exporter/dist/node/index");
require("../../plugins/subspace_storage/dist/node/index");
import "../../plugins/global_chat/dist/node/index.js";
import "../../plugins/player_auth/dist/node/index.js";
import "../../plugins/research_sync/dist/node/index.js";
import "../../plugins/statistics_exporter/dist/node/index.js";
import "../../plugins/subspace_storage/dist/node/index.js";
class TestControl extends lib.Link {
export class TestControl extends lib.Link {
constructor(connector, subscribe = true) {
super(connector);
this.hostUpdates = [];
@@ -93,7 +92,7 @@ class TestControl extends lib.Link {
}
}
class TestControlConnector extends lib.WebSocketClientConnector {
export class TestControlConnector extends lib.WebSocketClientConnector {
register() {
this.sendHandshake(
new lib.MessageRegisterControl(
@@ -105,7 +104,7 @@ class TestControlConnector extends lib.WebSocketClientConnector {
}
}
class TestHostConnector extends lib.WebSocketClientConnector {
export class TestHostConnector extends lib.WebSocketClientConnector {
register() {
this.sendHandshake(
new lib.MessageRegisterHost(
@@ -119,7 +118,7 @@ class TestHostConnector extends lib.WebSocketClientConnector {
}
// Mark that this test takes a lot of time, or depends on a test that takes a lot of time.
function slowTest(test) {
export function slowTest(test) {
if (process.env.FAST_TEST) {
test.skip();
@@ -129,7 +128,7 @@ function slowTest(test) {
}
// Mark that this test depends on an external API and may be slow or flaky.
function externalTest(test) {
export function externalTest(test) {
if (process.env.NO_EXTERNAL_TEST) {
test.skip();
}
@@ -138,7 +137,7 @@ function externalTest(test) {
}
// Mark that this test or suite of tests requires a factorio install to run.
function requiresFactorio(testOrSuite) {
export function requiresFactorio(testOrSuite) {
if (testOrSuite.skip) {
if (!haveFactorioInstall) {
testOrSuite.skip();
@@ -152,11 +151,11 @@ function requiresFactorio(testOrSuite) {
}
}
function hasFactorio() {
export function hasFactorio() {
return haveFactorioInstall;
}
async function get(urlPath) {
export async function get(urlPath) {
const url = new URL("https://localhost:4443");
url.pathname = urlPath;
let res = await fetch(url);
@@ -191,16 +190,16 @@ let haveFactorioInstall;
const baseHostConfig = loadJSON("config-host.json");
let url = "https://localhost:4443/";
let controlToken = jwt.sign({ aud: "user", user: "test" }, Buffer.from("TestSecretDoNotUse", "base64"));
let instancesDir = path.join("temp", "test", "instances");
let modsDir = path.join("temp", "test", "mods");
let databaseDir = path.join("temp", "test", "database");
let factorioDir = getFactorioDir(baseHostConfig);
export let url = "https://localhost:4443/";
export let controlToken = jwt.sign({ aud: "user", user: "test" }, Buffer.from("TestSecretDoNotUse", "base64"));
export let instancesDir = path.join("temp", "test", "instances");
export let modsDir = path.join("temp", "test", "mods");
export let databaseDir = path.join("temp", "test", "database");
export let factorioDir = getFactorioDir(baseHostConfig);
let pluginListPath = path.join("temp", "test", "plugin-list.json");
let controllerConfigPath = path.join("temp", "test", "config-controller.json");
let hostConfigPath = path.join("temp", "test", "config-host.json");
let controlConfigPath = path.join("temp", "test", "config-control.json");
export let controllerConfigPath = path.join("temp", "test", "config-controller.json");
export let hostConfigPath = path.join("temp", "test", "config-host.json");
export let controlConfigPath = path.join("temp", "test", "config-control.json");
function childOptions(options) {
return {
@@ -210,29 +209,29 @@ function childOptions(options) {
};
}
function exec(command, options = {}) {
export function exec(command, options = {}) {
// Uncomment to show commands run in tests
// console.log(command);
return util.promisify(child_process.exec)(command, childOptions(options));
}
function execController(...args) {
export function execController(...args) {
args[0] = `node --enable-source-maps ../../packages/controller ${args[0]}`;
return exec(...args);
}
function execHost(...args) {
export function execHost(...args) {
args[0] = `node --enable-source-maps ../../packages/host ${args[0]}`;
return exec(...args);
}
function execCtlProcess(...args) {
export function execCtlProcess(...args) {
args[0] = `node --enable-source-maps ../../packages/ctl ${args[0]}`;
return exec(...args);
}
let inExecCtl = false;
async function execCtl(command) {
export async function execCtl(command) {
inExecCtl = true;
process.chdir("temp/test");
try {
@@ -257,16 +256,16 @@ afterEach(function() {
}
});
async function sendRcon(instanceId, command) {
export async function sendRcon(instanceId, command) {
return await control.sendTo({ instanceId }, new lib.InstanceSendRconRequest(command));
}
function getControl() {
export function getControl() {
return control;
}
/** @returns {Promise<child_process.ChildProcess>} */
function spawn(name, cmd, waitFor, options = {}) {
export function spawn(name, cmd, waitFor, options = {}) {
const silent = process.env.SILENT_TEST;
const bootstrap = !controllerProcess || !hostProcess;
@@ -303,7 +302,7 @@ function spawn(name, cmd, waitFor, options = {}) {
});
}
function spawnNode(name, cmd, waitFor, options) {
export function spawnNode(name, cmd, waitFor, options) {
return spawn(name, `node --enable-source-maps ${cmd}`, waitFor, options);
}
@@ -437,34 +436,3 @@ process.on("exit", () => {
if (hostProcess) { hostProcess.kill(); }
if (controllerProcess) { controllerProcess.kill(); }
});
module.exports = {
TestControl,
TestControlConnector,
TestHostConnector,
execController,
execCtlProcess,
slowTest,
externalTest,
requiresFactorio,
get,
exec,
execCtl,
execHost,
sendRcon,
getControl,
spawn,
spawnNode,
url,
controlToken,
instancesDir,
modsDir,
databaseDir,
factorioDir,
hasFactorio,
controllerConfigPath,
hostConfigPath,
controlConfigPath,
};
+7 -8
View File
@@ -1,14 +1,13 @@
"use strict";
const path = require("path");
const assert = require("assert").strict;
const lib = require("@clusterio/lib");
const fs = require("node:fs/promises");
import path from "node:path";
import assert from "node:assert/strict";
import * as lib from "@clusterio/lib";
import fs from "node:fs/promises";
const { testMatrix } = require("../common");
const {
import { testMatrix } from "../common.js";
import {
slowTest, exec, execCtl, execCtlProcess, sendRcon, getControl,
requiresFactorio, hasFactorio, instancesDir,
} = require("./index");
} from "./index.js";
const instId = 48;
const instName = "Integration";
+5 -7
View File
@@ -1,11 +1,9 @@
"use strict";
const assert = require("assert").strict;
const events = require("events");
const fs = require("node:fs/promises");
const jwt = require("jsonwebtoken");
import assert from "node:assert/strict";
import events from "node:events";
import jwt from "jsonwebtoken";
const { TestControlConnector, TestHostConnector, TestControl, get, exec, url, slowTest } = require("./index");
const { ConnectionClosed, ProtocolError, PolicyViolation, AuthenticationFailed } = require("@clusterio/lib");
import { TestControlConnector, TestHostConnector, TestControl, get, exec, url, slowTest } from "./index.js";
import { ConnectionClosed, ProtocolError, PolicyViolation, AuthenticationFailed } from "@clusterio/lib";
let token = jwt.sign({ aud: "user", user: "test" }, Buffer.from("TestSecretDoNotUse", "base64"));
let tokenHost = jwt.sign({ aud: "host", host: 0 }, Buffer.from("TestSecretDoNotUse", "base64"));
+8 -9
View File
@@ -1,17 +1,16 @@
"use strict";
const assert = require("assert").strict;
const fs = require("node:fs/promises");
const JSZip = require("jszip");
const path = require("path");
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import JSZip from "jszip";
import path from "node:path";
const { patch, SaveModule } = require("@clusterio/host/dist/node/src/patch");
const lib = require("@clusterio/lib");
import { patch, SaveModule } from "@clusterio/host/dist/node/src/patch.js";
import * as lib from "@clusterio/lib";
const { slowTest } = require("./index");
import { slowTest } from "./index.js";
// The server integration test is required to run before this one
require("./server");
import "./server.js";
describe("Integration of lib/factorio/patch", function() {
describe("patch()", function() {
+5 -6
View File
@@ -1,12 +1,11 @@
"use strict";
const assert = require("assert").strict;
import assert from "node:assert/strict";
const lib = require("@clusterio/lib");
const { Host, Instance, InstanceConnection } = require("@clusterio/host");
const { ControlConnection, Controller, HostConnection, InstanceRecord } = require("@clusterio/controller");
import * as lib from "@clusterio/lib";
import { Host, Instance, InstanceConnection } from "@clusterio/host";
import { ControlConnection, Controller, HostConnection, InstanceRecord } from "@clusterio/controller";
// Although there are no imports, the file has side-effects such as "before" which can effect tests
require("./index");
import "./index.js";
const addr = lib.Address.fromShorthand;
+9 -10
View File
@@ -1,15 +1,14 @@
"use strict";
const assert = require("assert").strict;
const events = require("events");
const fs = require("node:fs/promises");
const path = require("path");
import assert from "node:assert/strict";
import events from "node:events";
import fs from "node:fs/promises";
import path from "node:path";
const lib = require("@clusterio/lib");
const { InstanceManager } = require("@clusterio/controller");
const { FactorioServer, _getFactorioVersion } = require("@clusterio/host/dist/node/src/server");
const { logger } = lib;
import * as lib from "@clusterio/lib";
import { InstanceManager } from "@clusterio/controller";
import { FactorioServer, _getFactorioVersion } from "@clusterio/host/dist/node/src/server.js";
import { logger } from "@clusterio/lib";
const { slowTest, factorioDir, requiresFactorio } = require("./index");
import { slowTest, factorioDir, requiresFactorio } from "./index.js";
describe("Integration of host/src/server", function() {
+2 -3
View File
@@ -1,7 +1,6 @@
"use strict";
const assert = require("assert").strict;
import assert from "node:assert/strict";
const { ExponentialBackoff } = require("@clusterio/lib");
import { ExponentialBackoff } from "@clusterio/lib";
describe("lib/ExponentialBackoff", function() {
describe("class ExponentialBackoff", function() {
+4 -5
View File
@@ -1,10 +1,9 @@
/* eslint-disable node/no-sync */
"use strict";
const assert = require("assert").strict;
const fs = require("node:fs/promises");
const path = require("path");
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import path from "node:path";
const { LockFile } = require("@clusterio/lib");
import { LockFile } from "@clusterio/lib";
const baseDir = path.join("temp", "test", "lock_file");
+7 -8
View File
@@ -1,15 +1,14 @@
"use strict";
const assert = require("assert").strict;
const path = require("path");
const fs = require("node:fs/promises");
const stream = require("node:stream");
const JSZip = require("jszip"); // Added for creating mock zips
import assert from "node:assert/strict";
import path from "node:path";
import fs from "node:fs/promises";
import stream from "node:stream";
import JSZip from "jszip"; // Added for creating mock zips
// Capture native fetch before any potential mocks are applied
const nativeFetch = global.fetch;
const { ModStore, ModInfo, ModVersionEquality } = require("@clusterio/lib"); // Adjust path based on compiled output
const { externalTest } = require("../integration");
import { ModStore, ModInfo, ModVersionEquality } from "@clusterio/lib"; // Adjust path based on compiled output
import { externalTest } from "../integration/index.js";
const MODS_DIR = path.join("temp", "test", "mod_store", "mods");
const CACHE_FILE = path.join(MODS_DIR, "mod-info-cache.json");
+2 -3
View File
@@ -1,7 +1,6 @@
"use strict";
const assert = require("assert").strict;
import assert from "node:assert/strict";
const { RateLimiter, wait } = require("@clusterio/lib");
import { RateLimiter, wait } from "@clusterio/lib";
describe("lib/RateLimiter", function() {
+2 -2
View File
@@ -1,5 +1,5 @@
const assert = require("assert").strict;
const { ValueCache, wait } = require("@clusterio/lib");
import assert from "node:assert/strict";
import { ValueCache, wait } from "@clusterio/lib";
describe("ValueCache", function () {
describe("constructor", function () {
+4 -4
View File
@@ -1,12 +1,12 @@
const assert = require("assert").strict;
const { checkSingletonImport } = require("@clusterio/lib/dist/node/src/check_singleton_import");
import assert from "node:assert/strict";
import { checkSingletonImport } from "@clusterio/lib/dist/node/src/check_singleton_import.js";
// Import lib so it sets the global indicating lib has been imported.
require("@clusterio/lib");
import "@clusterio/lib";
describe("lib/check_singleton_import", function() {
it("Should throw an installation error if called twice", function() {
assert.throws(
() => checkSingletonImport(__filename),
() => checkSingletonImport(import.meta.filename),
{
message: /Attempt to import duplicate copy of @clusterio\/lib/,
code: "InstallationError",
+43 -11
View File
@@ -1,8 +1,7 @@
"use strict";
const assert = require("assert").strict;
import assert from "node:assert/strict";
const lib = require("@clusterio/lib");
const mock = require("../mock");
import * as lib from "@clusterio/lib";
import * as mock from "../mock.js";
const addr = lib.Address.fromShorthand;
@@ -16,39 +15,68 @@ describe("lib/command", function() {
let testControl = new mock.MockControl(controlConnector);
let testController = new lib.Link(controllerConnector);
testController.handle(
lib.HostListRequest, () => [new lib.HostDetails("0.1", "Test Host", 11, false)]
lib.HostListRequest, () => [
new lib.HostDetails("0.1", "Test Host", 11, false),
new lib.HostDetails("0.1", "22", 12, false),
new lib.HostDetails("0.1", "12", 13, false),
]
);
testController.handle(
lib.InstanceDetailsListRequest, () => [new lib.InstanceDetails("Test Instance", 57, 4, undefined, "stopped")]
lib.InstanceDetailsListRequest, () => [
new lib.InstanceDetails("Test Instance", 57, 4, undefined, "stopped"),
new lib.InstanceDetails("10", 58, 4, undefined, "stopped"),
new lib.InstanceDetails("58", 59, 4, undefined, "stopped"),
]
);
testController.handle(lib.RoleListRequest, () => [testRole.toJSON()]);
let numericRole = lib.Role.fromJSON({ id: 29, name: "77", description: "Test", permissions: [] });
testController.handle(lib.RoleListRequest, () => [testRole.toJSON(), numericRole.toJSON()]);
describe("resolveHost", function() {
it("should pass an integer like string back", async function() {
assert.equal(await lib.resolveHost(null, "123"), 123);
it("should resolve an integer like string as an id", async function() {
assert.equal(await lib.resolveHost(testControl, "11"), 11);
});
it("should resolve a host name with the controller", async function() {
assert.equal(await lib.resolveHost(testControl, "Test Host"), 11);
});
it("should resolve an integer like name if no host has that id", async function() {
assert.equal(await lib.resolveHost(testControl, "22"), 12);
});
it("should prefer the id over an integer like name", async function() {
assert.equal(await lib.resolveHost(testControl, "12"), 12);
});
it("should throw if host is not found", async function() {
await assert.rejects(
lib.resolveHost(testControl, "invalid"),
new lib.CommandError("No host named invalid")
);
await assert.rejects(
lib.resolveHost(testControl, "123"),
new lib.CommandError("No host named 123")
);
});
});
describe("resolveInstance", function() {
it("should pass an integer like string back", async function() {
assert.equal(await lib.resolveInstance(null, "123"), 123);
it("should resolve an integer like string as an id", async function() {
assert.equal(await lib.resolveInstance(testControl, "57"), 57);
});
it("should resolve an instance name with the controller", async function() {
assert.equal(await lib.resolveInstance(testControl, "Test Instance"), 57);
});
it("should resolve an integer like name if no instance has that id", async function() {
assert.equal(await lib.resolveInstance(testControl, "10"), 58);
});
it("should prefer the id over an integer like name", async function() {
assert.equal(await lib.resolveInstance(testControl, "58"), 58);
});
it("should throw if instance is not found", async function() {
await assert.rejects(
lib.resolveInstance(testControl, "invalid"),
new lib.CommandError("No instance named invalid")
);
await assert.rejects(
lib.resolveInstance(testControl, "123"),
new lib.CommandError("No instance named 123")
);
});
});
describe("retrieveRole", function() {
@@ -58,6 +86,10 @@ describe("lib/command", function() {
it("should retrieve a role by name from a string", async function() {
assert.deepEqual(await lib.retrieveRole(testControl, "Test Role"), testRole);
});
it("should retrieve a role by an integer like name if no role has that id", async function() {
assert.deepEqual(await lib.retrieveRole(testControl, "28"), testRole);
assert.deepEqual(await lib.retrieveRole(testControl, "77"), numericRole);
});
it("should throw if role is not found", async function() {
await assert.rejects(
lib.retrieveRole(testControl, "invalid"),
+13 -5
View File
@@ -1,8 +1,7 @@
"use strict";
const lib = require("@clusterio/lib");
const fs = require("node:fs/promises");
const path = require("path");
const assert = require("assert").strict;
import * as lib from "@clusterio/lib";
import fs from "node:fs/promises";
import path from "node:path";
import assert from "node:assert/strict";
const CA = lib.ConfigAccess;
@@ -396,6 +395,15 @@ describe("lib/config/classes", function() {
const json = JSON.parse(await fs.readFile(filepath, "utf8"));
assert.deepEqual(json, testInstance.toJSON());
});
it("should not be readable by other users", async function() {
if (process.platform === "win32") {
this.skip();
}
const testInstance = new TestConfig("local", { "alpha.foo": "a" }, filepath);
testInstance.set("alpha.foo", "b"); // Sets the dirty flag
await testInstance.save();
assert.equal((await fs.stat(filepath)).mode & 0o777, 0o600);
});
it("should clear the dirty flag after saving", async function() {
const testInstance = new TestConfig("local", { "alpha.foo": "a" }, filepath);
testInstance.set("alpha.foo", "b"); // Sets the dirty flag
+2 -3
View File
@@ -1,6 +1,5 @@
"use strict";
const lib = require("@clusterio/lib");
const assert = require("assert").strict;
import * as lib from "@clusterio/lib";
import assert from "node:assert/strict";
// Each migration test should indicate which version introduced the migration
+3 -4
View File
@@ -1,8 +1,7 @@
"use strict";
const assert = require("assert").strict;
const lib = require("@clusterio/lib");
import assert from "node:assert/strict";
import * as lib from "@clusterio/lib";
const validators = lib.Config.validators;
const { ControllerConfig, HostConfig, InstanceConfig, ControlConfig } = lib;
import { ControllerConfig, HostConfig, InstanceConfig, ControlConfig } from "@clusterio/lib";
describe("lib/config/validators", function() {
describe("all", function() {
+3 -4
View File
@@ -1,8 +1,7 @@
"use strict";
const assert = require("assert").strict;
const { HostDetails } = require("@clusterio/lib");
import assert from "node:assert/strict";
import { HostDetails } from "@clusterio/lib";
const { testMatrix, testRoundTripJsonSerialisable } = require("../../common");
import { testMatrix, testRoundTripJsonSerialisable } from "../../common.js";
describe("lib/data/HostDetails", function () {
+3 -4
View File
@@ -1,8 +1,7 @@
"use strict";
const assert = require("assert").strict;
const { InstanceDetails } = require("@clusterio/lib");
import assert from "node:assert/strict";
import { InstanceDetails } from "@clusterio/lib";
const { testMatrix, testRoundTripJsonSerialisable } = require("../../common");
import { testMatrix, testRoundTripJsonSerialisable } from "../../common.js";
describe("lib/data/InstanceDetails", function () {
+7 -8
View File
@@ -1,13 +1,12 @@
"use strict";
const assert = require("assert").strict;
const fs = require("node:fs/promises");
const path = require("path");
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import path from "node:path";
const lib = require("@clusterio/lib");
const libBuildMod = require("@clusterio/lib/build_mod");
const { ModInfo, ModDependency } = lib;
import * as lib from "@clusterio/lib";
import * as libBuildMod from "@clusterio/lib/build_mod.js";
import { ModInfo, ModDependency } from "@clusterio/lib";
const { testMatrix, testRoundTripJsonSerialisable } = require("../../common");
import { testMatrix, testRoundTripJsonSerialisable } from "../../common.js";
describe("lib/data/ModInfo", function() {
describe("class ModDependency", function() {
+4 -5
View File
@@ -1,9 +1,8 @@
"use strict";
const assert = require("assert").strict;
const zlib = require("zlib");
import assert from "node:assert/strict";
import zlib from "node:zlib";
const lib = require("@clusterio/lib");
const { ModInfo, ModPack, getInstalledModUpdates, applyModRecordAdvisories } = lib;
import * as lib from "@clusterio/lib";
import { ModInfo, ModPack, getInstalledModUpdates, applyModRecordAdvisories } from "@clusterio/lib";
describe("lib/data/ModPack", function() {
+2 -3
View File
@@ -1,6 +1,5 @@
"use strict";
const assert = require("assert").strict;
const lib = require("@clusterio/lib");
import assert from "node:assert/strict";
import * as lib from "@clusterio/lib";
describe("lib/data/Role", function() {
let defaultPermissions;
+2 -4
View File
@@ -1,8 +1,6 @@
"use strict";
const assert = require("assert").strict;
import assert from "node:assert/strict";
const lib = require("@clusterio/lib");
const { SystemInfo } = lib;
import { SystemInfo } from "@clusterio/lib";
describe("lib/data/SystemInfo", function() {
+3 -4
View File
@@ -1,8 +1,7 @@
"use strict";
const assert = require("assert").strict;
const { UserDetails, PlayerStats } = require("@clusterio/lib");
import assert from "node:assert/strict";
import { UserDetails, PlayerStats } from "@clusterio/lib";
const { testMatrix, testRoundTripJsonSerialisable } = require("../../common");
import { testMatrix, testRoundTripJsonSerialisable } from "../../common.js";
describe("lib/data/UserDetails", function () {
it("should be round trip serialisable", function () {
+2 -4
View File
@@ -1,7 +1,5 @@
"use strict";
const assert = require("assert").strict;
const lib = require("@clusterio/lib");
const { Address } = lib;
import assert from "node:assert/strict";
import { Address } from "@clusterio/lib";
describe("lib/data/messages_core", function() {
describe("class Address", function() {
+3 -4
View File
@@ -1,8 +1,7 @@
"use strict";
const assert = require("assert").strict;
const lib = require("@clusterio/lib");
import assert from "node:assert/strict";
import * as lib from "@clusterio/lib";
const { testMatrix, testRoundTripJsonSerialisable } = require("../../common");
import { testMatrix, testRoundTripJsonSerialisable } from "../../common.js";
describe("lib/data/version", function() {
describe("isVersionEquality()", function() {
+4 -5
View File
@@ -1,9 +1,8 @@
"use strict";
const assert = require("assert").strict;
const fs = require("node:fs/promises");
const path = require("path");
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import path from "node:path";
const lib = require("@clusterio/lib");
import * as lib from "@clusterio/lib";
describe("lib/database", function() {
describe("mapToObject()", function() {
+5 -6
View File
@@ -1,9 +1,8 @@
"use strict";
const events = require("events");
const assert = require("assert").strict;
const lib = require("@clusterio/lib");
const fs = require("node:fs/promises");
const path = require("path");
import events from "node:events";
import assert from "node:assert/strict";
import * as lib from "@clusterio/lib";
import fs from "node:fs/promises";
import path from "node:path";
class MockDatastoreProvider extends lib.DatastoreProvider {
constructor() {
+2 -3
View File
@@ -1,7 +1,6 @@
"use strict";
const assert = require("assert").strict;
import assert from "node:assert/strict";
const lib = require("@clusterio/lib");
import * as lib from "@clusterio/lib";
describe("lib/errors", function() {
describe("class InvalidMessage", function() {
+4 -4
View File
@@ -1,8 +1,8 @@
const assert = require("assert").strict;
const { readFile } = require("node:fs/promises");
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
const { fetchFactorioVersions } = require("@clusterio/lib");
const { slowTest, externalTest } = require("../../integration");
import { fetchFactorioVersions } from "@clusterio/lib";
import { slowTest, externalTest } from "../../integration/index.js";
describe("FactorioVersions", function() {
describe("fetchFactorioVersions", function() {
+3 -3
View File
@@ -1,7 +1,7 @@
const assert = require("assert").strict;
import assert from "node:assert/strict";
const { fetchLatestReleases, resolveReleaseChannel } = require("@clusterio/lib");
const { slowTest, externalTest } = require("../../integration");
import { fetchLatestReleases, resolveReleaseChannel } from "@clusterio/lib";
import { slowTest, externalTest } from "../../integration/index.js";
describe("LatestReleases", function() {
describe("fetchLatestReleases", function() {
+5 -6
View File
@@ -1,11 +1,10 @@
"use strict";
const assert = require("assert").strict;
const zlib = require("zlib");
import assert from "node:assert/strict";
import zlib from "node:zlib";
const lib = require("@clusterio/lib");
import * as lib from "@clusterio/lib";
const testStrings = require("./test_strings");
const testSettings = require("./test_settings");
import * as testStrings from "./test_strings.js";
import * as testSettings from "./test_settings.js";
// Simple linear congruential random number generator
+1 -7
View File
@@ -1,7 +1,5 @@
// Lines for testing the factorio output
"use strict";
let testLines = new Map([
export const testLines = new Map([
[
" 1.306 Info RemoteCommandProcessor.cpp:131: Starting RCON interface at port 4000",
{
@@ -219,7 +217,3 @@ let testLines = new Map([
},
],
]);
module.exports = {
testLines,
};
+10 -24
View File
@@ -1,6 +1,5 @@
"use strict";
const default_map_gen_settings = {
export const default_map_gen_settings = {
autoplace_controls: {
"coal": { frequency: 1, size: 1, richness: 1 },
"copper-ore": { frequency: 1, size: 1, richness: 1 },
@@ -37,7 +36,7 @@ const default_map_gen_settings = {
water: 1,
};
const default_map_settings = {
export const default_map_settings = {
pollution: {
enabled: true,
diffusion_ratio: 0.02,
@@ -140,7 +139,7 @@ const default_map_settings = {
},
};
const modified_map_gen_settings = {
export const modified_map_gen_settings = {
autoplace_controls: {
"coal": { frequency: 1, size: 1, richness: 1 },
"copper-ore": { frequency: 0.5, size: 1, richness: 1 },
@@ -196,7 +195,7 @@ const modified_map_gen_settings = {
water: 0.3333333432674408,
};
const modified_map_settings = {
export const modified_map_settings = {
pollution: {
enabled: true,
diffusion_ratio: 0.02,
@@ -299,7 +298,7 @@ const modified_map_settings = {
},
};
const modified_v2_map_gen_settings = {
export const modified_v2_map_gen_settings = {
autoplace_controls: {
"coal": { frequency: 1, size: 1, richness: 1 },
"copper-ore": { frequency: 0.5, size: 1, richness: 1 },
@@ -362,7 +361,7 @@ const modified_v2_map_gen_settings = {
territory_settings: null,
};
const modified_v2_map_settings = {
export const modified_v2_map_settings = {
pollution: {
enabled: true,
diffusion_ratio: 0.02,
@@ -467,7 +466,7 @@ const modified_v2_map_settings = {
},
};
const modified_v2_1_map_gen_settings = {
export const modified_v2_1_map_gen_settings = {
"autoplace_controls": {
"coal": { "frequency": 1, "size": 1, "richness": 1 },
"copper-ore": { "frequency": 0.5, "size": 1, "richness": 1 },
@@ -516,7 +515,7 @@ const modified_v2_1_map_gen_settings = {
"territory_settings": null,
};
const modified_v2_1_map_settings = {
export const modified_v2_1_map_settings = {
"pollution": {
"enabled": true,
"diffusion_ratio": 0.02,
@@ -615,7 +614,7 @@ const modified_v2_1_map_settings = {
},
};
const modified_v2_1_13_map_settings = {
export const modified_v2_1_13_map_settings = {
...modified_v2_1_map_settings,
"enemy_expansion": {
...modified_v2_1_map_settings.enemy_expansion,
@@ -623,7 +622,7 @@ const modified_v2_1_13_map_settings = {
},
};
const modified_space_age_map_gen_settings = {
export const modified_space_age_map_gen_settings = {
autoplace_controls: {
"aquilo_crude_oil": { frequency: 1, size: 1, richness: 1 },
"calcite": { frequency: 1, size: 1, richness: 1 },
@@ -701,16 +700,3 @@ const modified_space_age_map_gen_settings = {
no_enemies_mode: false,
territory_settings: null,
};
module.exports = {
default_map_gen_settings,
default_map_settings,
modified_map_gen_settings,
modified_map_settings,
modified_v2_map_gen_settings,
modified_v2_map_settings,
modified_v2_1_map_gen_settings,
modified_v2_1_map_settings,
modified_v2_1_13_map_settings,
modified_space_age_map_gen_settings,
};
+6 -16
View File
@@ -1,7 +1,6 @@
"use strict";
// Exchange strings for testing with
const default_ = `
export default `
>>>eNpjZGBkUGQAgwZ7EOZgSc5PzIHxQJgrOb+gILVIN78oFVmYM
7moNCVVNz8TVXFqXmpupW5SYjGKYo7Movw8dBNYi0vy81BFSopSU
4uRRbhLixLzMktz0fUyMF5imubZ0CLHAML/6xkU/v8HYSDrAdAvI
@@ -14,7 +13,7 @@ MPlQMQJEAG2EO4yRgjTod+B0UEeJiuJUALUb8SA7IYUhA9Pwqw9j
GQ/mkMwIwLZH2giKg5YooELZGEKnHjBDHcNMDwvsMN4DvMdGJlBD
JCqL0AxCA8kAzMKQgs4gIObmQEBgGmjrO/lHgDxa6G4<<<`;
const modified = `
export const modified = `
>>>eNp1UzFs00AUvZ8mbRIEypCBSlCCyFAhOQqBKULxlQUxMCCxgnCc
S2rh2On5LAgMZOjAgISEurQLXSmCBTGwRWKhEkgIJraiMgBiaGmFOiC
FO5/PsUz6pfv+9/6/99+/kwEBOo2k6c83NhrZtOkaNkIDXa286fZ6hG
@@ -31,7 +30,7 @@ jnh3gmXQRElVwYyxI8x4FMqOzsu4edrKK6hNZ5wU7V9G+ufEFL+7yHi
cySQMp7wDHnRsBW571ORGn6fH2fUDj/FwV0iUbXPMbmTP7akkt8ChiL
/BPceEs3j1M3iz71/ObT9tA==<<<`;
const modified_v2 = `
export const modified_v2 = `
>>>eNp1Uz9oU0EY/y5N2iRWzRCFitSAGURIjNEpSN7VRRwcBFeJl5dL
+ujLe+3de9XoYIYODoogXdrFrlZ0EQe3gIsFBdHJrVIHBYdWi3QQ4l3
u3csz1Q++7333+31/75IYAJwRCpNx0yU2QNfQmjbd+XnKCi6jgg/hlM
@@ -49,7 +48,7 @@ Ln6IET6h2alhiMgvQ3SGxnDDDd32daT/yCD5fQ8R3WMEyeN/PENaNmy
E5utYOI24z/cT+oQf48FdgozaFZg6qX+1KqW+GYyy4qNZgFNY/0IAHj
RI5Q/27QvL<<<`;
const modified_v2_1 = `
export const modified_v2_1 = `
>>>eNp1U89rE0EUftM0NYlVc4iCIjWHHkRICNFTkezoRTx4ELxKnGwm
unSz087uVlsP5uDBg1CQXvSiVyt6EY9CwIu9if4DlXqwt1aK9CDEeZm
dyZrgg/f49nu/37BTQCAPGYDZaVcwH6DnGC24YmmJy4qQHAAsnXdl3O
@@ -65,7 +64,7 @@ JYkZplFakZG9jCkjt5gyQ4nHFv3qqTPmsjWiYeiVKOkP57RB9xB0Dvk
X9skPmJ46b3GGNscC4FCtiwbc3PTPrUX46YL/qSDp8BMOqAJI8C+u/S
peyl8e7GC3Ce2le3AIs019Y//gU3lOV1<<<`;
const modified_space_age = `
export const modified_space_age = `
>>>eNp1VEFoE0EUnUkb27TWBomFSqkBexAhMVZPRbJbL+LBg+BV1slm
kgzd7LYzs9HYgz304EEQpJf2Yq9W9CIevBW8WFAQPXmr1IOCh1aL9CD
UmZ2dzW4aB2by972Z9//7MyQFALggJpjIogWfOJ5lU7+KLY84ACwZeg
@@ -86,7 +85,7 @@ Gx0+5gy4RUpPimXd3JJg0hSVAZVaD42oXlGs+OdLeL8NIjXUO043NJp
38bydxUydeQi4j66kCmzxzUMyYTVaPneF1Uj+vlxQH+ZT82gl0Du2he
Y+lJ/JEpK/WZNmBM/mgXgnKlfCACLzljjH+s/hYo=<<<`;
const modified_v2_1_13 = `
export const modified_v2_1_13 = `
>>>eNp1U89rE0EUftMmNYm15hAFRWoOPYiQEKKgFMmOXsSDB0HwJHGy
mejSzU47u1utHszBgwdBkF70olcrehHvAS/2JvUfqNSDvbVSpAehzsv
sTNYEH7zHt9/7/YadAAIzSmE64wrmA/QcowVXLC5yWRGSA4Cl866M27
@@ -101,12 +100,3 @@ JYkZpmFakZGd1AkjtxhSQjYziizPKvHmmTPmUDWuYoiVKOgP57RB9yS
0Dvo1NMEfJJWw1i+YLmuywM1C1jwbPKaGnjffEMETl1yE9Qxu/9C7rp
u3nVP+RQebGLpzeY4SxwbkUKGDDtjU/J9P3/nrIfNHXdPAWgFF7JHkZ
0L+YLmXPjcc3XoAz1D69BVjk1sULxb83nuUA<<<`;
module.exports = {
default: default_,
modified,
modified_v2,
modified_v2_1,
modified_v2_1_13,
modified_space_age,
};
+16 -7
View File
@@ -1,11 +1,10 @@
"use strict";
const assert = require("assert").strict;
const fs = require("node:fs/promises");
const http = require("node:http");
const path = require("path");
const util = require("node:util");
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import http from "node:http";
import path from "node:path";
import util from "node:util";
const lib = require("@clusterio/lib");
import * as lib from "@clusterio/lib";
describe("lib/file_ops", function() {
let baseDir = path.join("temp", "test", "file_ops");
@@ -101,6 +100,16 @@ describe("lib/file_ops", function() {
await assert.rejects(fs.access(target.replace(".txt", ".tmp.txt")), "temporary was left behind");
assert.equal(await fs.readFile(target, "utf8"), "current");
});
it("should apply mode to a leftover temporary file", async function() {
if (process.platform === "win32") {
this.skip();
}
let target = path.join(baseDir, "safe", "mode.txt");
await fs.writeFile(target.replace(".txt", ".tmp.txt"), "stale", { mode: 0o644 });
await lib.safeOutputFile(target, "private", { mode: 0o600 });
assert.equal((await fs.stat(target)).mode & 0o777, 0o600);
assert.equal(await fs.readFile(target, "utf8"), "private");
});
it("should handle creating file in current working directory", async function() {
let target = "temporary-file-made-to-test-cwd.txt";
try {
+4 -5
View File
@@ -1,9 +1,8 @@
"use strict";
const assert = require("assert").strict;
const fs = require("node:fs/promises");
const path = require("path");
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import path from "node:path";
const lib = require("@clusterio/lib");
import * as lib from "@clusterio/lib";
describe("lib/hash", function() {
+2 -3
View File
@@ -1,8 +1,7 @@
"use strict";
const assert = require("assert").strict;
import assert from "node:assert/strict";
const hrtime = process.hrtime.bigint;
const lib = require("@clusterio/lib");
import * as lib from "@clusterio/lib";
describe("lib/helpers", function() {
+3 -4
View File
@@ -1,7 +1,6 @@
"use strict";
const assert = require("assert").strict;
const fs = require("node:fs/promises");
const lib = require("@clusterio/lib");
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import * as lib from "@clusterio/lib";
describe("lib/ini", function() {
describe("parse()", function() {
+4 -5
View File
@@ -1,9 +1,8 @@
"use strict";
const assert = require("assert").strict;
const events = require("events");
import assert from "node:assert/strict";
import events from "node:events";
const mock = require("../../mock");
const lib = require("@clusterio/lib");
import * as mock from "../../mock.js";
import * as lib from "@clusterio/lib";
describe("lib/link/connectors", function() {
+4 -5
View File
@@ -1,9 +1,8 @@
"use strict";
const assert = require("assert").strict;
const events = require("events");
import assert from "node:assert/strict";
import events from "node:events";
const lib = require("@clusterio/lib");
const mock = require("../../mock");
import * as lib from "@clusterio/lib";
import * as mock from "../../mock.js";
const addr = lib.Address.fromShorthand;
+5 -6
View File
@@ -1,10 +1,9 @@
"use strict";
const assert = require("assert").strict;
const fs = require("node:fs/promises");
const path = require("path");
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import path from "node:path";
const { testLines } = require("./factorio/lines");
const lib = require("@clusterio/lib");
import { testLines } from "./factorio/lines.js";
import * as lib from "@clusterio/lib";
describe("lib/logging_utils.js", function() {
+2 -3
View File
@@ -1,7 +1,6 @@
"use strict";
const assert = require("assert").strict;
import assert from "node:assert/strict";
const lib = require("@clusterio/lib");
import * as lib from "@clusterio/lib";
describe("lib/lua_tools", function() {
+23 -4
View File
@@ -1,11 +1,30 @@
"use strict";
const assert = require("assert").strict;
import assert from "node:assert/strict";
const mock = require("../mock");
const lib = require("@clusterio/lib");
import * as mock from "../mock.js";
import * as lib from "@clusterio/lib";
describe("lib/plugin", function() {
describe("pluginNeedsWebBuild()", function() {
it("should be false for plugins with only host, instance or ctl code", function() {
assert.equal(lib.pluginNeedsWebBuild({ name: "foo", title: "Foo" }), false);
assert.equal(lib.pluginNeedsWebBuild({
name: "foo", title: "Foo", hostEntrypoint: "host.js", instanceEntrypoint: "instance.js",
ctlEntrypoint: "ctl.js", messages: [],
}), false);
});
it("should be true for plugins with web or controller code or config fields", function() {
assert.equal(lib.pluginNeedsWebBuild({ name: "foo", title: "Foo", webEntrypoint: "web.js" }), true);
assert.equal(lib.pluginNeedsWebBuild({
name: "foo", title: "Foo", controllerEntrypoint: "controller.js",
}), true);
for (const field of [
"controllerConfigFields", "hostConfigFields", "instanceConfigFields", "controlConfigFields",
]) {
assert.equal(lib.pluginNeedsWebBuild({ name: "foo", title: "Foo", [field]: {} }), true, field);
}
});
});
describe("invokeHook()", function() {
let betaTestCalled = false;
let plugins = new Map([
+16 -15
View File
@@ -1,10 +1,10 @@
"use strict";
const assert = require("assert").strict;
const fs = require("node:fs/promises");
const path = require("path");
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import path from "node:path";
import { pathToFileURL } from "node:url";
const lib = require("@clusterio/lib");
const { BaseControllerPlugin } = require("@clusterio/controller");
import * as lib from "@clusterio/lib";
import { BaseControllerPlugin } from "@clusterio/controller";
describe("lib/plugin_loader", function() {
@@ -19,7 +19,7 @@ describe("lib/plugin_loader", function() {
await fs.mkdir(pluginPath, { recursive: true });
await fs.writeFile(
path.join(pluginPath, "index.js"),
`module.exports.plugin = { name: "${infoName}" };`
`export const plugin = { name: "${infoName}" };`
);
await fs.writeFile(
path.join(pluginPath, "package.json"),
@@ -47,6 +47,7 @@ describe("lib/plugin_loader", function() {
name: "test",
version: "0.0.1",
npmPackage: undefined,
packagePath: path.resolve(path.join(testPlugin, "package.json")),
requirePath: path.resolve(testPlugin),
webStaticPath: path.resolve(path.join(testPlugin, "dist", "web", "static")),
}]
@@ -55,7 +56,7 @@ describe("lib/plugin_loader", function() {
it("should reject on broken plugin", async function() {
let brokenMessage;
try {
require(path.resolve(brokenPlugin));
await import(pathToFileURL(path.resolve(path.join(brokenPlugin, "index.js"))));
} catch (err) {
brokenMessage = err.message;
}
@@ -83,7 +84,7 @@ describe("lib/plugin_loader", function() {
await writeEntrypoint(missingClass, "");
await writeEntrypoint(
wrongParentClass, "class ControllerPlugin {}\n module.exports = { ControllerPlugin };\n"
wrongParentClass, "export class ControllerPlugin {};\n"
);
});
it("should throw if class is missing from entrypoint", async function() {
@@ -91,13 +92,13 @@ describe("lib/plugin_loader", function() {
await assert.rejects(
lib.loadPluginClass(
"test",
path.posix.join(requirePath, "controller"),
path.posix.join(requirePath, "controller.js"),
"ControllerPlugin",
BaseControllerPlugin,
),
{
message:
`PluginError: Expected ${path.posix.join(requirePath, "controller")} ` +
`PluginError: Expected ${path.posix.join(requirePath, "controller.js")} ` +
"to export a class named ControllerPlugin",
}
);
@@ -107,14 +108,14 @@ describe("lib/plugin_loader", function() {
await assert.rejects(
lib.loadPluginClass(
"test",
path.posix.join(requirePath, "controller"),
path.posix.join(requirePath, "controller.js"),
"ControllerPlugin",
BaseControllerPlugin,
),
{
message:
"PluginError: Expected ControllerPlugin exported from " +
`${path.posix.join(requirePath, "controller")} to be a subclass of BaseControllerPlugin`,
`${path.posix.join(requirePath, "controller.js")} to be a subclass of BaseControllerPlugin`,
}
);
});
@@ -151,7 +152,7 @@ describe("lib/plugin_loader", function() {
await fs.mkdir(pluginPath, { recursive: true });
await fs.writeFile(
path.join(pluginPath, "index.js"),
`module.exports.plugin = { name: "${name}" };`
`export const plugin = { name: "${name}" };`
);
await fs.writeFile(
path.join(pluginPath, "package.json"),
@@ -194,7 +195,7 @@ describe("lib/plugin_loader", function() {
await fs.mkdir(path.join(deepPluginPath, "dist"));
await fs.writeFile(
path.join(deepPluginPath, "dist", "index.js"),
'module.exports.plugin = { name: "deep" };'
'export const plugin = { name: "deep" };'
);
// Create an npm module that is not a plugin
+2 -3
View File
@@ -1,7 +1,6 @@
"use strict";
const assert = require("assert").strict;
import assert from "node:assert/strict";
const prometheus = require("@clusterio/lib");
import * as prometheus from "@clusterio/lib";
describe("lib/prometheus", function() {
+16 -3
View File
@@ -1,6 +1,5 @@
"use strict";
const assert = require("assert").strict;
const lib = require("@clusterio/lib");
import assert from "node:assert/strict";
import * as lib from "@clusterio/lib";
describe("rce_ops", function() {
describe("updatePackage", function() {
@@ -29,6 +28,20 @@ describe("rce_ops", function() {
});
});
describe("handleUpdateAll", function() {
it("updates the core package and all npm plugins in one command", async function() {
const _audit = lib.logger.audit;
const messages = [];
lib.logger.audit = (msg) => { messages.push(msg); };
try {
await lib.handleUpdateAll("core", [{ npmPackage: "foo" }, { name: "local" }, { npmPackage: "bar" }]);
} finally {
lib.logger.audit = _audit;
}
assert.deepEqual(messages, ["RCE | npm update --save core foo bar"]);
});
});
describe("handlePluginInstall", function() {
const _fetch = global.fetch;
before(function() {
+6 -7
View File
@@ -1,11 +1,10 @@
"use strict";
const assert = require("assert").strict;
const fs = require("node:fs/promises");
const path = require("path");
const stream = require("stream");
const util = require("util");
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import path from "node:path";
import stream from "node:stream";
import util from "node:util";
const lib = require("@clusterio/lib");
import * as lib from "@clusterio/lib";
const finished = util.promisify(stream.finished);
+5 -6
View File
@@ -1,9 +1,8 @@
"use strict";
const events = require("events");
const assert = require("assert").strict;
const lib = require("@clusterio/lib");
const { MockController, MockConnector, MockControl } = require("../mock");
const { testMatrix, testRoundTripJsonSerialisable } = require("../common");
import events from "node:events";
import assert from "node:assert/strict";
import * as lib from "@clusterio/lib";
import { MockController, MockConnector, MockControl } from "../mock.js";
import { testMatrix, testRoundTripJsonSerialisable } from "../common.js";
const addr = lib.Address.fromShorthand;
+43 -3
View File
@@ -1,6 +1,5 @@
"use strict";
const assert = require("assert").strict;
const lib = require("@clusterio/lib");
import assert from "node:assert/strict";
import * as lib from "@clusterio/lib";
describe("lib/users", function() {
@@ -44,4 +43,45 @@ describe("lib/users", function() {
);
});
});
describe("registerPluginPermissions()", function() {
it("should define the permissions declared by plugins", function() {
lib.registerPluginPermissions([
{ name: "foo", permissions: [
{ name: "foo.first", title: "First", description: "The first" },
{ name: "foo.second", title: "Second", description: "The second", grantByDefault: true },
]},
{ name: "bar" },
{ name: "baz", permissions: [] },
]);
assert(lib.permissions.has("foo.first"), "foo.first was not defined");
assert.equal(lib.permissions.get("foo.first").grantByDefault, false);
assert(lib.permissions.has("foo.second"), "foo.second was not defined");
assert.equal(lib.permissions.get("foo.second").grantByDefault, true);
});
it("should throw if the name does not start with the plugin name", function() {
assert.throws(
() => lib.registerPluginPermissions([
{ name: "foo", permissions: [{ name: "bar.spam", title: "Spam", description: "Spam" }] },
]),
new Error("Expected name of permission 'bar.spam' for foo to start with 'foo.'")
);
assert.throws(
() => lib.registerPluginPermissions([
{ name: "foo", permissions: [{ title: "Spam", description: "Spam" }] },
]),
new Error("Expected name of permission 'undefined' for foo to start with 'foo.'")
);
});
it("should throw on already defined permission", function() {
assert.throws(
() => lib.registerPluginPermissions([
{ name: "foo", permissions: [{ name: "foo.first", title: "First", description: "The first" }] },
]),
new Error("Permission 'foo.first' is already defined")
);
});
});
});
+3 -4
View File
@@ -1,8 +1,7 @@
"use strict";
const assert = require("assert").strict;
const JSZip = require("jszip");
import assert from "node:assert/strict";
import JSZip from "jszip";
const { findRoot } = require("@clusterio/lib");
import { findRoot } from "@clusterio/lib";
describe("lib/factorio", function() {
+1 -2
View File
@@ -1,5 +1,4 @@
"use strict";
const { ModPack } = require("@clusterio/lib");
import { ModPack } from "@clusterio/lib";
const pack = new ModPack();
pack.name = "test-settings";
+7 -8
View File
@@ -1,12 +1,11 @@
"use strict";
const assert = require("assert").strict;
const fs = require("node:fs/promises");
const os = require("node:os");
const path = require("node:path");
const lib = require("@clusterio/lib");
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import * as lib from "@clusterio/lib";
const { Controller, ControlConnection } = require("@clusterio/controller");
const { testMatrix, testRoundTripJsonSerialisable } = require("../common");
import { Controller, ControlConnection } from "@clusterio/controller";
import { testMatrix, testRoundTripJsonSerialisable } from "../common.js";
describe("messages/controller", function() {
/** @type {Controller} */
+4 -5
View File
@@ -1,9 +1,8 @@
"use strict";
const assert = require("assert").strict;
const lib = require("@clusterio/lib");
import assert from "node:assert/strict";
import * as lib from "@clusterio/lib";
const { Host } = require("@clusterio/host");
const { testMatrix, testRoundTripJsonSerialisable } = require("../common");
import { Host } from "@clusterio/host";
import { testMatrix, testRoundTripJsonSerialisable } from "../common.js";
describe("messages/host", function() {
/** @type {Host} */
+6 -7
View File
@@ -1,13 +1,12 @@
"use strict";
const assert = require("assert").strict;
import assert from "node:assert/strict";
const lib = require("@clusterio/lib");
const { ModDependencyResolveRequest, ModDependency, ModInfo } = lib;
import * as lib from "@clusterio/lib";
import { ModDependencyResolveRequest, ModDependency, ModInfo } from "@clusterio/lib";
const { testMatrix, testRoundTripJsonSerialisable } = require("../common");
import { testMatrix, testRoundTripJsonSerialisable } from "../common.js";
const { Controller, ControlConnection } = require("@clusterio/controller");
const { externalTest } = require("../integration");
import { Controller, ControlConnection } from "@clusterio/controller";
import { externalTest } from "../integration/index.js";
describe("messages/mod", function() {
/** @type {Controller} */
+59 -5
View File
@@ -1,9 +1,8 @@
"use strict";
const assert = require("assert").strict;
const lib = require("@clusterio/lib");
import assert from "node:assert/strict";
import * as lib from "@clusterio/lib";
const { Controller, ControlConnection } = require("@clusterio/controller");
const { Host } = require("@clusterio/host");
import { Controller, ControlConnection } from "@clusterio/controller";
import { Host } from "@clusterio/host";
describe("messages/plugin", function() {
/** @type {Host} */
@@ -134,6 +133,61 @@ describe("messages/plugin", function() {
});
});
describe("UpdateAllRequest", function() {
function checkedPermissions(dst) {
const permissions = [];
const user = { checkPermission(permission) { permissions.push(permission); } };
lib.UpdateAllRequest.permission(user, { dst: lib.Address.fromShorthand(dst) });
return permissions;
}
it("requires controller and plugin update permissions on the controller", function() {
assert.deepEqual(checkedPermissions("controller"), ["core.controller.update", "core.plugin.update"]);
});
it("requires host and plugin update permissions on a host", function() {
assert.deepEqual(checkedPermissions({ hostId: 1 }), ["core.host.update", "core.plugin.update"]);
});
it("runs on the controller", async function() {
controller.pluginInfos = [{ npmPackage: "foo" }];
controller.config.set("controller.allow_remote_updates", true);
controller.config.set("controller.allow_plugin_updates", true);
await controlConnection.handleUpdateAllRequest(new lib.UpdateAllRequest());
});
it("rejects if remote updates are disabled on the controller", async function() {
controller.config.set("controller.allow_remote_updates", false);
await assert.rejects(
controlConnection.handleUpdateAllRequest(new lib.UpdateAllRequest()),
/Remote updates are disabled on this machine/
);
});
it("rejects if plugin updates are disabled on the controller", async function() {
controller.config.set("controller.allow_plugin_updates", false);
await assert.rejects(
controlConnection.handleUpdateAllRequest(new lib.UpdateAllRequest()),
/Plugin updates are disabled on this machine/
);
});
it("runs on a host", async function() {
host.pluginInfos = [{ npmPackage: "foo" }];
host.config.set("host.allow_remote_updates", true);
host.config.set("host.allow_plugin_updates", true);
await host.handleUpdateAllRequest(new lib.UpdateAllRequest());
});
it("rejects if remote updates are disabled on the host", async function() {
host.config.set("host.allow_remote_updates", false);
await assert.rejects(
host.handleUpdateAllRequest(new lib.UpdateAllRequest()),
/Remote updates are disabled on this machine/
);
});
it("rejects if plugin updates are disabled on the host", async function() {
host.config.set("host.allow_plugin_updates", false);
await assert.rejects(
host.handleUpdateAllRequest(new lib.UpdateAllRequest()),
/Plugin updates are disabled on this machine/
);
});
});
describe("PluginInstallRequest", function() {
const _fetch = global.fetch;
before(function() {
+15 -31
View File
@@ -1,14 +1,13 @@
"use strict";
const events = require("events");
const http = require("http");
const express = require("express");
import events from "node:events";
import http from "node:http";
import express from "express";
const lib = require("@clusterio/lib");
const { User, UserManager, UserRecord, InstanceManager } = require("@clusterio/controller");
import * as lib from "@clusterio/lib";
import { User, UserManager, UserRecord, InstanceManager } from "@clusterio/controller";
const addr = lib.Address.fromShorthand;
class MockLogger {
export class MockLogger {
child() { return this; }
fatal() { }
@@ -26,7 +25,7 @@ class MockLogger {
verbose() { }
}
class MockSocket {
export class MockSocket {
constructor() {
this.sentMessages = [];
this.events = new Map();
@@ -66,7 +65,7 @@ class MockSocket {
}
}
class MockConnector extends lib.BaseConnector {
export class MockConnector extends lib.BaseConnector {
constructor(src, dst) {
super(src, dst);
@@ -86,7 +85,7 @@ class MockConnector extends lib.BaseConnector {
}
}
class MockServer extends events.EventEmitter {
export class MockServer extends events.EventEmitter {
constructor() {
super();
this.reset();
@@ -116,7 +115,7 @@ class MockServer extends events.EventEmitter {
}
}
class MockInstance extends lib.Link {
export class MockInstance extends lib.Link {
constructor() {
super(new MockConnector(addr({ instanceId: 7357 }), addr({ hostId: 1 })));
this.logger = new MockLogger();
@@ -144,15 +143,15 @@ class MockInstance extends lib.Link {
}
}
class MockHost extends lib.Link {
export class MockHost extends lib.Link {
constructor() {
super(new MockConnector(addr({ hostId: 1 }), addr("controller")));
}
}
class MockControl extends lib.Link { }
export class MockControl extends lib.Link { }
class MockController {
export class MockController {
constructor() {
this.app = express();
this.app.locals.controller = this;
@@ -224,7 +223,7 @@ class MockController {
}
}
async function createControllerPlugin(ControllerPluginClass, info) {
export async function createControllerPlugin(ControllerPluginClass, info) {
let controller = new MockController();
let metrics = {};
let logger = new MockLogger();
@@ -233,25 +232,10 @@ async function createControllerPlugin(ControllerPluginClass, info) {
return plugin;
}
async function createInstancePlugin(InstancePluginClass, info) {
export async function createInstancePlugin(InstancePluginClass, info) {
let instance = new MockInstance();
let host = new MockHost();
let plugin = new InstancePluginClass(info, instance, host);
await plugin.init();
return plugin;
}
module.exports = {
MockLogger,
MockSocket,
MockConnector,
MockServer,
MockInstance,
MockHost,
MockControl,
MockController,
createControllerPlugin,
createInstancePlugin,
};