Files
scoreko-electron-dev/src/tests/runtime-provisioner.test.ts
T

197 lines
7.6 KiB
TypeScript

import assert from "node:assert/strict";
import path from "node:path";
import test from "node:test";
import { prepareUserNodecgRuntime } from "../main/nodecg/runtime-provisioner";
type FakeFsState = {
paths: Set<string>;
files: Map<string, string>;
removed: string[];
copied: Array<{ from: string; to: string }>;
};
function createFakeFs(initialPaths: string[] = [], initialFiles: Record<string, string> = {}) {
const state: FakeFsState = {
paths: new Set(initialPaths.map((candidatePath) => path.normalize(candidatePath))),
files: new Map(Object.entries(initialFiles).map(([filePath, content]) => [path.normalize(filePath), content])),
removed: [],
copied: [],
};
for (const filePath of state.files.keys()) {
state.paths.add(filePath);
}
return {
state,
deps: {
existsSync: (candidatePath: string) => state.paths.has(path.normalize(candidatePath)),
mkdirSync: (candidatePath: string) => {
state.paths.add(path.normalize(candidatePath));
return undefined;
},
rmSync: (candidatePath: string) => {
state.removed.push(path.normalize(candidatePath));
state.paths.delete(path.normalize(candidatePath));
},
cpSync: (from: string, to: string) => {
state.copied.push({ from: path.normalize(from), to: path.normalize(to) });
state.paths.add(path.normalize(to));
state.paths.add(path.join(path.normalize(to), "index.js"));
state.paths.add(path.join(path.normalize(to), "package.json"));
state.paths.add(path.join(path.normalize(to), "node_modules", "nodecg", "dist", "server", "bootstrap.js"));
state.paths.add(path.join(path.normalize(to), "bundles", "scoreko-dev", "package.json"));
},
readFileSync: (filePath: string) => state.files.get(path.normalize(filePath)) ?? "{}",
writeFileSync: (filePath: string, content: string) => {
state.files.set(path.normalize(filePath), content);
state.paths.add(path.normalize(filePath));
},
},
};
}
function getSourcePaths(source: string) {
return [
source,
path.join(source, "index.js"),
path.join(source, "package.json"),
path.join(source, "node_modules", "nodecg", "dist", "server", "bootstrap.js"),
path.join(source, "bundles", "scoreko-dev", "package.json"),
path.join(source, ".scoreko-runtime.json"),
];
}
test("prepareUserNodecgRuntime copies the packaged runtime into userData", () => {
const source = path.normalize("/app/lib/nodecg");
const userData = path.normalize("/user/scoreko");
const { state, deps } = createFakeFs(getSourcePaths(source), {
[path.join(source, ".scoreko-runtime.json")]: JSON.stringify({ bundleVersion: "0.1.0", nodecgVersion: "2.6.4" }),
});
const preparedRuntime = prepareUserNodecgRuntime({
sourceRuntimePath: source,
userDataPath: userData,
appVersion: "0.1.0",
bundleName: "scoreko-dev",
log: () => undefined,
deps,
});
assert.equal(preparedRuntime.runtimePath, path.join(userData, "nodecg"));
assert.equal(preparedRuntime.installed, true);
assert.equal(state.copied.length, 1);
assert.ok(state.paths.has(path.join(userData, "nodecg", "cfg")));
assert.ok(state.paths.has(path.join(userData, "nodecg", "db")));
assert.ok(state.paths.has(path.join(userData, "nodecg", "logs")));
assert.ok(state.files.has(path.join(userData, "nodecg", ".scoreko-installed-runtime.json")));
});
test("prepareUserNodecgRuntime keeps an up-to-date runtime in place", () => {
const source = path.normalize("/app/lib/nodecg");
const userData = path.normalize("/user/scoreko");
const target = path.join(userData, "nodecg");
const sourceManifest = { bundleVersion: "0.1.0", generatedAt: "2026-05-24T00:00:00.000Z", nodecgVersion: "2.6.4" };
const targetManifest = { appVersion: "0.1.0", bundleName: "scoreko-dev", sourceRuntime: sourceManifest };
const { state, deps } = createFakeFs(
[
...getSourcePaths(source),
path.join(target, "node_modules", "nodecg", "dist", "server", "bootstrap.js"),
path.join(target, "bundles", "scoreko-dev", "package.json"),
path.join(target, ".scoreko-installed-runtime.json"),
],
{
[path.join(source, ".scoreko-runtime.json")]: JSON.stringify(sourceManifest),
[path.join(target, ".scoreko-installed-runtime.json")]: JSON.stringify(targetManifest),
},
);
const preparedRuntime = prepareUserNodecgRuntime({
sourceRuntimePath: source,
userDataPath: userData,
appVersion: "0.1.0",
bundleName: "scoreko-dev",
log: () => undefined,
deps,
});
assert.equal(preparedRuntime.installed, false);
assert.equal(state.copied.length, 0);
assert.equal(state.removed.length, 0);
});
test("prepareUserNodecgRuntime refreshes managed files when the app version changes", () => {
const source = path.normalize("/app/lib/nodecg");
const userData = path.normalize("/user/scoreko");
const target = path.join(userData, "nodecg");
const sourceManifest = { bundleVersion: "0.1.0", generatedAt: "2026-05-24T00:00:00.000Z", nodecgVersion: "2.6.4" };
const targetManifest = { appVersion: "0.0.9", bundleName: "scoreko-dev", sourceRuntime: sourceManifest };
const { state, deps } = createFakeFs(
[
...getSourcePaths(source),
path.join(target, "node_modules", "nodecg", "dist", "server", "bootstrap.js"),
path.join(target, "bundles", "scoreko-dev", "package.json"),
path.join(target, ".scoreko-installed-runtime.json"),
],
{
[path.join(source, ".scoreko-runtime.json")]: JSON.stringify(sourceManifest),
[path.join(target, ".scoreko-installed-runtime.json")]: JSON.stringify(targetManifest),
},
);
const preparedRuntime = prepareUserNodecgRuntime({
sourceRuntimePath: source,
userDataPath: userData,
appVersion: "0.1.0",
bundleName: "scoreko-dev",
log: () => undefined,
deps,
});
assert.equal(preparedRuntime.installed, true);
assert.equal(state.copied.length, 1);
assert.ok(state.removed.includes(path.join(target, "node_modules")));
assert.ok(state.removed.includes(path.join(target, "bundles")));
assert.ok(!state.removed.includes(path.join(target, "db")));
});
test("prepareUserNodecgRuntime refreshes managed files when the source runtime was regenerated", () => {
const source = path.normalize("/app/lib/nodecg");
const userData = path.normalize("/user/scoreko");
const target = path.join(userData, "nodecg");
const sourceManifest = { bundleVersion: "0.1.0", generatedAt: "2026-05-24T01:00:00.000Z", nodecgVersion: "2.6.4" };
const targetSourceManifest = {
bundleVersion: "0.1.0",
generatedAt: "2026-05-24T00:00:00.000Z",
nodecgVersion: "2.6.4",
};
const targetManifest = { appVersion: "0.1.0", bundleName: "scoreko-dev", sourceRuntime: targetSourceManifest };
const { state, deps } = createFakeFs(
[
...getSourcePaths(source),
path.join(target, "node_modules", "nodecg", "dist", "server", "bootstrap.js"),
path.join(target, "bundles", "scoreko-dev", "package.json"),
path.join(target, ".scoreko-installed-runtime.json"),
],
{
[path.join(source, ".scoreko-runtime.json")]: JSON.stringify(sourceManifest),
[path.join(target, ".scoreko-installed-runtime.json")]: JSON.stringify(targetManifest),
},
);
const preparedRuntime = prepareUserNodecgRuntime({
sourceRuntimePath: source,
userDataPath: userData,
appVersion: "0.1.0",
bundleName: "scoreko-dev",
log: () => undefined,
deps,
});
assert.equal(preparedRuntime.installed, true);
assert.equal(state.copied.length, 1);
assert.ok(state.removed.includes(path.join(target, "bundles")));
assert.ok(!state.removed.includes(path.join(target, "cfg")));
});