import { afterEach, beforeEach, describe, expect, it } from "bun:test"; import { mkdir, mkdtemp, rm, stat, symlink, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { ResultReferenceResolver } from "../../src/results/resolver.js"; import { RESULT_REFERENCE_KIND, RESULT_REFERENCE_SOURCE, ResultReferenceStore, } from "../../src/results/store.js"; describe("ResultReferenceResolver", () => { let tempDir: string; let importRoot: string; let conversationWorkspace: string; let store: ResultReferenceStore; let resolver: ResultReferenceResolver; beforeEach(async () => { tempDir = await mkdtemp(join(tmpdir(), "tjwater-result-ref-")); importRoot = join(tempDir, "conversation-workspaces"); conversationWorkspace = join(importRoot, "conversation-1"); await mkdir(conversationWorkspace, { recursive: true }); store = new ResultReferenceStore(join(tempDir, "refs"), 60_000); resolver = new ResultReferenceResolver(store, importRoot, 1024 * 1024); await store.initialize(); }); afterEach(async () => { await rm(tempDir, { force: true, recursive: true }); }); it("stores metadata for render refs and resolves them", async () => { const record = await resolver.register({ actorKey: "actor-1", clientSessionId: "client-1", data: { node_area_map: { J1: "DMA-1", J2: "DMA-2", }, }, kind: RESULT_REFERENCE_KIND.renderJunctionsPayload, projectId: "project-1", projectKey: "project-key-1", schemaVersion: 1, sessionId: "session-1", source: RESULT_REFERENCE_SOURCE.agentGenerated, traceId: "trace-1", }); expect(record.kind).toBe(RESULT_REFERENCE_KIND.renderJunctionsPayload); expect(record.schemaVersion).toBe(1); expect(record.source).toBe(RESULT_REFERENCE_SOURCE.agentGenerated); const result = await resolver.getFullAuthorized( record.resultRef, { actorKey: "actor-1", projectId: "project-1", }, ); expect(result).not.toBeNull(); expect(result?.kind).toBe(RESULT_REFERENCE_KIND.renderJunctionsPayload); expect(result?.schema_version).toBe(1); expect(result?.source).toBe(RESULT_REFERENCE_SOURCE.agentGenerated); expect(result?.data).toEqual({ node_area_map: { J1: "DMA-1", J2: "DMA-2", }, }); }); it("rejects malformed refs and auth mismatches", async () => { const malformedRef = "res-bbbbbbbbbbbbbbbb"; await writeFile( join(tempDir, `${malformedRef}.json`), JSON.stringify( { resultRef: malformedRef, createdAt: "2026-05-21T00:00:00.000Z", data: { value: 1 }, preview: { count: 1, fields: ["value"], sample: { value: 1 }, summary: "object<1 fields>", }, projectId: "project-1", projectKey: "project-key-1", sessionId: "session-1", sizeBytes: 10, traceId: "trace-1", }, null, 2, ), "utf8", ); const malformed = await store.getAuthorizedRecord(malformedRef, { actorKey: "actor-1", projectId: "project-1", }); expect(malformed).toBeNull(); const renderRecord = await resolver.register({ actorKey: "actor-2", clientSessionId: "client-2", data: { node_area_map: { J1: "DMA-1", }, }, kind: RESULT_REFERENCE_KIND.renderJunctionsPayload, projectId: "project-2", projectKey: "project-key-2", schemaVersion: 1, sessionId: "session-2", source: RESULT_REFERENCE_SOURCE.agentGenerated, traceId: "trace-2", }); const wrongActor = await resolver.getFullAuthorized(renderRecord.resultRef, { actorKey: "actor-other", projectId: "project-2", }); expect(wrongActor).toBeNull(); }); it("registers render refs from local wrapper files and normalizes payloads", async () => { const filePath = join(conversationWorkspace, "render-wrapper.json"); await writeFile( filePath, JSON.stringify( { metadata: { createdAt: "2026-05-21T00:00:00.000Z", projectId: "project-3", }, location: { file_path: filePath, }, data: { node_area_map: { J1: "DMA-1", J2: 2, }, area_ids: ["DMA-1", " DMA-2 "], area_colors: { "DMA-1": "#ff0000", "DMA-2": "#00ff00", }, }, createdAt: "2026-05-21T00:00:00.000Z", }, null, 2, ), "utf8", ); const record = await resolver.registerRenderPayloadFile(filePath, { actorKey: "actor-3", clientSessionId: "client-3", projectId: "project-3", projectKey: "project-key-3", sessionId: "session-3", source: RESULT_REFERENCE_SOURCE.agentGenerated, traceId: "trace-3", workspaceDirectory: conversationWorkspace, }); expect(record.kind).toBe(RESULT_REFERENCE_KIND.renderJunctionsPayload); expect(record.source).toBe(RESULT_REFERENCE_SOURCE.agentGenerated); const result = await resolver.getFullAuthorized( record.resultRef, { actorKey: "actor-3", projectId: "project-3", }, { expectedKind: RESULT_REFERENCE_KIND.renderJunctionsPayload, }, ); expect(result?.data).toEqual({ node_area_map: { J1: "DMA-1", J2: "2", }, area_ids: ["DMA-1", "DMA-2"], area_colors: { "DMA-1": "#ff0000", "DMA-2": "#00ff00", }, }); await expect(stat(filePath)).rejects.toThrow(); }); it("rejects render payload files outside the configured import directory", async () => { const outsideDir = await mkdtemp(join(tmpdir(), "tjwater-result-outside-")); const filePath = join(outsideDir, "render-wrapper.json"); await writeFile( filePath, JSON.stringify({ metadata: {}, location: { file_path: filePath }, data: { node_area_map: { J1: "DMA-1" } }, }), "utf8", ); try { await expect( resolver.registerRenderPayloadFile(filePath, { actorKey: "actor-4", clientSessionId: "client-4", projectKey: "project-key-4", sessionId: "session-4", source: RESULT_REFERENCE_SOURCE.agentGenerated, traceId: "trace-4", workspaceDirectory: outsideDir, }), ).rejects.toThrow("RESULT_REF_IMPORT_DIR"); } finally { await rm(outsideDir, { force: true, recursive: true }); } }); it("rejects oversized render payload files before parsing", async () => { const filePath = join(conversationWorkspace, "oversized.json"); await writeFile(filePath, "x".repeat(128), "utf8"); const sizeLimitedResolver = new ResultReferenceResolver(store, importRoot, 64); await expect( sizeLimitedResolver.registerRenderPayloadFile(filePath, { actorKey: "actor-5", clientSessionId: "client-5", projectKey: "project-key-5", sessionId: "session-5", source: RESULT_REFERENCE_SOURCE.agentGenerated, traceId: "trace-5", workspaceDirectory: conversationWorkspace, }), ).rejects.toThrow("RESULT_REF_IMPORT_MAX_BYTES"); }); it("rejects render payload files owned by another conversation workspace", async () => { const otherWorkspace = join(importRoot, "conversation-2"); await mkdir(otherWorkspace); const filePath = join(otherWorkspace, "render-wrapper.json"); await writeFile( filePath, JSON.stringify({ metadata: {}, location: { file_path: filePath }, data: { node_area_map: { J1: "DMA-1" } }, }), "utf8", ); await expect( resolver.registerRenderPayloadFile(filePath, { actorKey: "actor-6", clientSessionId: "client-6", projectKey: "project-key-6", sessionId: "session-6", source: RESULT_REFERENCE_SOURCE.agentGenerated, traceId: "trace-6", workspaceDirectory: conversationWorkspace, }), ).rejects.toThrow("current conversation workspace"); }); it("rejects a conversation workspace that is itself a symbolic link", async () => { const targetWorkspace = join(importRoot, "conversation-target"); const linkedWorkspace = join(importRoot, "conversation-linked"); await mkdir(targetWorkspace); await symlink(targetWorkspace, linkedWorkspace, "dir"); const filePath = join(targetWorkspace, "render-wrapper.json"); await writeFile( filePath, JSON.stringify({ metadata: {}, location: { file_path: filePath }, data: { node_area_map: { J1: "DMA-1" } }, }), "utf8", ); await expect( resolver.registerRenderPayloadFile(filePath, { actorKey: "actor-7", clientSessionId: "client-7", projectKey: "project-key-7", sessionId: "session-7", source: RESULT_REFERENCE_SOURCE.agentGenerated, traceId: "trace-7", workspaceDirectory: linkedWorkspace, }), ).rejects.toThrow("symbolic link"); }); });