Files
quixos-protocol/test/source-closure.test.ts
T

237 lines
10 KiB
TypeScript

import assert from "node:assert/strict";
import { execFile as callback } from "node:child_process";
import { promisify } from "node:util";
import { mkdtemp, mkdir, writeFile, readFile, rm, symlink } from "node:fs/promises";
import path from "node:path";
import os from "node:os";
import test from "node:test";
import { pathToFileURL } from "node:url";
import { materializeWorkspaceSources, workspaceSourcePin } from "../src/capability-language/source-closure.js";
import { prepareDevelopmentSources } from "../src/capability-language/development-sources.js";
import { backupWorkspaceSources, restoreWorkspaceSources } from "../src/capability-language/source-backup.js";
const exec = promisify(callback);
const lock = (resources: string) => `quixos-lock version 1 {
quixos source { repository "https://unavailable.test/toolchain.git"; commit "${"a".repeat(40)}"; }
${resources}
}`;
test("exact source closure recovers through explicitly selected bundles, preserves history and rejects edits", async (context) => {
const temporary = await mkdtemp(path.join(os.tmpdir(), "qx-closure-"));
context.after(() => rm(temporary, { recursive: true, force: true }));
const locations: Record<string, string> = {};
const repository = async (name: string, resources: string) => {
const directory = path.join(temporary, name);
await mkdir(directory);
const git = async (...args: string[]) => (await exec("git", args, { cwd: directory })).stdout.trim();
await git("init");
await writeFile(path.join(directory, "quixos.lock"), lock(""));
await git("add", ".");
const commit = async () => {
await git("-c", "user.name=Test", "-c", "user.email=test@example.test", "commit", "-am", "Source");
return await git("rev-parse", "HEAD");
};
const parent = await commit();
await writeFile(path.join(directory, "quixos.lock"), lock(resources));
await writeFile(path.join(directory, "marker"), name);
await git("add", ".");
const rev = await commit();
await git("tag", `quixos-reachability/${rev}`);
const bundle = path.join(temporary, `${name}.bundle`);
await git("bundle", "create", bundle, `refs/tags/quixos-reachability/${rev}`);
const source = { repository: `https://unavailable.test/${name}.git`, commit: rev };
locations[source.repository] = pathToFileURL(bundle).href;
return { ...source, parent };
};
const common = await repository("common", "");
const dependency = `interface Common source { repository "${common.repository}"; commit "${common.commit}"; }`;
const left = await repository("left", dependency),
right = await repository("right", dependency);
const root = await repository(
"root",
`
package Left source { repository "${left.repository}"; commit "${left.commit}"; }
package Right source { repository "${right.repository}"; commit "${right.commit}"; }`,
);
const directory = path.join(temporary, "checkout");
const closure = await materializeWorkspaceSources({ source: root, directory, locations, concurrency: 2 });
assert.deepEqual(
await materializeWorkspaceSources({ source: root, directory, repositoryPrefix: "https://unavailable.test/" }),
closure,
);
// Allow this root but not its dependencies: reject them before Git acquisition.
const scopedRoot = { ...root, repository: "https://unavailable.test/selected/root.git" };
await assert.rejects(
materializeWorkspaceSources({
source: scopedRoot,
directory: path.join(temporary, "scoped"),
locations: { ...locations, [scopedRoot.repository]: locations[root.repository] },
repositoryPrefix: "https://unavailable.test/selected/",
}),
/outside the acquisition boundary/,
);
await assert.rejects(
materializeWorkspaceSources({ source: root, directory, repositoryPrefix: "https://unavailable.test/root.git/" }),
/outside the acquisition boundary/,
);
assert.equal(closure.resources.length, 4, "diamond dependency is materialized once");
assert.deepEqual(JSON.parse(await readFile(path.join(directory, "sources.json"), "utf8")), closure);
const rootEntry = closure.resources.find((entry) => entry.kind === "workspace")!;
const rootDirectory = path.join(directory, rootEntry.directory);
const rootLock = path.join(rootDirectory, "quixos.lock");
const rootBody = await readFile(rootLock, "utf8");
const outsideLock = path.join(temporary, "outside.lock");
await writeFile(outsideLock, rootBody);
// Exercise the shared loader directly: committed root symlinks must not let
// privileged source inspection read outside the acquired repository.
const { loadQuixosLock } = await import("../src/resource-lock/index.js");
const linkedLock = path.join(temporary, "linked.lock");
await symlink(outsideLock, linkedLock);
await assert.rejects(loadQuixosLock(linkedLock), /root locks must be ordinary/);
const backup = path.join(temporary, "retained");
const manifest = await backupWorkspaceSources({ source: root, directory, output: backup });
assert.equal(manifest.repositories.length, 4);
const restored = await restoreWorkspaceSources({ backup, directory: path.join(temporary, "restored") });
assert.deepEqual(restored, closure, "Source-only recovery retains canonical identity without an available forge");
await assert.rejects(backupWorkspaceSources({ source: root, directory, output: backup }), /must not exist/);
const missing = {
...manifest,
repositories: manifest.repositories.filter((entry) => entry.repository !== common.repository),
};
await writeFile(path.join(backup, "backup.json"), JSON.stringify(missing));
await assert.rejects(
restoreWorkspaceSources({ backup, directory: path.join(temporary, "incomplete") }),
/omits required repository/,
);
await writeFile(path.join(backup, "backup.json"), JSON.stringify(manifest));
await writeFile(path.join(backup, manifest.repositories[0].bundle), "damaged backup");
await assert.rejects(
restoreWorkspaceSources({ backup, directory: path.join(temporary, "damaged") }),
/integrity mismatch/,
);
assert.equal((await exec("git", ["rev-parse", "HEAD^"], { cwd: rootDirectory })).stdout.trim(), root.parent);
assert.equal(
(await exec("git", ["remote", "get-url", "origin"], { cwd: rootDirectory })).stdout.trim(),
root.repository,
);
// Cached recovery does not need the primary or the backup.
assert.deepEqual(await materializeWorkspaceSources({ source: root, directory }), closure);
const pinFile = path.join(temporary, "source.json");
await writeFile(pinFile, JSON.stringify(root));
const cli = path.resolve(import.meta.dirname, "../src/capability-language/tool-cli.js");
const command = await exec(process.execPath, [cli, "source-checkout", pinFile, "--dir", directory]);
assert.deepEqual(JSON.parse(command.stdout), closure, "Protocol CLI preserves exact cached source identity");
const scoped = await exec(process.execPath, [
cli,
"source-checkout",
pinFile,
"--dir",
directory,
"--repository-prefix",
"https://unavailable.test/",
]);
assert.deepEqual(JSON.parse(scoped.stdout), closure);
const cliBackup = path.join(temporary, "cli-backup");
const backupCommand = await exec(process.execPath, [
cli,
"source-backup",
pinFile,
"--dir",
directory,
"--output",
cliBackup,
]);
assert.equal(JSON.parse(backupCommand.stdout).repositories.length, 4);
const restoreCommand = await exec(process.execPath, [
cli,
"source-restore",
cliBackup,
"--dir",
path.join(temporary, "cli-restore"),
]);
assert.deepEqual(JSON.parse(restoreCommand.stdout), closure);
const developmentCommand = await exec(process.execPath, [
cli,
"source-develop",
pinFile,
"--dir",
path.join(temporary, "cli-developer"),
"--cache",
directory,
]);
const developmentResult = JSON.parse(developmentCommand.stdout);
assert.equal(developmentResult.repositories, 4);
assert.deepEqual(developmentResult.source, closure.root);
const a = await prepareDevelopmentSources({
source: root,
cacheDirectory: directory,
directory: path.join(temporary, "a"),
});
const b = await prepareDevelopmentSources({
source: root,
cacheDirectory: directory,
directory: path.join(temporary, "b"),
});
assert.notEqual(a.workspaceId, b.workspaceId);
const assigned = "f8f2f8a3-328c-45a2-847a-31869ad28ce7";
const c = await prepareDevelopmentSources({
source: root,
cacheDirectory: directory,
directory: path.join(temporary, "assigned"),
workspaceId: assigned,
});
assert.equal(c.workspaceId, assigned);
assert.equal(
JSON.parse(await readFile(path.join(c.directory, ".quixos/resource-graph.json"), "utf8")).workspaceId,
assigned,
);
await assert.rejects(
prepareDevelopmentSources({
source: root,
cacheDirectory: directory,
directory: path.join(temporary, "invalid"),
workspaceId: "not-a-workspace",
}),
/Workspace UUID/,
);
const aRoot = path.join(a.directory, "root"),
bRoot = path.join(b.directory, "root");
assert.equal((await exec("git", ["remote", "get-url", "origin"], { cwd: aRoot })).stdout.trim(), root.repository);
assert.equal(
await readFile(path.join(aRoot, "quixos.lock"), "utf8"),
await readFile(path.join(rootDirectory, "quixos.lock"), "utf8"),
);
await exec("git", ["cat-file", "-e", root.parent], { cwd: aRoot });
await writeFile(path.join(aRoot, "marker"), "Independent developer change");
assert.equal(await readFile(path.join(bRoot, "marker"), "utf8"), "root");
assert.equal(await readFile(path.join(rootDirectory, "marker"), "utf8"), "root");
await assert.rejects(
prepareDevelopmentSources({ source: root, cacheDirectory: directory, directory: a.directory }),
/never overwritten/,
);
await writeFile(path.join(rootDirectory, "marker"), "edited");
await assert.rejects(materializeWorkspaceSources({ source: root, directory, locations }), /modified/);
await assert.rejects(
materializeWorkspaceSources({
source: root,
directory: path.join(temporary, "bounded"),
locations,
maxRepositories: 2,
}),
/repository limit/,
);
await assert.rejects(
materializeWorkspaceSources({
source: root,
directory,
locations: { [root.repository]: "https://secret:password@example.test/root.git" },
}),
/transport substitution/,
);
});
test("Workspace pins reject floating revisions and embedded credentials", () => {
assert.throws(() => workspaceSourcePin({ repository: "https://example.test/root.git", commit: "main" }));
assert.throws(() =>
workspaceSourcePin({ repository: "https://user:password@example.test/root.git", commit: "a".repeat(40) }),
);
});