130 lines · 3.9 KB · TypeScript
RawDownload
import {  EnvironmentId,  ProjectId,  ThreadId,  type OrchestrationSearchThreadsResult,} from "@t3tools/contracts";import * as Cause from "effect/Cause";import { AsyncResult, Atom, AtomRegistry } from "effect/unstable/reactivity";import { expect, it } from "vite-plus/test"; import {  createThreadSearchResultsAtomFamily,  makeThreadSearchKey,  threadSearchMatchKey,} from "./threadSearch.ts"; const envA = EnvironmentId.make("env-a");const envB = EnvironmentId.make("env-b"); it("creates stable keys regardless of environment order", () => {  expect(makeThreadSearchKey([envB, envA], "needle")).toBe(    makeThreadSearchKey([envA, envB], "needle"),  );}); it("creates keys without array methods unavailable in Hermes", () => {  const descriptor = Object.getOwnPropertyDescriptor(Array.prototype, "toSorted");  Reflect.deleteProperty(Array.prototype, "toSorted");   try {    expect(makeThreadSearchKey([envB, envA], "needle")).toBe('[["env-a","env-b"],"needle"]');  } finally {    if (descriptor !== undefined) {      Reflect.defineProperty(Array.prototype, "toSorted", descriptor);    }  }}); it("encodes scoped thread keys without delimiter collisions", () => {  const first = threadSearchMatchKey({    environmentId: EnvironmentId.make("env\u0000thread"),    threadId: ThreadId.make("id"),  });  const second = threadSearchMatchKey({    environmentId: EnvironmentId.make("env"),    threadId: ThreadId.make("thread\u0000id"),  });   expect(first).not.toBe(second);}); it("accepts search keys at the maximum decoded query length", () => {  const queries: string[] = [];  const searchAtom = createThreadSearchResultsAtomFamily<Error>({    getSearchAtom: (_environmentId, query) => {      queries.push(query);      return Atom.make(AsyncResult.success({ matches: [] }));    },    labelPrefix: "test:thread-search",  });  const registry = AtomRegistry.make();  const query = "a".repeat(200);   try {    registry.get(searchAtom(makeThreadSearchKey([envA], query)));    registry.get(searchAtom(makeThreadSearchKey([envA], ` ${query} `)));     expect(queries).toEqual([query, query]);  } finally {    registry.dispose();  }}); it("ignores invalid search keys", () => {  let searchCount = 0;  const searchAtom = createThreadSearchResultsAtomFamily<Error>({    getSearchAtom: () => {      searchCount += 1;      return Atom.make(AsyncResult.success({ matches: [] }));    },    labelPrefix: "test:thread-search",  });  const registry = AtomRegistry.make();   try {    const state = registry.get(searchAtom(makeThreadSearchKey([envA], "a".repeat(201))));    const malformedState = registry.get(searchAtom("not json{{"));     expect(state).toEqual({ matches: [], isLoading: false });    expect(malformedState).toEqual({ matches: [], isLoading: false });    expect(searchCount).toBe(0);  } finally {    registry.dispose();  }}); it("merges successful environments and silently ignores failures", () => {  const result: OrchestrationSearchThreadsResult = {    matches: [      {        threadId: ThreadId.make("thread-a"),        projectId: ProjectId.make("project-a"),        source: "user",        snippet: "needle",        messageCreatedAt: "2026-07-30T00:00:00.000Z",      },    ],  };  const searchAtom = createThreadSearchResultsAtomFamily<Error>({    getSearchAtom: (environmentId) =>      environmentId === envA        ? Atom.make(AsyncResult.success(result))        : Atom.make(            AsyncResult.failure<OrchestrationSearchThreadsResult, Error>(              Cause.fail(new Error("unsupported rpc")),            ),          ),    labelPrefix: "test:thread-search",  });  const registry = AtomRegistry.make();   const state = registry.get(searchAtom(makeThreadSearchKey([envB, envA], "needle")));  expect(state).toEqual({    matches: [{ ...result.matches[0], environmentId: envA }],    isLoading: false,  });  expect(threadSearchMatchKey(state.matches[0]!)).toBe('["env-a","thread-a"]');   registry.dispose();});