/** * Spec-conformance test for `HashAlgorithmRegistry`. Pins the OTel * replacement-event surface and singleton-isolation invariant from the * `bitpacker-crypto-rewrite` spec. */ import { describe, expect, it } from "@suaveplan/testing/runner"; import { withRecordingTracer } from "../../tests/helpers/recording-tracer.js"; import { _resetDefaultHashRegistryForTesting, createHashRegistry, defaultHashRegistry, type HashAlgorithm, } from "./hash-registry.js"; describe("hash-registry — spec conformance", () => { it("replacement emits OTel hash-registry.replaced span with attributes", { timeout: 5000, }, () => { withRecordingTracer((session) => { const reg = createHashRegistry(); const replacement: HashAlgorithm = { name: "sha256", outputBytes: 32, hash: () => Promise.resolve(new Uint8Array(32)), }; reg.register(replacement); const spans = session .spansFor("bitpacker-crypto") .filter( (s) => s.name === "bitpacker-crypto.hash-registry.replaced" ); expect(spans).toHaveLength(1); const span = spans[0]; expect(span?.attributes["bitpacker.crypto.algorithm"]).toBe( "sha256" ); expect( span?.attributes["bitpacker.crypto.previous_output_bytes"] ).toBe(32); expect(span?.attributes["bitpacker.crypto.new_output_bytes"]).toBe( 32 ); expect(span?.ended).toBe(true); }); }); it("registering a fresh name does NOT emit replaced span", { timeout: 5000, }, () => { withRecordingTracer((session) => { const reg = createHashRegistry(); reg.register({ name: "novel", outputBytes: 8, hash: () => Promise.resolve(new Uint8Array(8)), }); const spans = session .spansFor("bitpacker-crypto") .filter( (s) => s.name === "bitpacker-crypto.hash-registry.replaced" ); expect(spans).toHaveLength(0); }); }); it("createHashRegistry isolates from singleton", { timeout: 5000 }, () => { _resetDefaultHashRegistryForTesting(); const fresh = createHashRegistry(); fresh.register({ name: "iso-test", outputBytes: 1, hash: () => Promise.resolve(new Uint8Array(1)), }); expect(defaultHashRegistry().has("iso-test")).toBe(false); }); });