1import { EnvironmentId, ProjectId, ThreadId, type ScopedThreadRef } from "@t3tools/contracts";
2import * as Cause from "effect/Cause";
3import { AsyncResult, Atom, AtomRegistry } from "effect/reactivity";
4import { expect, it, vi } from "vite-plus/test";
5
6const state = vi.hoisted(() => ({ reply: null as unknown }));
7vi.mock(import("./threads"), async () => {
8 const { Atom } = await import("effect/reactivity");
9 return {
10 environmentThreadDetails: {} as never,
11 environmentThreadShells: {
12 threadShellAtom: () => Atom.make(null),
13 threadRefsAtom: Atom.make([{ environmentId: "remote", threadId: "older-thread" }]),
14 } as never,
15 };
16});
17vi.mock(import("./orchestration"), () => ({
18 orchestrationEnvironment: { threadLinkMetadata: () => state.reply } as never,
19}));
20vi.mock(import("./atom-registry"), () => ({ appAtomRegistry: {} as never }));
21vi.mock(import("./projects"), () => ({ environmentProjects: {} as never }));
22vi.mock(import("./server"), () => ({
23 environmentServerConfigsAtom: {} as never,
24 serverEnvironment: {} as never,
25}));
26vi.mock(import("./assets"), () => ({ projectFaviconUrlAtom: (() => null) as never }));
27vi.mock(import("../native/ProjectChipIconImages"), () => ({
28 projectGlyphImageAtom: (() => null) as never,
29 projectGlyphKey: (() => "") as never,
30}));
31
32import { resolveThreadLink } from "./entities";
33
34function setup() {
35 const reply = Atom.make<
36 AsyncResult.AsyncResult<Record<string, { title: string; projectId: ProjectId | null }>, string>
37 >(AsyncResult.initial());
38 state.reply = reply;
39 return { registry: AtomRegistry.make(), reply };
40}
41
42it("uses confirmed matches instead of provisional titles when resolving legacy links", () => {
43 const { registry, reply } = setup();
44 using _registry = { [Symbol.dispose]: () => registry.dispose() };
45 const local: ScopedThreadRef = {
46 environmentId: EnvironmentId.make("local"),
47 threadId: ThreadId.make("older-thread"),
48 };
49 const remote: ScopedThreadRef = { ...local, environmentId: EnvironmentId.make("remote") };
50 const link = { environmentId: null, threadId: local.threadId };
51 const resolve = () => resolveThreadLink(registry.get.bind(registry), link, local.environmentId);
52 const release = registry.mount(reply);
53 try {
54 expect(resolve()).toEqual(remote);
55 registry.set(reply, AsyncResult.failure(Cause.fail("offline")));
56 expect(resolve()).toEqual(remote);
57 expect(resolveThreadLink(registry.get.bind(registry), local, remote.environmentId)).toEqual(
58 local,
59 );
60 registry.set(
61 reply,
62 AsyncResult.success({
63 [local.threadId]: { title: "Thread", projectId: ProjectId.make("project") },
64 }),
65 );
66 expect(resolve()).toEqual(local);
67 registry.set(reply, AsyncResult.success({}));
68 expect(resolve()).toEqual(remote);
69 } finally {
70 release();
71 }
72});