test: strengthen evidence runtime handoff coverage

This commit is contained in:
2026-08-09 19:46:47 +02:00
parent 36fbd58277
commit 64b778ade9
2 changed files with 98 additions and 19 deletions
+47 -1
View File
@@ -141,7 +141,7 @@ async function fixture(workspaceSource = filesystemWorkspace) {
}; };
for (const [name, value] of Object.entries(environment)) vi.stubEnv(name, value); for (const [name, value] of Object.entries(environment)) vi.stubEnv(name, value);
vi.stubEnv("THT_HOME", join(root, "home")); vi.stubEnv("THT_HOME", join(root, "home"));
return { root, dataRoot, secretRoot, registry, registryConfig, revision }; return { root, source, dataRoot, secretRoot, registry, registryConfig, revision };
} }
function runnerFor(f: Awaited<ReturnType<typeof fixture>>): ThtRunner { function runnerFor(f: Awaited<ReturnType<typeof fixture>>): ThtRunner {
@@ -234,6 +234,52 @@ test("separate runtime leases hand off byte-identical revision Evidence configs
} }
}); });
test("real Evidence-content-only commit changes runtime identity and root with identical descriptor YAML", async () => {
const f = await fixture();
const runner = runnerFor(f);
const first = runner.acquireWorkspaceRuntime(f.revision.snapshotPath);
const descriptorBefore = readFileSync(f.revision.snapshotPath, "utf8");
writeFileSync(
join(f.source, "workspace-content", "psd-clinical", "evidence", "guide.md"),
"# Content-only revision two\n",
);
await git(f.source, ["add", "workspace-content/psd-clinical/evidence/guide.md"]);
await git(f.source, ["commit", "-m", "Update Evidence content only"]);
await git(f.source, ["push", "origin", "main"]);
await f.registry.pull();
const current = (await f.registry.list())[0];
const second = runner.acquireWorkspaceRuntime(current.snapshotPath);
try {
expect(current.commit).not.toBe(f.revision.commit);
expect(current.blob).toBe(f.revision.blob);
expect(readFileSync(current.snapshotPath, "utf8")).toBe(descriptorBefore);
const firstRendered = parse(readFileSync(first.path, "utf8"));
const secondRendered = parse(readFileSync(second.path, "utf8"));
expect(firstRendered.runtime_identity.workspace_revision).toBe(f.revision.commit);
expect(secondRendered.runtime_identity.workspace_revision).toBe(current.commit);
expect(secondRendered.evidence.sources[0].root).toBe(join(
f.registryConfig.root,
"snapshots",
current.commit,
"workspace-content",
"psd-clinical",
"evidence",
));
expect(secondRendered.evidence.sources[0].root).not.toBe(firstRendered.evidence.sources[0].root);
for (const lease of [first, second]) {
await expect(runFile(thtBin, ["config", "check", "-c", lease.path], {
cwd: harnessDir,
env: { ...process.env, THT_HOME: join(f.root, "home") },
})).resolves.toBeDefined();
}
} finally {
first.release();
second.release();
}
});
test("signed HTTP Evidence resolves its file binding and config check never captures its contents", async () => { test("signed HTTP Evidence resolves its file binding and config check never captures its contents", async () => {
const f = await fixture(evidenceWorkspace(` type: http const f = await fixture(evidenceWorkspace(` type: http
uris: [https://evidence.example.test/guide.md] uris: [https://evidence.example.test/guide.md]
+51 -18
View File
@@ -1,4 +1,7 @@
import { expect, test } from "vitest"; import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, expect, test } from "vitest";
import { parse } from "yaml"; import { parse } from "yaml";
import { import {
renderRuntimeConfig, renderRuntimeConfig,
@@ -300,6 +303,20 @@ test("renders REST bindings through the legacy rest sections without secret valu
expect(yaml).not.toContain("\n api_key: "); expect(yaml).not.toContain("\n api_key: ");
}); });
const evidenceSecretRoots: string[] = [];
afterEach(() => {
evidenceSecretRoots.splice(0).forEach((root) => rmSync(root, { recursive: true, force: true }));
});
function evidenceSecretFile(name: string, contents: string): string {
const root = mkdtempSync(join(tmpdir(), "tht-renderer-evidence-secret-"));
evidenceSecretRoots.push(root);
const path = join(root, name);
writeFileSync(path, contents, { mode: 0o600 });
return path;
}
function evidenceWorkspace(source: Record<string, unknown>, policy?: Record<string, unknown>) { function evidenceWorkspace(source: Record<string, unknown>, policy?: Record<string, unknown>) {
return parseWorkspaceYaml(`${canonicalEvidenceWorkspace}\nevidence:\n source: ${JSON.stringify(source)}${ return parseWorkspaceYaml(`${canonicalEvidenceWorkspace}\nevidence:\n source: ${JSON.stringify(source)}${
policy === undefined ? "" : `\n policy: ${JSON.stringify(policy)}` policy === undefined ? "" : `\n policy: ${JSON.stringify(policy)}`
@@ -410,7 +427,8 @@ test("renders public HTTP Evidence with exact fractional-second timeouts and eve
}); });
test("renders signed HTTP Evidence as provenance plus a validated file path only", () => { test("renders signed HTTP Evidence as provenance plus a validated file path only", () => {
const signedFile = "/run/secrets/evidence-signed-urls.json"; const canary = "SIGNED-URL-CANARY-CONTENT";
const signedFile = evidenceSecretFile("evidence-signed-urls.json", canary);
const yaml = evidenceRender({ const yaml = evidenceRender({
type: "http", type: "http",
uris: [ uris: [
@@ -437,7 +455,7 @@ test("renders signed HTTP Evidence as provenance plus a validated file path only
allow_private_hosts: false, allow_private_hosts: false,
max_cache_bytes: 67_108_864, max_cache_bytes: 67_108_864,
}]); }]);
expect(yaml).not.toContain("SIGNED-URL-CANARY-CONTENT"); expect(yaml).not.toContain(canary);
}); });
test("renders ambient S3 Evidence without credential keys", () => { test("renders ambient S3 Evidence without credential keys", () => {
@@ -465,7 +483,13 @@ test("renders ambient S3 Evidence without credential keys", () => {
}); });
test("renders static S3 Evidence with endpoint policy, limits, and file paths but no contents", () => { test("renders static S3 Evidence with endpoint policy, limits, and file paths but no contents", () => {
const yaml = evidenceRender({ const accessCanary = "ACCESS-CANARY-CONTENT";
const secretCanary = "SECRET-CANARY-CONTENT";
const tokenCanary = "TOKEN-CANARY-CONTENT";
const accessFile = evidenceSecretFile("evidence-access", accessCanary);
const secretFile = evidenceSecretFile("evidence-secret", secretCanary);
const tokenFile = evidenceSecretFile("evidence-token", tokenCanary);
const source = {
type: "s3", type: "s3",
uri: "s3://clinical-evidence/published/", uri: "s3://clinical-evidence/published/",
credentials: "static_files", credentials: "static_files",
@@ -478,14 +502,13 @@ test("renders static S3 Evidence with endpoint policy, limits, and file paths bu
max_objects: 33, max_objects: 33,
max_pages: 4, max_pages: 4,
page_size: 5, page_size: 5,
}, { };
missing: [], const values = {
values: { THT_WS_PSD_CLINICAL_EVIDENCE_ACCESS_KEY_FILE: accessFile,
THT_WS_PSD_CLINICAL_EVIDENCE_ACCESS_KEY_FILE: "/run/secrets/evidence-access", THT_WS_PSD_CLINICAL_EVIDENCE_SECRET_KEY_FILE: secretFile,
THT_WS_PSD_CLINICAL_EVIDENCE_SECRET_KEY_FILE: "/run/secrets/evidence-secret", THT_WS_PSD_CLINICAL_EVIDENCE_SESSION_TOKEN_FILE: tokenFile,
THT_WS_PSD_CLINICAL_EVIDENCE_SESSION_TOKEN_FILE: "/run/secrets/evidence-token", };
}, const yaml = evidenceRender(source, { missing: [], values });
});
expect(parse(yaml).evidence.sources).toEqual([{ expect(parse(yaml).evidence.sources).toEqual([{
type: "s3", type: "s3",
@@ -493,9 +516,9 @@ test("renders static S3 Evidence with endpoint policy, limits, and file paths bu
prefix: "published/", prefix: "published/",
endpoint_url: "http://minio.internal:9000/", endpoint_url: "http://minio.internal:9000/",
region: "eu-central-1", region: "eu-central-1",
access_key_file: "/run/secrets/evidence-access", access_key_file: accessFile,
secret_key_file: "/run/secrets/evidence-secret", secret_key_file: secretFile,
session_token_file: "/run/secrets/evidence-token", session_token_file: tokenFile,
trusted_endpoint: true, trusted_endpoint: true,
allow_private_endpoint: true, allow_private_endpoint: true,
allow_insecure_endpoint: true, allow_insecure_endpoint: true,
@@ -504,9 +527,19 @@ test("renders static S3 Evidence with endpoint policy, limits, and file paths bu
max_pages: 4, max_pages: 4,
page_size: 5, page_size: 5,
}]); }]);
expect(yaml).not.toContain("ACCESS-CANARY-CONTENT"); expect(yaml).not.toContain(accessCanary);
expect(yaml).not.toContain("SECRET-CANARY-CONTENT"); expect(yaml).not.toContain(secretCanary);
expect(yaml).not.toContain("TOKEN-CANARY-CONTENT"); expect(yaml).not.toContain(tokenCanary);
const withoutToken = parse(evidenceRender(source, {
missing: [],
values: {
THT_WS_PSD_CLINICAL_EVIDENCE_ACCESS_KEY_FILE: accessFile,
THT_WS_PSD_CLINICAL_EVIDENCE_SECRET_KEY_FILE: secretFile,
},
})).evidence.sources[0];
expect(withoutToken).toMatchObject({ access_key_file: accessFile, secret_key_file: secretFile });
expect(withoutToken).not.toHaveProperty("session_token_file");
}); });
test("omits Evidence configuration and policy when the descriptor has no Evidence", () => { test("omits Evidence configuration and policy when the descriptor has no Evidence", () => {