import { EnvironmentId, ProjectId, ThreadId, type OrchestrationV2ShellSnapshot, } from "@t3tools/contracts"; import * as Option from "effect/Option"; import { Atom, AtomRegistry } from "effect/unstable/reactivity"; import { describe, expect, it } from "vite-plus/test"; import { PrimaryConnectionTarget } from "../connection/model.ts"; import { v2ShellSnapshot, v2ThreadShell } from "./orchestrationV2TestFixtures.ts"; import { createEnvironmentThreadShellAtoms } from "./threadShell.ts"; const environmentId = EnvironmentId.make("environment-v2"); const remoteEnvironmentId = EnvironmentId.make("remote-environment-v2"); const otherProjectId = ProjectId.make("other-project"); function makeHarness(environmentIds: ReadonlyArray = [environmentId]) { const snapshotAtom = Atom.family((_environmentId: EnvironmentId) => Atom.make(v2ShellSnapshot), ); const catalogValueAtom = Atom.make({ isReady: true, entries: new Map( environmentIds.map((id) => [ id, { target: new PrimaryConnectionTarget({ environmentId: id, label: "Environment", httpBaseUrl: "https://example.test", wsBaseUrl: "wss://example.test", }), profile: Option.none(), }, ]), ), }); return { registry: AtomRegistry.make(), snapshotAtom, catalogValueAtom, threads: createEnvironmentThreadShellAtoms({ catalogValueAtom, snapshotAtom }), }; } describe("v2 thread shell lists", () => { it("shares point and list values without retaining an atom for every listed thread", () => { const harness = makeHarness(); const snapshot = { ...v2ShellSnapshot, threads: Array.from({ length: 200 }, (_, index) => ({ ...v2ThreadShell, id: ThreadId.make(`thread-${index}`), })), }; harness.registry.set(harness.snapshotAtom(environmentId), snapshot); const listAtom = harness.threads.threadShellsAtom; const projectListAtom = harness.threads.threadShellsForProjectRefsAtom([ { environmentId, projectId: v2ThreadShell.projectId }, ]); const disposeList = harness.registry.mount(listAtom); const disposeProjectList = harness.registry.mount(projectListAtom); try { const before = harness.registry.get(listAtom); expect(before).toHaveLength(200); expect(harness.registry.get(projectListAtom)).toEqual(before); expect(harness.registry.getNodes().size).toBeLessThan(20); const firstAtom = harness.threads.threadShellAtom({ environmentId, threadId: snapshot.threads[0]!.id, }); expect(harness.registry.get(firstAtom)).toBe(before[0]); harness.registry.set(harness.snapshotAtom(environmentId), { ...snapshot, snapshotSequence: 1, threads: snapshot.threads.map((thread, index) => index === 199 ? { ...thread, title: "Updated last thread" } : thread, ), }); const after = harness.registry.get(listAtom); expect(after[0]).toBe(before[0]); expect(after.at(-1)).not.toBe(before.at(-1)); expect(after.at(-1)?.title).toBe("Updated last thread"); expect(harness.registry.get(projectListAtom).at(-1)).toBe(after.at(-1)); expect(harness.registry.get(firstAtom)).toBe(after[0]); expect(harness.registry.getNodes().size).toBeLessThan(20); } finally { disposeProjectList(); disposeList(); harness.registry.dispose(); } }); it("preserves project memberships on updates and keeps the same thread separate per environment", () => { const harness = makeHarness([environmentId, remoteEnvironmentId]); const otherThread = { ...v2ThreadShell, id: ThreadId.make("other-thread"), projectId: otherProjectId, }; const snapshot = { ...v2ShellSnapshot, threads: [v2ThreadShell, otherThread] }; harness.registry.set(harness.snapshotAtom(environmentId), snapshot); const membershipAtom = harness.threads.environmentThreadRefsByProjectAtom(environmentId); const listAtom = harness.threads.threadShellsForProjectRefsAtom([ { environmentId: remoteEnvironmentId, projectId: v2ThreadShell.projectId }, { environmentId, projectId: v2ThreadShell.projectId }, ]); const disposeList = harness.registry.mount(listAtom); try { const membership = harness.registry.get(membershipAtom); const before = harness.registry.get(listAtom); expect(before).toHaveLength(2); expect(before[0]).not.toBe(before[1]); expect(before.map((thread) => thread.environmentId)).toEqual([ remoteEnvironmentId, environmentId, ]); harness.registry.set(harness.snapshotAtom(environmentId), { ...snapshot, threads: [v2ThreadShell, { ...otherThread, title: "Changed elsewhere" }], }); expect(harness.registry.get(membershipAtom)).toBe(membership); expect(harness.registry.get(listAtom)).toBe(before); harness.registry.set(harness.snapshotAtom(environmentId), { ...snapshot, threads: [{ ...v2ThreadShell, projectId: otherProjectId }, otherThread], }); expect(harness.registry.get(listAtom)).toEqual([before[0]]); harness.registry.set(harness.snapshotAtom(remoteEnvironmentId), { ...v2ShellSnapshot, threads: [], }); expect(harness.registry.get(listAtom)).toEqual([]); } finally { disposeList(); harness.registry.dispose(); } }); });