diff --git a/src/apps/cli/test-support/p2p-automatic-host.test.ts b/src/apps/cli/test-support/p2p-automatic-host.test.ts index 229fcc57..f4e03930 100644 --- a/src/apps/cli/test-support/p2p-automatic-host.test.ts +++ b/src/apps/cli/test-support/p2p-automatic-host.test.ts @@ -82,7 +82,7 @@ export async function runP2PAutomaticHostFixture(options: CLIOptions, context: C } const commandOutcome = runCommand(options, context).then( (result) => ({ type: "result" as const, result }), - (error) => ({ type: "error" as const, error }) + (error: unknown) => ({ type: "error" as const, error }) ); const connectedWaitStarted = Date.now(); while (!service.transportLifecycle.isConnected) { diff --git a/src/features/ReviewHarness/reviewHarnessFileContentHashBenchmarkRuntime.ts b/src/features/ReviewHarness/reviewHarnessFileContentHashBenchmarkRuntime.ts index 8d6b8831..12a49dbc 100644 --- a/src/features/ReviewHarness/reviewHarnessFileContentHashBenchmarkRuntime.ts +++ b/src/features/ReviewHarness/reviewHarnessFileContentHashBenchmarkRuntime.ts @@ -5,16 +5,41 @@ import { type FileContentHashBenchmarkOperations, } from "./reviewHarnessFileContentHashBenchmark"; +type XxHash64Engine = { + h64Raw(content: Uint8Array): unknown; +}; + +function isXxHashFactory(value: unknown): value is () => unknown { + return typeof value === "function"; +} + +function isXxHash64Engine(value: unknown): value is XxHash64Engine { + return typeof value === "object" && value !== null && "h64Raw" in value && typeof value.h64Raw === "function"; +} + export async function createFileContentHashBenchmarkOperations( clock: Pick = performance ): Promise { + const factory: unknown = xxhashNew; + if (!isXxHashFactory(factory)) { + throw new TypeError("The xxHash64 factory must be callable."); + } const started = clock.now(); - const xxHash = await xxhashNew(); + const xxHash: unknown = await factory(); const xxHashInitialisationMs = clock.now() - started; + if (!isXxHash64Engine(xxHash)) { + throw new TypeError("The xxHash64 engine must provide h64Raw."); + } return { xxHashInitialisationMs, createInput: createFixedFileContentHashInput, sha256: (content) => computeFileContentHash(content), - xxHash64: async (content) => xxHash.h64Raw(new Uint8Array(await content.arrayBuffer())), + xxHash64: async (content) => { + const hash = xxHash.h64Raw(new Uint8Array(await content.arrayBuffer())); + if (typeof hash !== "bigint") { + throw new TypeError("The xxHash64 result must be a bigint."); + } + return hash; + }, }; } diff --git a/src/features/ReviewHarness/reviewHarnessFileContentHashBenchmarkRuntime.unit.spec.ts b/src/features/ReviewHarness/reviewHarnessFileContentHashBenchmarkRuntime.unit.spec.ts index a5d6db38..c8f60005 100644 --- a/src/features/ReviewHarness/reviewHarnessFileContentHashBenchmarkRuntime.unit.spec.ts +++ b/src/features/ReviewHarness/reviewHarnessFileContentHashBenchmarkRuntime.unit.spec.ts @@ -1,6 +1,11 @@ -import { describe, expect, it } from "vitest"; +import { afterEach, describe, expect, it, vi } from "vitest"; +import { xxhashNew } from "octagonal-wheels/hash/xxhash.js"; import { createFileContentHashBenchmarkOperations } from "./reviewHarnessFileContentHashBenchmarkRuntime"; +vi.mock("octagonal-wheels/hash/xxhash.js", { spy: true }); + +afterEach(() => vi.restoreAllMocks()); + describe("Review Harness file content hash benchmark adapter", () => { it("uses the packaged SHA-256 helper and xxHash64 engine with fixed Blob content", async () => { const operations = await createFileContentHashBenchmarkOperations(); @@ -11,5 +16,19 @@ describe("Review Harness file content hash benchmark adapter", () => { expect(typeof (await operations.xxHash64(content))).toBe("bigint"); expect(await operations.sha256(content)).toBe(await operations.sha256(content)); expect(await operations.xxHash64(content)).toBe(await operations.xxHash64(content)); + expect(xxhashNew).toHaveBeenCalledOnce(); + }); + + it("rejects an engine without an xxHash64 operation", async () => { + vi.mocked(xxhashNew).mockResolvedValueOnce({}); + + await expect(createFileContentHashBenchmarkOperations()).rejects.toBeInstanceOf(TypeError); + }); + + it("rejects a hash result which is not a bigint", async () => { + vi.mocked(xxhashNew).mockResolvedValueOnce({ h64Raw: () => 123 }); + const operations = await createFileContentHashBenchmarkOperations(); + + await expect(operations.xxHash64(new Blob(["fixed content"]))).rejects.toBeInstanceOf(TypeError); }); });