Unify §3 test overlaps (todo §3); fold the 5× image-pin checks into one compose.test.ts scan + same-version sidecar test, drop the duplicate committed-JWKS re-validation in config.test.ts

This commit is contained in:
2026-06-17 17:07:39 +02:00
parent 360449e76b
commit fcf042fa66
7 changed files with 41 additions and 74 deletions

View File

@@ -1,8 +1,9 @@
// Guards the dev/prod compose split + stack ordering (§3): long-running Ory services
// carry readiness healthchecks so `depends_on: service_healthy` works, the web app waits
// for the services it talks to (kratos + keto, per config.ts), prod publishes no internal
// Ory ports while dev exposes the ones a browser must reach, and the visual E2E stays
// Ory-free. Real boot is verified by running the stack; this catches edits.
// Guards the dev/prod compose split + stack ordering (§3): every image is pinned to an
// exact version (AGENTS.md), long-running Ory services carry readiness healthchecks so
// `depends_on: service_healthy` works, the web app waits for the services it talks to
// (kratos + keto, per config.ts), prod publishes no internal Ory ports while dev exposes
// the ones a browser must reach, and the visual E2E stays Ory-free. Real boot is verified
// by running the stack; this catches edits.
import { test } from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
@@ -15,6 +16,24 @@ const e2e = read("compose.e2e.yml");
// compose.yml lists web first, postgres second — slice the web service block.
const webBlock = compose.slice(compose.indexOf("\n web:"), compose.indexOf("\n postgres:"));
test("every image is pinned to an exact version, never a floating tag", () => {
// One scan over all compose files (postgres, the three Ory pairs, mailpit); web/e2e build.
const images = [...[compose, override, e2e].join("\n").matchAll(/image:\s*(\S+)/g)].map((m) => m[1]!);
assert.ok(images.length >= 8, "scans the pinned images");
for (const img of images) {
assert.match(img.split(":").at(-1)!, /^v?\d+\.\d+/, `${img} pins a version-like tag`);
assert.doesNotMatch(img, /latest|edge|[\^~*]/, `${img} is exact, not floating`);
}
});
test("each Ory service and its migrate sidecar share one pinned version", () => {
for (const svc of ["hydra", "keto", "kratos"]) {
const tags = [...compose.matchAll(new RegExp(`image:\\s*oryd/${svc}:(\\S+)`, "g"))].map((m) => m[1]);
assert.equal(tags.length, 2, `${svc} + ${svc}-migrate both present`);
assert.equal(tags[0], tags[1], `${svc} server + migrate pinned to the same version`);
}
});
test("long-running Ory services declare readiness healthchecks", () => {
for (const [svc, port] of [["kratos", 4433], ["keto", 4466], ["hydra", 4444]] as const)
assert.match(compose, new RegExp(`wget[^\\n]*:${port}/health/ready`),

View File

@@ -1,7 +1,5 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { test } from "node:test";
import { fileURLToPath } from "node:url";
import { loadConfig } from "./config.ts";
// Explicit secure-secret enforcement (no environment sniffing): secrets are the only
@@ -27,17 +25,10 @@ test("loads dev defaults when the environment is empty", () => {
test("JWKS_URL defaults to the committed Kratos tokenizer signing key, not an http endpoint", () => {
// The session JWT is signed by the tokenizer key (kratos.yml jwks_url); Kratos does NOT
// republish it at /.well-known/jwks.json, so the §4 verifier reads that same file://.
// gen-jwks.test.ts owns that the file is a valid ES256 signing key with a kid.
const url = new URL(loadConfig({}).jwksUrl);
assert.equal(url.protocol, "file:");
assert.match(url.pathname, /tokenizer\/jwks\.json$/);
// And that file is a real ES256 signing JWKS carrying a kid (what the verifier resolves by).
const path = fileURLToPath(new URL("../ory/kratos/tokenizer/jwks.json", import.meta.url));
const key = (JSON.parse(readFileSync(path, "utf8")) as { keys: { alg: string; kid: string; kty: string }[] }).keys[0];
assert.ok(key, "tokenizer JWKS must have a key");
assert.equal(key.alg, "ES256");
assert.equal(key.kty, "EC");
assert.ok(key.kid, "tokenizer JWKS key must carry a kid");
});
test("parses explicit boolean toggles and rejects non-boolean values", () => {

View File

@@ -1,8 +1,7 @@
// Guards the Ory Hydra config (§3): image pinned to an exact version (AGENTS.md),
// migrations run before the server (hydra-migrate → hydra), the DSN targets the hydra
// database, the server listens on the public/admin ports, and the issuer +
// login/consent/logout URLs point at our app. Real boot is verified by running the
// stack; this catches edits.
// Guards the Ory Hydra config (§3): migrations run before the server (hydra-migrate →
// hydra), the DSN targets the hydra database, the server listens on the public/admin
// ports, and the issuer + login/consent/logout URLs point at our app. Version pinning is
// in compose.test.ts. Real boot is verified by running the stack; this catches edits.
import { test } from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
@@ -11,15 +10,6 @@ const read = (p: string) => readFileSync(new URL(`../${p}`, import.meta.url), "u
const compose = read("compose.yml");
const hydraYml = read("ory/hydra/hydra.yml");
test("compose pins both hydra services to one exact version", () => {
const tags = [...compose.matchAll(/image:\s*oryd\/hydra:(\S+)/g)].map((m) => m[1]);
assert.equal(tags.length, 2, "hydra + hydra-migrate both present");
assert.equal(tags[0], tags[1], "both pinned to the same version");
const tag = tags[0]!;
assert.match(tag, /^v\d+\.\d+\.\d+$/, `${tag} is an exact vMAJOR.MINOR.PATCH`);
assert.doesNotMatch(tag, /latest|[\^~*]/, `${tag} is exact, not floating`);
});
test("hydra migrations run once before the server starts", () => {
assert.ok((compose.match(/migrate sql -e --yes/g) ?? []).length >= 2,
"hydra-migrate runs SQL migrations (alongside kratos)");

View File

@@ -1,8 +1,7 @@
// Guards the Ory Keto config (§3): image pinned to an exact version (AGENTS.md),
// migrations run before the server (keto-migrate → keto), the DSN targets the keto
// database, read/write APIs serve on the ports config.ts points at, and the OPL
// declares the role/group/resource namespaces. Real boot is verified by running the
// stack; this catches edits.
// Guards the Ory Keto config (§3): migrations run before the server (keto-migrate →
// keto), the DSN targets the keto database, read/write APIs serve on the ports config.ts
// points at, and the OPL declares the role/group/resource namespaces. Version pinning is
// in compose.test.ts. Real boot is verified by running the stack; this catches edits.
import { test } from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
@@ -12,15 +11,6 @@ const compose = read("compose.yml");
const ketoYml = read("ory/keto/keto.yml");
const opl = read("ory/keto/namespaces.keto.ts");
test("compose pins both keto services to one exact version", () => {
const tags = [...compose.matchAll(/image:\s*oryd\/keto:(\S+)/g)].map((m) => m[1]);
assert.equal(tags.length, 2, "keto + keto-migrate both present");
assert.equal(tags[0], tags[1], "both pinned to the same version");
const tag = tags[0]!;
assert.match(tag, /^v\d+\.\d+\.\d+$/, `${tag} is an exact vMAJOR.MINOR.PATCH`);
assert.doesNotMatch(tag, /latest|[\^~*]/, `${tag} is exact, not floating`);
});
test("keto migrations run once before the server starts", () => {
assert.match(compose, /migrate\s+up\s+-y/, "keto-migrate runs migrations");
assert.ok((compose.match(/condition:\s*service_completed_successfully/g) ?? []).length >= 2,

View File

@@ -1,7 +1,7 @@
// Guards the Ory Kratos config (§3): image pinned to an exact version (AGENTS.md),
// migrations run before the server (kratos-migrate → kratos), the DSN targets the
// kratos database, and the identity schema carries email (password identifier) +
// name traits. Real boot is verified by running the stack; this catches edits.
// Guards the Ory Kratos config (§3): migrations run before the server (kratos-migrate →
// kratos), the DSN targets the kratos database, and the identity schema carries email
// (password identifier) + name traits. Version pinning is in compose.test.ts. Real boot
// is verified by running the stack; this catches edits.
import { test } from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
@@ -11,15 +11,6 @@ const compose = read("compose.yml");
const kratosYml = read("ory/kratos/kratos.yml");
const schema = JSON.parse(read("ory/kratos/identity.schema.json"));
test("compose pins both kratos services to one exact version", () => {
const tags = [...compose.matchAll(/image:\s*oryd\/kratos:(\S+)/g)].map((m) => m[1]);
assert.equal(tags.length, 2, "kratos + kratos-migrate both present");
assert.equal(tags[0], tags[1], "both pinned to the same version");
const tag = tags[0]!;
assert.match(tag, /^v\d+\.\d+\.\d+$/, `${tag} is an exact vMAJOR.MINOR.PATCH`);
assert.doesNotMatch(tag, /latest|[\^~*]/, `${tag} is exact, not floating`);
});
test("migrations run once before the server starts", () => {
assert.match(compose, /migrate sql -e --yes/, "kratos-migrate runs SQL migrations");
assert.match(compose, /condition:\s*service_completed_successfully/,
@@ -98,10 +89,3 @@ test("the committed OIDC claims mapper maps email + name", () => {
assert.match(mapper, /given_name/, "given name → name.first");
assert.match(mapper, /family_name/, "family name → name.last");
});
test("compose pins the dev mail catcher to an exact version", () => {
const tag = read("compose.override.yml").match(/image:\s*axllent\/mailpit:(\S+)/)?.[1];
assert.ok(tag, "compose.override.yml pins a mailpit image");
assert.match(tag, /^v\d+\.\d+\.\d+$/, `${tag} is an exact version`);
assert.doesNotMatch(tag, /latest|edge|[\^~*]/, `${tag} is exact, not floating`);
});

View File

@@ -1,6 +1,6 @@
// Guards the Ory Postgres config (§3): image stays pinned to an exact version
// (AGENTS.md rule) and each Ory service keeps its own database. Real container
// behaviour is verified by booting postgres in CI/e2e; this catches edits.
// Guards the Ory Postgres config (§3): each Ory service keeps its own database (the
// image pin is covered by compose.test.ts's global scan). Real container behaviour is
// verified by booting postgres in CI/e2e; this catches edits.
import { test } from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
@@ -8,13 +8,6 @@ import { readFileSync } from "node:fs";
const read = (p: string) => readFileSync(new URL(`../${p}`, import.meta.url), "utf8");
const ORY_DATABASES = ["hydra", "keto", "kratos"]; // one DB per Ory service
test("compose pins the postgres image to an exact version", () => {
const tag = read("compose.yml").match(/image:\s*postgres:(\S+)/)?.[1];
assert.ok(tag, "compose.yml pins a postgres image");
assert.match(tag, /^\d+\.\d+/, `${tag} pins major.minor`);
assert.doesNotMatch(tag, /latest|[\^~*]/, `${tag} is exact, not floating`);
});
test("init SQL gives each Ory service its own database", () => {
const sql = read("ory/postgres/init/init.sql");
for (const db of ORY_DATABASES) {