Files
ForgeFlow-Public/tests/dependency-wiring.test.mjs
NuklearRabbit f60b269686
ForgeFlow quality gate / quality (push) Successful in 4m12s
ForgeFlow quality gate / secret-scan (push) Successful in 7s
Publish curated ForgeFlow source from 2ed1787c0b52
2026-09-29 22:50:57 +02:00

166 lines
7.9 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
const { registerDeploymentIpc } = require("../src/main/ipc/deployment-handlers.cjs");
const { UnraidDeploymentService } = require("../src/main/unraid-deployment-service.cjs");
const REPOSITORY = { fullName: "Jens/ForgeFlow", owner: { login: "Jens" }, localPath: "C:/Projects/ForgeFlow" };
const WORKLOAD = { workloadId: "workload-1", classification: { type: "ambiguous" } };
// Every collaborator answers, so a channel can only fail on a dependency the
// module references but never receives.
function harness(overrides = {}) {
const calls = [];
const record = (name, result) => async (...args) => { calls.push({ name, args }); return typeof result === "function" ? result(...args) : result; };
const handlers = new Map();
const profile = overrides.profile || { id: "profile-1", provider: "ssh-unraid", branch: "main", name: "Production" };
const dependencies = {
register: (channel, handler) => handlers.set(channel, handler),
store: {
data: { servers: [{ id: "server-1", name: "Unraid" }], operations: [] },
getDeploymentProfile: () => profile,
getPublicState: () => ({ ok: true }),
saveDeploymentProfile: record("store.saveDeploymentProfile", profile),
deleteDeploymentProfile: record("store.deleteDeploymentProfile", []),
addOperation: record("store.addOperation", null),
},
resolveRepository: record("resolveRepository", REPOSITORY),
unraid: {
preflight: record("unraid.preflight", { ok: true }),
repairWriteAccess: record("unraid.repairWriteAccess", { changed: true, after: {}, before: {} }),
deploy: record("unraid.deploy", { id: "operation-1" }),
rollback: record("unraid.rollback", { id: "operation-2" }),
linkServerWorkload: record("unraid.linkServerWorkload", { linked: true }),
configureServerGitAccess: record("unraid.configureServerGitAccess", { keyFingerprint: "a", hostFingerprint: "b" }),
verifyServerGitProfile: record("unraid.verifyServerGitProfile", { readiness: "ready", ready: true, checkedAt: "now" }),
discoverServerWorkloads: record("unraid.discoverServerWorkloads", { serverId: "server-1", workloads: [] }),
planServerInventoryReconciliation: record("unraid.planServerInventoryReconciliation", { plan: { id: "plan-1", summary: {} } }),
reconcileServerInventory: record("unraid.reconcileServerInventory", { adopted: 0, refreshed: 0, retired: 0 }),
scanServerInventory: record("unraid.scanServerInventory", { workloads: [WORKLOAD] }),
refreshProfileState: record("unraid.refreshProfileState", { liveSha: null }),
applyDockerManMetadata: record("unraid.applyDockerManMetadata", { applied: true }),
refreshOperation: record("unraid.refreshOperation", null),
reconcileRecordedOperations: record("unraid.reconcileRecordedOperations", []),
},
deployments: {
deploy: record("deployments.deploy", { id: "operation-3" }),
rollback: record("deployments.rollback", { id: "operation-4" }),
checkHealth: record("deployments.checkHealth", { healthy: true }),
refreshProfileState: record("deployments.refreshProfileState", { liveSha: null }),
},
evaluateDeploymentPolicy: () => ({ note: "", overridden: false, reason: "", violations: [] }),
audit: { append: record("audit.append", null) },
deployKeys: {
inventory: record("deployKeys.inventory", { keys: [] }),
planRotation: record("deployKeys.planRotation", { id: "rotation-1" }),
rotate: record("deployKeys.rotate", { rotated: true }),
planRevocation: record("deployKeys.planRevocation", { id: "revocation-1" }),
revoke: record("deployKeys.revoke", { revoked: true }),
restore: record("deployKeys.restore", { restored: true }),
},
repositories: { refresh: record("repositories.refresh", [REPOSITORY]) },
inventoryReviews: {
preview: (...args) => { calls.push({ name: "inventoryReviews.preview", args }); return { id: "review-1" }; },
apply: record("inventoryReviews.apply", { applied: true }),
},
diagnostics: { info: record("diagnostics.info"), warning: record("diagnostics.warning"), error: record("diagnostics.error"), debug: record("diagnostics.debug") },
git: {},
gitea: { getBranch: record("gitea.getBranch", { commit: { id: "c".repeat(40) } }) },
ssh: {},
preflight: { runDeployment: record("preflight.runDeployment", { ok: "actions" }) },
...overrides.dependencies,
};
registerDeploymentIpc(dependencies);
return { handlers, calls, names: () => calls.map((item) => item.name) };
}
const PAYLOAD = {
repository: REPOSITORY,
fullName: REPOSITORY.fullName,
profileId: "profile-1",
sha: "a".repeat(40),
serverId: "server-1",
workloadId: WORKLOAD.workloadId,
planId: "plan-1",
action: "manual-link",
url: "https://app.example/health",
profile: { name: "Production" },
targetSha: "b".repeat(40),
};
// Both provider paths have to run: a dependency that only the Gitea Actions
// branch reads stays invisible while every channel is exercised as SSH/Unraid.
for (const provider of ["ssh-unraid", "gitea-actions"]) {
test(`every deployment IPC channel runs with the dependencies it is given (${provider})`, async () => {
const { handlers } = harness({ profile: { id: "profile-1", provider, branch: "main", name: "Production" } });
assert.ok(handlers.size >= 20, "expected the complete deployment channel surface");
const failures = [];
for (const [channel, handler] of handlers) {
try {
await handler({ ...PAYLOAD });
} catch (error) {
// A refusal is a decision the handler made; a missing dependency is not.
if (error instanceof ReferenceError || error instanceof TypeError) {
failures.push(`${channel}: ${error.name}: ${error.message}`);
}
}
}
assert.deepEqual(failures, []);
});
}
test("deployment preflight routes by provider", async () => {
const actions = harness({ profile: { id: "profile-1", provider: "gitea-actions" } });
assert.deepEqual(await actions.handlers.get("deployment:preflight")({ ...PAYLOAD }), { ok: "actions" });
assert.ok(actions.names().includes("preflight.runDeployment"));
const unraid = harness();
assert.deepEqual(await unraid.handlers.get("deployment:preflight")({ ...PAYLOAD }), { ok: true });
assert.ok(unraid.names().includes("unraid.preflight"));
assert.ok(!unraid.names().includes("preflight.runDeployment"));
});
test("write-access repair is refused for anything but an SSH/Unraid profile", async () => {
const actions = harness({ profile: { id: "profile-1", provider: "gitea-actions" } });
await assert.rejects(
() => actions.handlers.get("deployment:repair-write-access")({ ...PAYLOAD }),
/available only for SSH \/ Unraid/,
);
});
test("a stale workload blocks an inventory review instead of guessing", async () => {
const { handlers } = harness({
dependencies: { unraid: { scanServerInventory: async () => ({ workloads: [] }) } },
});
for (const channel of ["deployment:plan-inventory-review", "deployment:apply-inventory-review"]) {
await assert.rejects(() => handlers.get(channel)({ ...PAYLOAD }), (error) => {
assert.equal(error.code, "INVENTORY_REVIEW_WORKLOAD_STALE");
return true;
});
}
});
test("write-access repair builds a repair script that preserves runtime paths", () => {
const service = new UnraidDeploymentService({});
const profile = {
id: "profile-3",
provider: "ssh-unraid",
remoteFolder: "portfolio",
composeFiles: ["docker-compose.yml"],
preservePaths: ["data/uploads"],
};
const server = { id: "server-1", basePath: "/mnt/user/appdata" };
const script = service.permissionRepairScript(profile, server, "/mnt/user/appdata/portfolio");
assert.equal(typeof script, "string");
assert.match(script, /data\/uploads/);
assert.match(script, /node_modules/);
assert.match(script, /ForgeFlow repaired project write access/);
});