mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-11 23:04:52 +09:00
Split up codebase into the packages @clsterio/lib, @clusterio/master, @clusterio/slave, and @clusterio/ctl using lerna to manage them. This will allow deployment to use only the parts needed reducing install size, and better separate the code into shared library, master, slave and control.
238 lines
8.4 KiB
JavaScript
238 lines
8.4 KiB
JavaScript
"use strict";
|
|
const assert = require("assert").strict;
|
|
const events = require("events");
|
|
const fs = require("fs-extra");
|
|
const path = require("path");
|
|
|
|
const factorio = require("@clusterio/lib/factorio");
|
|
const { testLines } = require("./lines");
|
|
|
|
|
|
describe("lib/factorio/server", function() {
|
|
describe("_getVersion()", function() {
|
|
it("should get the version from a changelog", async function() {
|
|
let version = await factorio._getVersion(path.join("test", "file", "changelog-test.txt"));
|
|
assert.equal(version, "0.1.1");
|
|
});
|
|
it("should return null if unable to find the version", async function() {
|
|
let version = await factorio._getVersion(path.join("test", "file", "changelog-bad.txt"));
|
|
assert.equal(version, null);
|
|
});
|
|
it("should return null if file does not exist", async function() {
|
|
let version = await factorio._getVersion(path.join("test", "file", "does-not-exist.txt"));
|
|
assert.equal(version, null);
|
|
});
|
|
});
|
|
|
|
describe("_versionOrder()", function() {
|
|
it("should sort an array of versions", function() {
|
|
let versions = ["1.2.3", "0.1.4", "0.1.2", "1.2.3", "0.1.5", "1.10.2"];
|
|
versions.sort(factorio._versionOrder);
|
|
assert.deepEqual(
|
|
versions,
|
|
["1.10.2", "1.2.3", "1.2.3", "0.1.5", "0.1.4", "0.1.2"]
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("_findVersion()", function() {
|
|
it("should find a given install dir with latest as target", async function() {
|
|
let installDir = path.join("test", "file", "factorio");
|
|
let [dir, version] = await factorio._findVersion(installDir, "latest");
|
|
assert.equal(dir, path.join(installDir, "data"));
|
|
assert.equal(version, "0.1.1");
|
|
});
|
|
it("should find a given install dir with correct version as target", async function() {
|
|
let installDir = path.join("test", "file", "factorio");
|
|
let [dir, version] = await factorio._findVersion(installDir, "0.1.1");
|
|
assert.equal(dir, path.join(installDir, "data"));
|
|
assert.equal(version, "0.1.1");
|
|
});
|
|
it("should reject if the install dir version does not match target version", async function() {
|
|
let installDir = path.join("test", "file", "factorio");
|
|
await assert.rejects(
|
|
factorio._findVersion(installDir, "0.1.2"),
|
|
new Error("Factorio version 0.1.2 was requested, but install directory contains 0.1.1")
|
|
);
|
|
});
|
|
it("should search given directory for latest Factorio install", async function() {
|
|
let installDir = path.join("test", "file");
|
|
let [dir, version] = await factorio._findVersion(installDir, "latest");
|
|
assert.equal(dir, path.join(installDir, "factorio", "data"));
|
|
assert.equal(version, "0.1.1");
|
|
});
|
|
it("should search given directory for given Factorio install", async function() {
|
|
let installDir = path.join("test", "file");
|
|
let [dir, version] = await factorio._findVersion(installDir, "0.1.1");
|
|
assert.equal(dir, path.join(installDir, "factorio", "data"));
|
|
assert.equal(version, "0.1.1");
|
|
});
|
|
it("should reject if no factorio install with the given version was found", async function() {
|
|
let installDir = path.join("test", "file");
|
|
await assert.rejects(
|
|
factorio._findVersion(installDir, "0.1.2"),
|
|
new Error("Unable to find Factorio version 0.1.2")
|
|
);
|
|
});
|
|
it("should reject if no factorio install was found", async function() {
|
|
let installDir = path.join("test", "file", "instances");
|
|
await assert.rejects(
|
|
factorio._findVersion(installDir, "latest"),
|
|
new Error(`Unable to find any Factorio install in ${installDir}`)
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("randomDynamicPort()", function() {
|
|
it("should return a port number", function() {
|
|
let port = factorio._randomDynamicPort();
|
|
assert.equal(typeof port, "number");
|
|
assert(Number.isInteger(port));
|
|
assert(0 <= port && port < 2**16);
|
|
});
|
|
|
|
it("should return a port number in the dynamic range", function() {
|
|
function validate(port) {
|
|
return (49152 <= port && port <= 65535);
|
|
}
|
|
for (let i=0; i < 20; i++) {
|
|
assert(validate(factorio._randomDynamicPort()));
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("generatePassword()", function() {
|
|
it("should return a string", async function() {
|
|
let password = await factorio._generatePassword(1);
|
|
assert.equal(typeof password, "string");
|
|
});
|
|
|
|
it("should return a string of the given length", async function() {
|
|
let password = await factorio._generatePassword(10);
|
|
assert.equal(password.length, 10);
|
|
});
|
|
|
|
it("should contain only a-z, A-Z, 0-9", async function() {
|
|
let password = await factorio._generatePassword(10);
|
|
assert(/^[a-zA-Z0-9]+$/.test(password), `${password} failed test`);
|
|
});
|
|
});
|
|
|
|
describe("class LineSplitter", function() {
|
|
function createSplitter(lines) {
|
|
return new factorio._LineSplitter(line => lines.push(line.toString("utf-8")));
|
|
}
|
|
|
|
it("should split three lines", function() {
|
|
let lines = [];
|
|
let ls = createSplitter(lines);
|
|
ls.data(Buffer.from("line 1\nline 2\nline 3\n"));
|
|
ls.end();
|
|
assert.deepEqual(lines, ["line 1", "line 2", "line 3"]);
|
|
});
|
|
it("should split three Windows line endings lines", function() {
|
|
let lines = [];
|
|
let ls = createSplitter(lines);
|
|
ls.data(Buffer.from("line 1\r\nline 2\r\nline 3\r\n"));
|
|
assert.deepEqual(lines, ["line 1", "line 2", "line 3"]);
|
|
});
|
|
it("should give the last non-terminated line on .end()", function() {
|
|
let lines = [];
|
|
let ls = createSplitter(lines);
|
|
ls.data(Buffer.from("line a\nline b"));
|
|
assert.deepEqual(lines, ["line a"]);
|
|
ls.end();
|
|
assert.deepEqual(lines, ["line a", "line b"]);
|
|
});
|
|
it("should handled partial lines", function() {
|
|
let lines = [];
|
|
let ls = createSplitter(lines);
|
|
ls.data(Buffer.from("part 1"));
|
|
ls.data(Buffer.from(" part 2 "));
|
|
ls.data(Buffer.from("part 3\n"));
|
|
ls.end();
|
|
assert.deepEqual(lines, ["part 1 part 2 part 3"]);
|
|
});
|
|
});
|
|
|
|
describe("parseOutput()", function() {
|
|
it("should parse the test lines", function() {
|
|
for (let [line, reference] of testLines) {
|
|
reference.source = "test";
|
|
let output = factorio._parseOutput(line, "test");
|
|
delete output.received;
|
|
assert.deepEqual(output, reference);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("class FactorioServer", function() {
|
|
let writePath = path.join("temp", "test", "server");
|
|
let server = new factorio.FactorioServer(path.join("test", "file", "factorio"), writePath, {});
|
|
|
|
describe(".init()", function() {
|
|
it("should not throw on first call", async function() {
|
|
await server.init();
|
|
});
|
|
|
|
it("should throw if called twice", async function() {
|
|
await assert.rejects(server.init(), new Error("Expected state new but state is init"));
|
|
});
|
|
});
|
|
|
|
describe(".version", function() {
|
|
it("should return the version detected", function() {
|
|
assert.equal(server.version, "0.1.1");
|
|
});
|
|
});
|
|
|
|
describe("._handleIpc()", function() {
|
|
it("should emit the correct ipc event", async function() {
|
|
let waiter = events.once(server, "ipc-channel");
|
|
await server._handleIpc(Buffer.from('\f$ipc:channel?j"value"'));
|
|
let result = await waiter;
|
|
assert.equal(result[0], "value");
|
|
});
|
|
it("should handle special characters in channel name", async function() {
|
|
let waiter = events.once(server, "ipc-$ ?\x00\x0a:");
|
|
await server._handleIpc(Buffer.from('\f$ipc:$ \\x3f\\x00\\x0a:?j"value"'));
|
|
let result = await waiter;
|
|
assert.equal(result[0], "value");
|
|
});
|
|
it("should throw on malformed ipc line", async function() {
|
|
await assert.rejects(
|
|
server._handleIpc(Buffer.from("\f$ipc:blah")),
|
|
new Error('Malformed IPC line "\f$ipc:blah"')
|
|
);
|
|
});
|
|
it("should throw on unknown type", async function() {
|
|
await assert.rejects(
|
|
server._handleIpc(Buffer.from("\f$ipc:channel??")),
|
|
new Error("Unknown IPC type '?'")
|
|
);
|
|
});
|
|
it("should throw on unknown file type", async function() {
|
|
await assert.rejects(
|
|
server._handleIpc(Buffer.from("\f$ipc:channel?ffoo.invalid")),
|
|
new Error("Unknown IPC file format 'invalid'")
|
|
);
|
|
});
|
|
it("should throw on file name with slash", async function() {
|
|
await assert.rejects(
|
|
server._handleIpc(Buffer.from("\f$ipc:channel?fa/b")),
|
|
new Error("Invalid IPC file name 'a/b'")
|
|
);
|
|
});
|
|
it("should load and delete json file", async function() {
|
|
let filePath = server.writePath("script-output", "data.json");
|
|
await fs.outputFile(filePath, '{"data":"spam"}');
|
|
let waiter = events.once(server, "ipc-channel");
|
|
await server._handleIpc(Buffer.from("\f$ipc:channel?fdata.json"));
|
|
let result = await waiter;
|
|
assert.deepEqual(result[0], { "data": "spam" });
|
|
assert(!await fs.pathExists(filePath), "File was not deleted");
|
|
});
|
|
});
|
|
});
|
|
});
|