import { describe, expect, it, vi } from "vitest"; import { encodeToolcraftModelDocument } from "../canonical/model-document-codec"; import { digestToolcraftModelDocument } from "../canonical/model-document-digest"; import { createModelNode, createValidModelDocument, } from "../canonical/model-document-test-support"; import { TOOLCRAFT_DEFAULT_MODEL_IMPORT_LIMITS } from "../model-import-limits"; import { analyzeToolcraftModel } from "../topology/model-topology-analysis"; import { createToolcraftModelWorkerClient } from "./model-import-worker-client"; import { snapshotToolcraftModelWorkerAnalysisSummary } from "./model-import-worker-diagnostics"; import { copyArrayBuffer, draftFor, emitDecodeLifecycle, FakeWorker, forgedDigest, importRequest, resultFor, } from "./model-import-worker-client-test-support"; import { TOOLCRAFT_MODEL_WORKER_MAX_DIAGNOSTICS, type ToolcraftModelWorkerResponse, } from "./model-import-worker-protocol"; import { createToolcraftModelWorkerReceiptDigest, } from "./model-import-worker-receipt"; describe("createToolcraftModelWorkerClient protocol validation", () => { it("publishes a verified draft before its verified final result", async () => { const worker = new FakeWorker(); const publicationOrder: string[] = []; const client = createToolcraftModelWorkerClient({ workerFactory: () => worker }); const pending = client.importModel(importRequest("verified-draft"), { onDraft: () => publicationOrder.push("draft"), onResult: () => publicationOrder.push("result"), }); const request = worker.posts[0]!.message; emitDecodeLifecycle(worker, request); worker.emitMessage(resultFor(request)); await expect(pending).resolves.toMatchObject({ kind: "result" }); expect(publicationOrder).toEqual(["draft", "result"]); }); it("rejects a forged draft before publication and before any result", async () => { const worker = new FakeWorker(); const onDraft = vi.fn(); const onResult = vi.fn(); const client = createToolcraftModelWorkerClient({ workerFactory: () => worker }); const pending = client.importModel(importRequest("forged-draft"), { onDraft, onResult, }); const request = worker.posts[0]!.message; worker.emitMessage({ generation: request.generation, jobId: request.jobId, kind: "progress", phase: "decoding", progress: 0.9, }); const forged = draftFor(request); if (forged.kind !== "draft") throw new Error("Expected model draft."); worker.emitMessage({ ...forged, draft: { ...forged.draft, canonicalDocumentDigest: forgedDigest }, }); await expect(pending).resolves.toMatchObject({ feedback: { code: "model-worker-invalid-message" }, kind: "error", }); expect(onDraft).not.toHaveBeenCalled(); expect(onResult).not.toHaveBeenCalled(); expect(worker.terminate).toHaveBeenCalledTimes(1); }); it("rejects a valid final document that differs from its verified draft", async () => { const worker = new FakeWorker(); const onDraft = vi.fn(); const onResult = vi.fn(); const client = createToolcraftModelWorkerClient({ workerFactory: () => worker }); const pending = client.importModel(importRequest("draft-result-mismatch"), { onDraft, onResult, }); const request = worker.posts[0]!.message; emitDecodeLifecycle(worker, request); const finalDocument = createValidModelDocument({ nodes: [createModelNode({ name: "Unexpected replacement" })], }); const finalBytes = encodeToolcraftModelDocument(finalDocument); const resultWithoutReceipt = { analysis: snapshotToolcraftModelWorkerAnalysisSummary(analyzeToolcraftModel( finalDocument, "realtime-mesh", TOOLCRAFT_DEFAULT_MODEL_IMPORT_LIMITS, )), appearanceResources: [], canonicalDocument: copyArrayBuffer(finalBytes), canonicalDocumentDigest: digestToolcraftModelDocument(finalDocument), operation: "decode-and-analyze" as const, }; worker.emitMessage({ generation: request.generation, jobId: request.jobId, kind: "result", result: { ...resultWithoutReceipt, receiptDigest: createToolcraftModelWorkerReceiptDigest({ generation: request.generation, jobId: request.jobId, result: resultWithoutReceipt, }), }, }); await expect(pending).resolves.toMatchObject({ feedback: { code: "model-worker-invalid-message" }, kind: "error", }); expect(onDraft).toHaveBeenCalledTimes(1); expect(onResult).not.toHaveBeenCalled(); expect(worker.terminate).toHaveBeenCalledTimes(1); }); it("terminates on oversized typed feedback received from the worker", async () => { const worker = new FakeWorker(); const client = createToolcraftModelWorkerClient({ workerFactory: () => worker }); const pending = client.importModel(importRequest("job-1")); const request = worker.posts[0]!.message; worker.emitMessage({ feedback: { category: "format", code: "x".repeat(500), message: "y".repeat(10_000), }, generation: request.generation, jobId: request.jobId, kind: "error", }); const outcome = await pending; if (outcome.kind !== "error") throw new Error("Expected error."); expect(outcome.feedback.code).toBe("model-worker-invalid-message"); expect(worker.terminate).toHaveBeenCalledTimes(1); }); it.each([ ["forged digest", (response: ToolcraftModelWorkerResponse) => { if (response.kind === "result") { (response.result as unknown as { canonicalDocumentDigest: string }) .canonicalDocumentDigest = forgedDigest; } }], ["malformed canonical bytes", (response: ToolcraftModelWorkerResponse) => { if (response.kind === "result") { (response.result as unknown as { canonicalDocument: ArrayBuffer }) .canonicalDocument = new ArrayBuffer(8); } }], ["malformed analysis", (response: ToolcraftModelWorkerResponse) => { if (response.kind === "result") { (response.result.analysis.statistics as unknown as { vertexCount: number }) .vertexCount = -1; } }], ["contradictory analysis outcome", (response: ToolcraftModelWorkerResponse) => { if (response.kind === "result") { (response.result.analysis as unknown as { outcome: string }).outcome = "fatal"; } }], ["receipt-bound summary mutation", (response: ToolcraftModelWorkerResponse) => { if (response.kind === "result") { const statistics = response.result.analysis.statistics as unknown as { decodedBytes: number; }; statistics.decodedBytes = statistics.decodedBytes === 0 ? 1 : statistics.decodedBytes - 1; } }], ["wrong operation", (response: ToolcraftModelWorkerResponse) => { if (response.kind === "result") { (response.result as unknown as { operation: string }).operation = "repair"; } }], ] as const)( "rejects a current-generation result with %s before publication", async (_label, mutate) => { const worker = new FakeWorker(); const onResult = vi.fn(); const client = createToolcraftModelWorkerClient({ workerFactory: () => worker }); const pending = client.importModel(importRequest("hostile-result"), { onResult }); const request = worker.posts[0]!.message; emitDecodeLifecycle(worker, request); const response = structuredClone(resultFor(request)); mutate(response); worker.emitMessage(response); await expect(pending).resolves.toMatchObject({ kind: "error", feedback: { category: "resource-unavailable", code: "model-worker-invalid-message" }, }); expect(worker.terminate).toHaveBeenCalledTimes(1); expect(onResult).not.toHaveBeenCalled(); }, ); it.each([ ["diagnostic", { diagnostic: { affectedCount: 1, code: "hostile", explanation: "Malformed severity.", severity: "catastrophic", }, kind: "diagnostic", }], ["progress phase", { kind: "progress", phase: "uploading", progress: 0.2 }], ] as const)( "rejects a malformed current-generation %s without invoking observers", async (_label, body) => { const worker = new FakeWorker(); const observers = { onDiagnostic: vi.fn(), onProgress: vi.fn(), onResult: vi.fn() }; const client = createToolcraftModelWorkerClient({ workerFactory: () => worker }); const pending = client.importModel(importRequest("hostile-message"), observers); const request = worker.posts[0]!.message; worker.emitMessage({ ...body, generation: request.generation, jobId: request.jobId }); await expect(pending).resolves.toMatchObject({ kind: "error", feedback: { code: "model-worker-invalid-message" }, }); expect(worker.terminate).toHaveBeenCalledTimes(1); expect(observers.onDiagnostic).not.toHaveBeenCalled(); expect(observers.onProgress).not.toHaveBeenCalled(); expect(observers.onResult).not.toHaveBeenCalled(); }, ); it("terminates a worker that exceeds the protected diagnostic stream budget", async () => { const worker = new FakeWorker(); const onDiagnostic = vi.fn(); const onResult = vi.fn(); const client = createToolcraftModelWorkerClient({ workerFactory: () => worker }); const pending = client.importModel(importRequest("diagnostic-flood"), { onDiagnostic, onResult, }); const request = worker.posts[0]!.message; emitDecodeLifecycle(worker, request); for (let index = 0; index <= TOOLCRAFT_MODEL_WORKER_MAX_DIAGNOSTICS; index += 1) { worker.emitMessage({ diagnostic: { affectedCount: 1, code: "diagnostic-flood", explanation: `Diagnostic ${index}.`, severity: "info", }, generation: request.generation, jobId: request.jobId, kind: "diagnostic", }); } await expect(pending).resolves.toMatchObject({ feedback: { code: "model-worker-invalid-message" }, kind: "error", }); expect(onDiagnostic).toHaveBeenCalledTimes( TOOLCRAFT_MODEL_WORKER_MAX_DIAGNOSTICS, ); expect(onResult).not.toHaveBeenCalled(); expect(worker.terminate).toHaveBeenCalledTimes(1); }); it("rejects a structurally valid result before the required lifecycle", async () => { const worker = new FakeWorker(); const onResult = vi.fn(); const client = createToolcraftModelWorkerClient({ workerFactory: () => worker }); const pending = client.importModel(importRequest("early-result"), { onResult }); const request = worker.posts[0]!.message; worker.emitMessage(resultFor(request)); await expect(pending).resolves.toMatchObject({ kind: "error", feedback: { code: "model-worker-invalid-message" }, }); expect(worker.terminate).toHaveBeenCalledTimes(1); expect(onResult).not.toHaveBeenCalled(); }); });