import { test } from "node:test"; import assert from "node:assert/strict"; import { digestFrom, pinnedReference, registryAddress, repositoryFor } from "../src/lifecycle/registry.ts"; /** * The registry inside a scenario (novox/hq 04-ISSUES/009). * * These test the pure parts. The parts that need a registry are exercised by raising a * scenario, because a fake registry would assert that the fake behaves as expected * (novox/hq ADR 0017). */ test("a digest is read from what the registry actually said", () => { // The real shape of `docker push` output. The digest here is the REGISTRY's, not Docker // Hub's, and that is the point: a declaration pins what this registry serves. const output = "The push refers to repository [localhost:5000/alpine]\n" + "63f227048c13: Pushed\n" + "3.20: digest: sha256:6c2a9711b0a9f32b0239d9222eb1072309cf46c6431d319ae249186d811a987c size: 528\n"; assert.equal( digestFrom(output), "sha256:6c2a9711b0a9f32b0239d9222eb1072309cf46c6431d319ae249186d811a987c", ); }); test("no digest is not an empty digest", () => { // A push that reported no digest leaves nothing for a declaration to pin, and inventing one // would be worse than failing — the host would refuse it later, further from the cause. assert.equal(digestFrom("The push refers to repository [localhost:5000/alpine]\n"), null); assert.equal(digestFrom(""), null); // Hex, but the wrong LENGTH. An earlier version used "tooshort", whose letters fall outside // a-f — so it failed the character class and proved nothing about the length check. assert.equal(digestFrom("digest: sha256:abc123"), null); assert.equal(digestFrom("digest: sha256:" + "a".repeat(63)), null, "63 is not 64"); }); test("the repository is the reference without its tag", () => { assert.equal(repositoryFor("alpine:3.20"), "alpine"); assert.equal(repositoryFor("alpine"), "alpine"); assert.equal(repositoryFor("library/postgres:17"), "library/postgres"); // A port in a hostname is a colon that is NOT a tag, and treating it as one would serve the // image from a truncated path. assert.equal(repositoryFor("localhost:5000/alpine:3.20"), "localhost:5000/alpine"); assert.equal(repositoryFor("localhost:5000/alpine"), "localhost:5000/alpine"); }); test("the registry's address is derived from its segment", () => { assert.equal(registryAddress("192.0.2.0/24"), "192.0.2.250"); assert.equal(registryAddress("198.51.100.0/24"), "198.51.100.250"); // An IPv6-only segment cannot host it, and saying so beats producing an address nothing // can be pointed at. assert.throws(() => registryAddress("2001:db8:a::/48"), /not an IPv4 network/); }); test("what a declaration pins is the registry's own digest", () => { // Not Docker Hub's. ADR 0006 requires a reference that is exact and cannot move, and a // digest this registry assigned is both. const pinned = pinnedReference("192.0.2.250", { requested: "alpine:3.20", repository: "alpine", digest: "sha256:" + "6".repeat(64), }); assert.equal(pinned, `192.0.2.250:5000/alpine@sha256:${"6".repeat(64)}`); assert.ok(pinned.includes("@sha256:"), "the host refuses anything not pinned by digest"); assert.ok(!pinned.includes(":3.20"), "a tag would move; the digest is what is pinned"); });