Files
factorio-clusterio/test/lib/data/ModPack.js
T
Hornwitser 9a967a5779 Refactor JSON serialiable class pattern
Change the JSON seriasiable data classes to use an explicit static
fromJSON method and an optional toJSON method for turning them to and
from JSON.

This improves over the previous pattern of passing the JSON to the
constructor as it frees up the constructor to be able to take whichever
argument types suits the code instead of being beholden to the JSON
serialised form.
2023-02-25 00:02:12 +01:00

268 lines
8.0 KiB
JavaScript

"use strict";
const assert = require("assert").strict;
const zlib = require("zlib");
const libSchema = require("@clusterio/lib/schema");
const ExportManifest = require("@clusterio/lib/data/ExportManifest");
const ModPack = require("@clusterio/lib/data/ModPack");
describe("lib/data/ModPack", function() {
describe("class ModPack", function() {
it("should round trip serialize", function() {
const validate = libSchema.compile(ModPack.jsonSchema);
function check(pack) {
const json = JSON.parse(JSON.stringify(pack));
if (!validate(json)) {
throw validate.errors;
}
assert.deepEqual(ModPack.fromJSON(json), pack);
const packStringed = ModPack.fromModPackString(pack.toModPackString());
packStringed.id = pack.id;
packStringed.exportManifest = pack.exportManifest;
assert.deepEqual(packStringed, pack);
}
check(ModPack.fromJSON({}));
check(ModPack.fromJSON({ name: "MyPack" }));
check(ModPack.fromJSON({ description: "My Description" }));
check(ModPack.fromJSON({ factorio_version: "2.0" }));
check(ModPack.fromJSON({ mods: [
{ name: "subspace_storage", enabled: true, version: "1.99.8" },
{ name: "clusterio_lib", enabled: true, version: "0.1.2", sha1: "012345abcd" },
]}));
check(ModPack.fromJSON({ settings: {
"startup": {
"bool-setting": { "value": true },
},
"runtime-global": {
"number-setting": { "value": 123 },
},
"runtime-per-user": {
"string-setting": { "value": "a string" },
},
}}));
check(ModPack.fromJSON({ export_manifest: { assets: { setting: "settings.json" }}}));
check(ModPack.fromJSON({ deleted: true }));
check(ModPack.fromJSON({
name: "Super pack",
description: "Every option at once.",
factorio_version: "2.0",
mods: [
{ name: "subspace_storage", enabled: true, version: "1.99.8" },
{ name: "clusterio_lib", enabled: true, version: "0.1.2", sha1: "012345abcd" },
],
settings: {
"startup": {
"bool-setting": { "value": true },
},
"runtime-global": {
"number-setting": { "value": 123 },
},
"runtime-per-user": {
"string-setting": { "value": "a string" },
},
},
export_manifest: { assets: { setting: "settings.json" }},
deleted: true,
}));
});
it("should sort integer factorio versions lexicographically", function() {
let unsortedVersions = ["1.0", "1.1.0", "0.1", "3.0.0", "1.2", "0.3.1", "0.3.3", "2.1.1", "0.0.1"];
let sortedVersions = ["0.0.1", "0.1", "0.3.1", "0.3.3", "1.0", "1.1.0", "1.2", "2.1.1", "3.0.0"];
let factorioMods = unsortedVersions.map(v => ModPack.fromJSON({ factorio_version: v }));
factorioMods.sort((a, b) => a.integerFactorioVersion - b.integerFactorioVersion);
assert.deepEqual(factorioMods.map(mod => mod.factorioVersion), sortedVersions);
});
describe(".fillDefaultSettings()", function() {
const prototypes = {
"bool-setting": {
"bool": {
name: "bool",
type: "bool-setting",
setting_type: "startup",
default_value: true,
},
},
"double-setting": {
"number": {
name: "number",
type: "double-setting",
setting_type: "runtime-global",
default_value: 123,
},
},
"string-setting": {
"string": {
name: "string",
type: "string-setting",
setting_type: "runtime-per-user",
default_value: "a string",
},
},
};
const mockLogger = { warn: () => {} };
it("should fill in defaults for settings", function() {
const pack = ModPack.fromJSON({});
pack.fillDefaultSettings(prototypes, mockLogger);
assert.deepEqual(pack.toJSON().settings, {
"startup": {
"bool": { "value": true },
},
"runtime-global": {
"number": { "value": 123 },
},
"runtime-per-user": {
"string": { "value": "a string" },
},
});
});
it("should not overwrite existing values", function() {
const pack = ModPack.fromJSON({ settings: {
"startup": {
"bool": { "value": false },
},
"runtime-global": {
"number": { "value": 2 },
},
"runtime-per-user": {
"string": { "value": "spam" },
},
}});
pack.fillDefaultSettings(prototypes, mockLogger);
assert.deepEqual(pack.toJSON().settings, {
"startup": {
"bool": { "value": false },
},
"runtime-global": {
"number": { "value": 2 },
},
"runtime-per-user": {
"string": { "value": "spam" },
},
});
});
it("should ignore unknown setting_type and missing default_value", function() {
const pack = ModPack.fromJSON({});
pack.fillDefaultSettings({
"string-setting": {
"foo": {
name: "foo",
type: "string-setting",
setting_type: "magic-that-does-not-exist",
default_value: "a string",
},
"bar": {
name: "bar",
type: "string-setting",
setting_type: "startup",
},
},
}, mockLogger);
assert.deepEqual(pack.toJSON().settings, {
"startup": {},
"runtime-global": {},
"runtime-per-user": {},
});
});
});
describe(".fromModPackString()", function() {
it("should handle malformed strings", function() {
assert.throws(
() => ModPack.fromModPackString("AMalformedString"),
new Error("Malformed mod pack string: zlib inflate failed")
);
let badJsonMsg;
try {
JSON.parse("Not Json");
} catch (err) {
badJsonMsg = err.message;
}
assert.throws(
// eslint-disable-next-line node/no-sync
() => ModPack.fromModPackString(Buffer.from(zlib.deflateSync("Not Json")).toString("base64")),
new Error(`Malformed mod pack string: ${badJsonMsg}`)
);
assert.throws(
// eslint-disable-next-line node/no-sync
() => ModPack.fromModPackString(Buffer.from(zlib.deflateSync('{"i":1}')).toString("base64")),
new Error("Malformed mod pack string: Schema validation failed")
);
});
});
describe("toModSettingsDat()", function() {
it("should properly serialise settings", function() {
const pack = ModPack.fromJSON({ settings: {
"startup": {
"bool-setting": { "value": true },
},
"runtime-global": {
"number-setting": { "value": 123 },
},
"runtime-per-user": {
"string-setting": { "value": "a string" },
},
}});
function istr(str) {
return [
Uint8Array.from([0, str.length]),
Buffer.from(str),
];
}
/* eslint-disable indent */
assert.deepEqual(
pack.toModSettingsDat(),
Buffer.concat([
new Uint8Array(Uint16Array.from([1, 1, 0, 0]).buffer), // version
Uint8Array.from([0]), // reserved
Uint8Array.from([5, 0]), // dictionary
new Uint8Array(Uint32Array.from([3]).buffer), // items
...istr("startup"), // name
Uint8Array.from([5, 0]), // dictionary
new Uint8Array(Uint32Array.from([1]).buffer), // items
...istr("bool-setting"), // name
Uint8Array.from([5, 0]), // dictinary
new Uint8Array(Uint32Array.from([1]).buffer), // items
...istr("value"), // name
Uint8Array.from([1, 0, 1]), // boolean
...istr("runtime-global"), // name
Uint8Array.from([5, 0]), // dictionary
new Uint8Array(Uint32Array.from([1]).buffer), // items
...istr("number-setting"), // name
Uint8Array.from([5, 0]), // dictinary
new Uint8Array(Uint32Array.from([1]).buffer), // items
...istr("value"), // name
Uint8Array.from([2, 0]), // number
new Uint8Array(Float64Array.from([123]).buffer),
...istr("runtime-per-user"), // name
Uint8Array.from([5, 0]), // dictionary
new Uint8Array(Uint32Array.from([1]).buffer), // items
...istr("string-setting"), // name
Uint8Array.from([5, 0]), // dictinary
new Uint8Array(Uint32Array.from([1]).buffer), // items
...istr("value"), // name
Uint8Array.from([3, 0, 0, "a string".length]), // string
Buffer.from("a string"),
])
);
/* eslint-enable indent */
});
});
});
});