Validate untyped hash and CLI fixture values

The packaged xxHash export has no resolved type declaration. Check the
factory, engine, and bigint result at the benchmark boundary, and treat
CLI fixture rejection values as unknown.

Add regression cases for invalid engines and hash results while retaining
the real-engine and initialisation-count assertions.
This commit is contained in:
vorotamoroz committed 2026-10-31 07:13:26 +00:00
1 parent 5e3e7e1330
commit c7a62d5e1c
3 files changed
+48 -4

No files matched your search

@@ -82,7 +82,7 @@ export async function runP2PAutomaticHostFixture(options: CLIOptions, context: C
} }
const commandOutcome = runCommand(options, context).then( const commandOutcome = runCommand(options, context).then(
(result) => ({ type: "result" as const, result }), (result) => ({ type: "result" as const, result }),
(error) => ({ type: "error" as const, error }) (error: unknown) => ({ type: "error" as const, error })
); );
const connectedWaitStarted = Date.now(); const connectedWaitStarted = Date.now();
while (!service.transportLifecycle.isConnected) { while (!service.transportLifecycle.isConnected) {
@@ -5,16 +5,41 @@ import {
type FileContentHashBenchmarkOperations, type FileContentHashBenchmarkOperations,
} from "./reviewHarnessFileContentHashBenchmark"; } 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( export async function createFileContentHashBenchmarkOperations(
clock: Pick<Performance, "now"> = performance clock: Pick<Performance, "now"> = performance
): Promise<FileContentHashBenchmarkOperations> { ): Promise<FileContentHashBenchmarkOperations> {
const factory: unknown = xxhashNew;
if (!isXxHashFactory(factory)) {
throw new TypeError("The xxHash64 factory must be callable.");
}
const started = clock.now(); const started = clock.now();
const xxHash = await xxhashNew(); const xxHash: unknown = await factory();
const xxHashInitialisationMs = clock.now() - started; const xxHashInitialisationMs = clock.now() - started;
if (!isXxHash64Engine(xxHash)) {
throw new TypeError("The xxHash64 engine must provide h64Raw.");
}
return { return {
xxHashInitialisationMs, xxHashInitialisationMs,
createInput: createFixedFileContentHashInput, createInput: createFixedFileContentHashInput,
sha256: (content) => computeFileContentHash(content), 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;
},
}; };
} }
@@ -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"; import { createFileContentHashBenchmarkOperations } from "./reviewHarnessFileContentHashBenchmarkRuntime";
vi.mock("octagonal-wheels/hash/xxhash.js", { spy: true });
afterEach(() => vi.restoreAllMocks());
describe("Review Harness file content hash benchmark adapter", () => { describe("Review Harness file content hash benchmark adapter", () => {
it("uses the packaged SHA-256 helper and xxHash64 engine with fixed Blob content", async () => { it("uses the packaged SHA-256 helper and xxHash64 engine with fixed Blob content", async () => {
const operations = await createFileContentHashBenchmarkOperations(); 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(typeof (await operations.xxHash64(content))).toBe("bigint");
expect(await operations.sha256(content)).toBe(await operations.sha256(content)); expect(await operations.sha256(content)).toBe(await operations.sha256(content));
expect(await operations.xxHash64(content)).toBe(await operations.xxHash64(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);
}); });
}); });