diff --git a/tests/runner-e2e/docker-postgres.test.ts b/tests/runner-e2e/docker-postgres.test.ts index 87c4ef74729..f7a035e7060 100644 --- a/tests/runner-e2e/docker-postgres.test.ts +++ b/tests/runner-e2e/docker-postgres.test.ts @@ -250,4 +250,7 @@ it("creates, verifies and cleans the explicit amd64 receipt using only the owned expect(databaseIdentityMatches(databaseFingerprintIdentity({ [DATABASE_LANE_ENV]: "owned-docker-postgres", [POSTGRES_ARTIFACT_ENV]: "linux-amd64" }), publicDockerDatabaseIdentity(owned.receipt))).toBe(true); expect(databaseIdentityMatches(databaseFingerprintIdentity({ [DATABASE_LANE_ENV]: "owned-docker-postgres" }), publicDockerDatabaseIdentity(owned.receipt))).toBe(false); await owned.close(); expect(state.container).toBeNull(); expect(state.volume).toBeNull(); expect(owned.receipt.status).toBe("removed"); + const result = { schema: "paperclip.runner-e2e.result/v1", executionId: "remote-offline", attempt: 1, status: "passed", profileId: "offline", environmentId: "local", caseId: "offline", provider: null, model: null, runtimeMode: "none", startedAt: "2026-10-27T00:00:00Z", finishedAt: "2026-10-27T00:00:01Z", durationMs: 1000, cleanup: "passed", databaseIdentity: publicDockerDatabaseIdentity(owned.receipt) }; + expect(() => validateRetainedRunnerResult(result)).not.toThrow(); + for (const change of [{ architecture: undefined }, { image: POSTGRES_IMAGE }, { imageId: POSTGRES_IMAGE_ID }]) expect(() => validateRetainedRunnerResult({ ...result, databaseIdentity: { ...result.databaseIdentity, ...change } })).toThrow(); }); diff --git a/tests/runner-e2e/docker-postgres.ts b/tests/runner-e2e/docker-postgres.ts index c342d43079d..3ee5369c4e8 100644 --- a/tests/runner-e2e/docker-postgres.ts +++ b/tests/runner-e2e/docker-postgres.ts @@ -27,7 +27,7 @@ export function selectedPostgresArtifact(env = process.env): { image: PostgresIm if (!imageId) throw new Error("Unreviewed amd64 PostgreSQL image identity"); return { image: POSTGRES_AMD64_IMAGE, imageId, architecture: "amd64" as const, version: POSTGRES_VERSION }; } -function knownPostgresReceipt(receipt: { image: string; imageId: string; architecture?: string }) { +export function reviewedPostgresReceiptIdentity(receipt: { image: string; imageId: string; architecture?: string }) { return receipt.image === POSTGRES_IMAGE && receipt.imageId === POSTGRES_IMAGE_ID && !receipt.architecture || receipt.image === POSTGRES_AMD64_IMAGE && POSTGRES_AMD64_IMAGE_IDS.some(id => id === receipt.imageId) && receipt.architecture === "amd64"; } @@ -115,7 +115,7 @@ function exactPasswordBindSource(source: unknown, receipt: DockerDatabaseReceipt export function assertOwnedContainer(value: any, receipt: DockerDatabaseReceipt, running?: boolean) { const mounts = value?.Mounts, data = mounts?.find((m: any) => m.Destination === "/var/lib/postgresql"), password = mounts?.find((m: any) => m.Destination === "/run/secrets/postgres-password"); const ports = value?.HostConfig?.PortBindings?.["5432/tcp"], config = value?.Config; - if (!fullId(receipt.containerId) || value?.Id !== receipt.containerId || value.Name !== `/${receipt.containerName}` || !knownPostgresReceipt(receipt) || value.Image !== receipt.imageId || config?.Image !== receipt.image || !hasLabels(config.Labels, receipt) + if (!fullId(receipt.containerId) || value?.Id !== receipt.containerId || value.Name !== `/${receipt.containerName}` || !reviewedPostgresReceiptIdentity(receipt) || value.Image !== receipt.imageId || config?.Image !== receipt.image || !hasLabels(config.Labels, receipt) || value.HostConfig.Privileged !== false || !["default", "bridge"].includes(value.HostConfig.NetworkMode) || !Array.isArray(mounts) || mounts.length !== 2 || data?.Type !== "volume" || data.Name !== receipt.volumeName || data.RW !== true || password?.Type !== "bind" || !exactPasswordBindSource(password.Source, receipt) || password.RW !== false || !Array.isArray(ports) || ports.length !== 1 || ports[0].HostIp !== "127.0.0.1" || Object.keys(value.HostConfig.PortBindings).join() !== "5432/tcp" @@ -132,7 +132,7 @@ async function privateRoot(root: string) { } function assertReceipt(value: DockerDatabaseReceipt, root: string) { if (value?.schemaVersion !== 1 || value.lane !== "owned-docker-postgres" || value.temporaryRoot !== root || !uuidHex(value.nonce) || value.containerName !== `paperclip-e2e-pg-${value.nonce}` || value.volumeName !== `paperclip-e2e-pg-data-${value.nonce}` - || !knownPostgresReceipt(value) || value.imageVersion !== POSTGRES_VERSION || !/^[a-z][a-z0-9_]{1,60}$/.test(value.username) || !/^[a-z][a-z0-9_]{1,60}$/.test(value.database)) throw new Error("Invalid private Docker database receipt"); + || !reviewedPostgresReceiptIdentity(value) || value.imageVersion !== POSTGRES_VERSION || !/^[a-z][a-z0-9_]{1,60}$/.test(value.username) || !/^[a-z][a-z0-9_]{1,60}$/.test(value.database)) throw new Error("Invalid private Docker database receipt"); } async function readPrivateDockerReceipt(filename: string, root: string) { await privateRoot(root); @@ -275,7 +275,7 @@ export function databaseFingerprintIdentity(env = process.env) { export type DatabaseFingerprintIdentity = ReturnType; export function databaseIdentityMatches(expected: DatabaseFingerprintIdentity, actual?: DockerDatabaseIdentity) { if (expected.lane === "embedded-postgres") return actual === undefined; - return !!actual && actual.lane === expected.lane && actual.image === expected.image && actual.imageId === expected.imageId && actual.imageVersion === expected.version && knownPostgresReceipt(actual) && (expected.architecture ?? undefined) === actual.architecture + return !!actual && actual.lane === expected.lane && actual.image === expected.image && actual.imageId === expected.imageId && actual.imageVersion === expected.version && reviewedPostgresReceiptIdentity(actual) && (expected.architecture ?? undefined) === actual.architecture && actual.artifactVerified && !!actual.contextSha256 && !!actual.engineSha256 && fullId(actual.containerId) && !!actual.cluster && /^18\.6(?:\s|$)/.test(actual.cluster.version) && /^[0-9]+$/.test(actual.cluster.systemIdentifier) && ["ready", "removed"].includes(actual.status); } diff --git a/tests/runner-e2e/result-validation.ts b/tests/runner-e2e/result-validation.ts index 86370172047..2b714abc0f4 100644 --- a/tests/runner-e2e/result-validation.ts +++ b/tests/runner-e2e/result-validation.ts @@ -1,4 +1,4 @@ -import { POSTGRES_IMAGE, POSTGRES_IMAGE_ID } from "./docker-postgres.js"; +import { POSTGRES_IMAGE, POSTGRES_IMAGE_ID, POSTGRES_AMD64_IMAGE, POSTGRES_AMD64_IMAGE_IDS, reviewedPostgresReceiptIdentity } from "./docker-postgres.js"; import { validateWorkerTiming } from "./worker-timing.js"; import { connectionCheckpoints, connectionEvidencePasses, connectionRunSignals } from "./connection-evidence.js"; import { githubJourneyCheckpoints, gradeGitHubReviewJourney } from "./github-review-evidence.js"; @@ -226,12 +226,14 @@ const matcher: Rule = (value, at) => { rule(value, at); }; const fields = { - databaseIdentity: optional(closedShape({ - lane: oneOf("owned-docker-postgres"), image: oneOf(POSTGRES_IMAGE), imageId: oneOf(POSTGRES_IMAGE_ID), imageVersion: oneOf("18.6-1.pgdg13+2"), + databaseIdentity: optional((value, at) => { closedShape({ + lane: oneOf("owned-docker-postgres"), image: oneOf(POSTGRES_IMAGE, POSTGRES_AMD64_IMAGE), imageId: oneOf(POSTGRES_IMAGE_ID, ...POSTGRES_AMD64_IMAGE_IDS), architecture: optional(oneOf("amd64")), imageVersion: oneOf("18.6-1.pgdg13+2"), artifactVerified: boolean, contextSha256: nullable(matching(/^[a-f0-9]{64}$/)), engineSha256: nullable(matching(/^[a-f0-9]{64}$/)), containerId: nullable(matching(/^[a-f0-9]{64}$/)), cluster: optional(closedShape({ version: matching(/^18\.6(?:\s|$)/), systemIdentifier: matching(/^[0-9]+$/) })), status: oneOf("creating", "ready", "failed", "removed", "cleanup-failed", "startup-unconfirmed"), - })), + })(value, at); + if (!reviewedPostgresReceiptIdentity(value as { image: string; imageId: string; architecture?: string })) invalid(at); + }), accountingIdentity: optional(shape({ campaignId: string, attemptId: string })), publicMcp: optional(assistant), schema: oneOf(