mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-11 23:04:52 +09:00
Add option in the controller config to set where static assets are loaded from in order to support deployments where the static assets are server via a CDN while the API is directly accessed. This is useful because some CDNs put low limits on how large requests bodies can be, which causes issues with uploading large mods and saves.
374 lines
11 KiB
JavaScript
374 lines
11 KiB
JavaScript
"use strict";
|
|
const assert = require("assert").strict;
|
|
const lib = require("@clusterio/lib");
|
|
const validators = lib.Config.validators;
|
|
const { ControllerConfig, HostConfig, InstanceConfig, ControlConfig } = lib;
|
|
|
|
describe("lib/config/validators", function() {
|
|
describe("all", function() {
|
|
it("should call all validators in order", function() {
|
|
let calls = [];
|
|
|
|
function v1(value, config) {
|
|
calls.push("v1");
|
|
}
|
|
function v2(value, config) {
|
|
calls.push("v2");
|
|
}
|
|
|
|
const combined = validators.all(v1, v2);
|
|
combined("value", {});
|
|
|
|
assert.deepEqual(calls, ["v1", "v2"], "validators were not called in order");
|
|
});
|
|
|
|
it("should propagate errors from validators", function() {
|
|
function v1() {}
|
|
function v2() {
|
|
throw new Error("failure");
|
|
}
|
|
|
|
const combined = validators.all(v1, v2);
|
|
|
|
assert.throws(
|
|
() => combined("value", {}),
|
|
/failure/,
|
|
"error from validator was not propagated"
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("optional", function() {
|
|
it("should skip validation when value is null", function() {
|
|
let called = false;
|
|
|
|
function validator() {
|
|
called = true;
|
|
}
|
|
|
|
const wrapped = validators.optional(validator);
|
|
wrapped(null, {});
|
|
|
|
assert.equal(called, false, "validator should not have been called");
|
|
});
|
|
|
|
it("should run validator when value is not null", function() {
|
|
let called = false;
|
|
|
|
function validator() {
|
|
called = true;
|
|
}
|
|
|
|
const wrapped = validators.optional(validator);
|
|
wrapped("value", {});
|
|
|
|
assert.equal(called, true, "validator was not called");
|
|
});
|
|
});
|
|
|
|
describe("greaterThan", function() {
|
|
it("should allow values greater than min", function() {
|
|
const validator = validators.greaterThan(5);
|
|
assert.doesNotThrow(() => validator(6));
|
|
});
|
|
|
|
it("should throw if value equals min", function() {
|
|
const validator = validators.greaterThan(5);
|
|
|
|
assert.throws(
|
|
() => validator(5),
|
|
/Value must be greater than 5/,
|
|
"did not throw on equal value"
|
|
);
|
|
});
|
|
|
|
it("should throw if value is less than min", function() {
|
|
const validator = validators.greaterThan(5);
|
|
|
|
assert.throws(
|
|
() => validator(4),
|
|
/Value must be greater than 5/,
|
|
"did not throw on smaller value"
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("greaterThanZero", function() {
|
|
it("should allow values greater than zero", function() {
|
|
assert.doesNotThrow(() => validators.greaterThanZero(1));
|
|
});
|
|
|
|
it("should throw on zero", function() {
|
|
assert.throws(
|
|
() => validators.greaterThanZero(0),
|
|
/Value must be greater than 0/
|
|
);
|
|
});
|
|
|
|
it("should throw on negative values", function() {
|
|
assert.throws(
|
|
() => validators.greaterThanZero(-1),
|
|
/Value must be greater than 0/
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("greaterThanEqual", function() {
|
|
it("should allow values greater than min", function() {
|
|
const validator = validators.greaterThanEqual(5);
|
|
assert.doesNotThrow(() => validator(6));
|
|
});
|
|
|
|
it("should allow values equal to min", function() {
|
|
const validator = validators.greaterThanEqual(5);
|
|
assert.doesNotThrow(() => validator(5));
|
|
});
|
|
|
|
it("should throw if value is less than min", function() {
|
|
const validator = validators.greaterThanEqual(5);
|
|
|
|
assert.throws(
|
|
() => validator(4),
|
|
/Value must be greater than 5/,
|
|
"did not throw on smaller value"
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("greaterThanEqualZero", function() {
|
|
it("should allow zero", function() {
|
|
assert.doesNotThrow(() => validators.greaterThanEqualZero(0));
|
|
});
|
|
|
|
it("should allow positive values", function() {
|
|
assert.doesNotThrow(() => validators.greaterThanEqualZero(1));
|
|
});
|
|
|
|
it("should throw on negative values", function() {
|
|
assert.throws(
|
|
() => validators.greaterThanEqualZero(-1),
|
|
/Value must be greater than 0/
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("integer", function() {
|
|
it("should allow integer values", function() {
|
|
assert.doesNotThrow(() => validators.integer(10));
|
|
});
|
|
|
|
it("should throw on non-integer values", function() {
|
|
assert.throws(
|
|
() => validators.integer(1.5),
|
|
/Value must be an integer/,
|
|
"did not throw on float"
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("filePath", function() {
|
|
it("should accept valid paths", function() {
|
|
assert.doesNotThrow(() => validators.filePath("test/file.txt"));
|
|
assert.doesNotThrow(() => validators.filePath("/absolute/path"));
|
|
});
|
|
|
|
it("should throw if value is not a string", function() {
|
|
// This is the only case that will cause path.resolve to throw on posix
|
|
assert.throws(
|
|
() => validators.filePath(null),
|
|
/Value must be a valid path/
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("lib/config/definitions/validators", function() {
|
|
describe("Controller Config", function() {
|
|
it("should validate controller.static_url", function() {
|
|
const config = new ControllerConfig("controller");
|
|
|
|
// valid cases
|
|
config.set("controller.static_url", "");
|
|
config.set("controller.static_url", null);
|
|
config.set("controller.static_url", "static/");
|
|
config.set("controller.static_url", "./static/");
|
|
config.set("controller.static_url", "/static/");
|
|
config.set("controller.static_url", "http://example/static/");
|
|
|
|
// invalid cases
|
|
assert.throws(
|
|
() => config.set("controller.static_url", "/foo/"),
|
|
"Path component of static_url must be \/static\/ not \/foo\/."
|
|
);
|
|
assert.throws(
|
|
() => config.set("controller.static_url", "http://example/foo/static/"),
|
|
"Path component of static_url must be \/static\/ not \/foo\/static\/."
|
|
);
|
|
assert.throws(
|
|
() => config.set("controller.static_url", "http://example/static"),
|
|
"Path component of static_url must be \/static\/ not \/static."
|
|
);
|
|
assert.throws(
|
|
() => config.set("controller.static_url", "http://:80"),
|
|
/Invalid URL/,
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("Host Config", function() {
|
|
|
|
});
|
|
|
|
describe("Instance Config", function() {
|
|
it("should validate factorio.version", function() {
|
|
const config = new InstanceConfig("controller");
|
|
|
|
// valid cases
|
|
assert.doesNotThrow(() => config.set("factorio.version", "latest"));
|
|
assert.doesNotThrow(() => config.set("factorio.version", "1.1"));
|
|
assert.doesNotThrow(() => config.set("factorio.version", "1.1.87"));
|
|
|
|
// invalid case
|
|
assert.throws(
|
|
() => config.set("factorio.version", "invalidValue"),
|
|
/Value must be 'latest', a release channel, or match X.Y or X.Y.Z/
|
|
);
|
|
});
|
|
|
|
describe("factorio.player_online_autosave_slots", function() {
|
|
it("should validate against autosave slots", function() {
|
|
const config = new InstanceConfig("controller", {
|
|
"factorio.settings": {
|
|
autosave_slots: 5,
|
|
},
|
|
});
|
|
|
|
// valid: equal
|
|
assert.doesNotThrow(() => config.set("factorio.player_online_autosave_slots", 5));
|
|
|
|
// valid: greater
|
|
assert.doesNotThrow(() => config.set("factorio.player_online_autosave_slots", 6));
|
|
|
|
// invalid: less than autosave_slots
|
|
assert.throws(
|
|
() => config.set("factorio.player_online_autosave_slots", 4),
|
|
/Value cannot be less than the number of autosave slots/
|
|
);
|
|
});
|
|
it("should allow disabling player online autosaves", function() {
|
|
const config = new InstanceConfig("controller", {
|
|
"factorio.settings": {
|
|
autosave_slots: 5,
|
|
},
|
|
});
|
|
|
|
assert.doesNotThrow(() => config.set("factorio.player_online_autosave_slots", 0));
|
|
});
|
|
it("should allow when autosave_slots is not a number", function() {
|
|
const config = new InstanceConfig("controller", {
|
|
"factorio.settings": {
|
|
autosave_slots: null,
|
|
},
|
|
});
|
|
|
|
assert.doesNotThrow(() => config.set("factorio.player_online_autosave_slots", 0));
|
|
assert.doesNotThrow(() => config.set("factorio.player_online_autosave_slots", 1));
|
|
});
|
|
});
|
|
|
|
describe("plugin.load_plugin", function() {
|
|
const initialConfigFields = {};
|
|
|
|
before(function() {
|
|
for (const fieldName of Object.keys(InstanceConfig.fieldDefinitions)) {
|
|
if (fieldName.endsWith("load_plugin")) {
|
|
initialConfigFields[fieldName] = false;
|
|
}
|
|
}
|
|
});
|
|
|
|
it("should validate against factorio.enable_save_patching", function() {
|
|
class TestInstanceConfig extends InstanceConfig {
|
|
static fieldDefinitions = { ...InstanceConfig.fieldDefinitions };
|
|
}
|
|
|
|
lib.addPluginFieldDefinitions(
|
|
{name: "save_patching", features: ["SavePatching"] },
|
|
"instanceConfigFields", TestInstanceConfig
|
|
);
|
|
|
|
const config = new TestInstanceConfig("controller", initialConfigFields);
|
|
|
|
// valid: does not require script commands
|
|
assert.doesNotThrow(() => config.set("factorio.enable_script_commands", false));
|
|
|
|
// valid: can have load disabled
|
|
assert.doesNotThrow(() => config.set("save_patching.load_plugin", false));
|
|
|
|
// valid: does not require save patching when not loaded
|
|
assert.doesNotThrow(() => config.set("factorio.enable_save_patching", false));
|
|
|
|
// invalid: requires save patching when loaded
|
|
assert.throws(
|
|
() => config.set("save_patching.load_plugin", true),
|
|
/requires save patching/
|
|
);
|
|
});
|
|
it("should validate against factorio.enable_script_commands", function() {
|
|
class TestInstanceConfig extends InstanceConfig {
|
|
static fieldDefinitions = { ...InstanceConfig.fieldDefinitions };
|
|
}
|
|
|
|
lib.addPluginFieldDefinitions(
|
|
{ name: "script_commands", features: ["ScriptCommands"] },
|
|
"instanceConfigFields", TestInstanceConfig
|
|
);
|
|
|
|
const config = new TestInstanceConfig("controller", initialConfigFields);
|
|
|
|
// valid: does not require save patching
|
|
assert.doesNotThrow(() => config.set("factorio.enable_save_patching", false));
|
|
|
|
// valid: can have load disabled
|
|
assert.doesNotThrow(() => config.set("script_commands.load_plugin", false));
|
|
|
|
// valid: does not require script commands when not loaded
|
|
assert.doesNotThrow(() => config.set("factorio.enable_script_commands", false));
|
|
|
|
// invalid: requires script commands when loaded
|
|
assert.throws(
|
|
() => config.set("script_commands.load_plugin", true),
|
|
/requires script commands/
|
|
);
|
|
});
|
|
it("should allow when no feature flags are present", function() {
|
|
class TestInstanceConfig extends InstanceConfig {
|
|
static fieldDefinitions = { ...InstanceConfig.fieldDefinitions };
|
|
}
|
|
|
|
lib.addPluginFieldDefinitions(
|
|
{ name: "no_features", features: [] },
|
|
"instanceConfigFields", TestInstanceConfig
|
|
);
|
|
|
|
const config = new TestInstanceConfig("controller", initialConfigFields);
|
|
|
|
// valid: does not require save patching
|
|
assert.doesNotThrow(() => config.set("factorio.enable_save_patching", false));
|
|
|
|
// valid: does not require script commands
|
|
assert.doesNotThrow(() => config.set("factorio.enable_script_commands", false));
|
|
|
|
// valid: can have load disabled
|
|
assert.doesNotThrow(() => config.set("no_features.load_plugin", false));
|
|
|
|
// no invalid case
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("Control Config", function() {
|
|
|
|
});
|
|
});
|