Files

162 lines
6.6 KiB
JavaScript

import assert from "node:assert/strict";
import {
components, enabledComponents, mergePlayerData, stripPlayerData, scenarioComponents,
} from "../dist/node/components.js";
const allComponents = Object.keys(components);
function configWith(disabled = []) {
return { get: name => !disabled.includes(name.replace("player_sync.sync_", "")) };
}
const character = { inventories: { main: [] } };
const stored = {
generation: 1,
name: "test",
controller: "god",
cheat_mode: true,
force: "player",
color: [1, 0, 0],
chat_color: [1, 0, 0],
tag: "stored",
inventories: { main: ["stored god item"] },
quick_bar: ["stored-item"],
recipe_notifications: "stored",
};
const uploaded = {
generation: 2,
name: "test",
controller: "character",
cheat_mode: false,
force: "enemy",
color: [0, 1, 0],
chat_color: [0, 1, 0],
tag: "uploaded",
character,
crafting_queue: [],
personal_logistic_slots: [],
quick_bar: ["uploaded-item"],
recipe_notifications: "uploaded",
};
describe("player_sync components", function() {
describe("enabledComponents()", function() {
it("should enable every component by default", function() {
assert.deepEqual([...enabledComponents(configWith())], allComponents);
});
it("should leave out disabled components", function() {
const enabled = enabledComponents(configWith(["force", "quick_bar"]));
assert.equal(enabled.has("force"), false);
assert.equal(enabled.has("quick_bar"), false);
assert.equal(enabled.size, allComponents.length - 2);
});
it("should sync the controller without the inventories", function() {
const enabled = enabledComponents(configWith(["inventories"]));
assert.equal(enabled.has("controller"), true);
});
});
describe("stripPlayerData()", function() {
it("should keep everything when all components are synced", function() {
const playerData = structuredClone(uploaded);
stripPlayerData(playerData, new Set(allComponents));
assert.deepEqual(playerData, uploaded);
});
it("should remove the fields of disabled components", function() {
const playerData = structuredClone(uploaded);
stripPlayerData(playerData, new Set(allComponents.filter(c => !["appearance", "inventories"].includes(c))));
for (const key of ["color", "chat_color", "tag", "character", "crafting_queue"]) {
assert.equal(key in playerData, false, key);
}
assert.equal(playerData.force, "enemy");
});
it("should remove the god inventory with the inventories", function() {
const playerData = structuredClone(stored);
stripPlayerData(playerData, new Set(allComponents.filter(c => c !== "inventories")));
assert.equal("inventories" in playerData, false);
assert.equal(playerData.controller, "god");
});
});
describe("scenarioComponents()", function() {
it("should sync everything when no components are sent", function() {
assert.deepEqual([...scenarioComponents(undefined)], allComponents);
});
it("should sync the components marked true", function() {
const enabled = scenarioComponents({ force: true, quick_bar: true, settings: false });
assert.deepEqual([...enabled], ["force", "quick_bar"]);
});
it("should sync nothing for an empty table encoded as an array", function() {
assert.equal(scenarioComponents([]).size, 0);
});
it("should sync the controller without the inventories", function() {
assert.deepEqual([...scenarioComponents({ controller: true })], ["controller"]);
});
});
describe("mergePlayerData()", function() {
function withComponents(data, disabled) {
const enabled = allComponents.filter(c => !disabled.includes(c));
return { ...data, components: Object.fromEntries(enabled.map(c => [c, true])) };
}
it("should store the upload as is when it has no components", function() {
assert.deepEqual(mergePlayerData(stored, uploaded), uploaded);
});
it("should not store the components of the upload", function() {
assert.deepEqual(mergePlayerData(stored, withComponents(uploaded, [])), uploaded);
});
it("should keep the stored fields of disabled components", function() {
const merged = mergePlayerData(stored, withComponents(uploaded, ["force", "appearance"]));
assert.equal(merged.force, "player");
assert.deepEqual(merged.color, [1, 0, 0]);
assert.equal(merged.tag, "stored");
assert.deepEqual(merged.quick_bar, ["uploaded-item"]);
assert.equal(merged.generation, 2);
});
it("should drop disabled components without stored data", function() {
const merged = mergePlayerData(undefined, withComponents(uploaded, ["force"]));
assert.equal("force" in merged, false);
assert.equal(merged.tag, "uploaded");
});
it("should take the items but not the controller when the controller is not synced", function() {
const merged = mergePlayerData(stored, withComponents(uploaded, ["controller"]));
assert.equal(merged.controller, "god");
assert.equal(merged.cheat_mode, true);
assert.equal(merged.character, character);
assert.equal("inventories" in merged, false, "the god inventory moved to the character");
});
it("should take the items of a player without a character", function() {
const storedCharacter = {
...stored, character, crafting_queue: ["stored"], personal_logistic_slots: [{ name: "stored" }],
};
const god = { ...uploaded, controller: "god", inventories: { main: ["new god item"] } };
delete god.character;
delete god.crafting_queue;
delete god.personal_logistic_slots;
const merged = mergePlayerData(storedCharacter, withComponents(god, ["controller"]));
assert.equal("character" in merged, false);
assert.equal("crafting_queue" in merged, false);
assert.deepEqual(merged.inventories, { main: ["new god item"] });
assert.deepEqual(merged.personal_logistic_slots, [{ name: "stored" }], "nowhere to hold logistics");
});
it("should drop logistic requests a character uploaded without", function() {
const storedSlots = { ...stored, personal_logistic_slots: [{ name: "stored" }] };
const noSlots = { ...uploaded };
delete noSlots.personal_logistic_slots;
const merged = mergePlayerData(storedSlots, withComponents(noSlots, []));
assert.equal("personal_logistic_slots" in merged, false);
});
it("should take cleared logistic requests from an upload without inventories", function() {
const storedSlots = { ...stored, personal_logistic_slots: [{ name: "stored" }] };
// Factorio 1.1 serializes a character without requests as an empty table
const cleared = { ...uploaded, personal_logistic_slots: {} };
delete cleared.character;
delete cleared.crafting_queue;
const merged = mergePlayerData(storedSlots, withComponents(cleared, ["inventories"]));
assert.deepEqual(merged.personal_logistic_slots, {});
});
});
});