243 lines · 6.7 KB · TypeScript
RawDownload
import { describe, expect, it } from "vite-plus/test";import {  DEFAULT_RUNTIME_MODE,  EnvironmentId,  ProjectId,  ProviderInstanceId,  ThreadId,} from "@t3tools/contracts";import type { Thread } from "../types";import { getLatestThreadForProject, sortThreads } from "./threadSort"; const LOCAL_ENVIRONMENT_ID = EnvironmentId.make("environment-local");const PROJECT_ID = ProjectId.make("project-1"); function makeThread(overrides: Partial<Thread> = {}): Thread {  return {    id: ThreadId.make("thread-1"),    environmentId: LOCAL_ENVIRONMENT_ID,    projectId: PROJECT_ID,    title: "Thread",    modelSelection: { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5.4" },    runtimeMode: DEFAULT_RUNTIME_MODE,    interactionMode: "default",    session: null,    messages: [],    proposedPlans: [],    createdAt: "2026-03-09T10:00:00.000Z",    archivedAt: null,    deletedAt: null,    updatedAt: "2026-03-09T10:00:00.000Z",    latestTurn: null,    branch: null,    worktreePath: null,    checkpoints: [],    activities: [],    ...overrides,  };} describe("sortThreads", () => {  it("sorts threads by the latest user message in recency mode", () => {    const sorted = sortThreads(      [        makeThread({          id: ThreadId.make("thread-1"),          updatedAt: "2026-03-09T10:10:00.000Z",          messages: [            {              id: "message-1" as never,              role: "user",              text: "older",              turnId: null,              createdAt: "2026-03-09T10:01:00.000Z",              updatedAt: "2026-03-09T10:01:00.000Z",              streaming: false,            },          ],        }),        makeThread({          id: ThreadId.make("thread-2"),          createdAt: "2026-03-09T10:05:00.000Z",          updatedAt: "2026-03-09T10:05:00.000Z",          messages: [            {              id: "message-2" as never,              role: "user",              text: "newer",              turnId: null,              createdAt: "2026-03-09T10:06:00.000Z",              updatedAt: "2026-03-09T10:06:00.000Z",              streaming: false,            },          ],        }),      ],      "updated_at",    );     expect(sorted.map((thread) => thread.id)).toEqual([      ThreadId.make("thread-2"),      ThreadId.make("thread-1"),    ]);  });   it("falls back to thread timestamps when there is no user message", () => {    const sorted = sortThreads(      [        makeThread({          id: ThreadId.make("thread-1"),          updatedAt: "2026-03-09T10:01:00.000Z",          messages: [            {              id: "message-1" as never,              role: "assistant",              text: "assistant only",              turnId: null,              createdAt: "2026-03-09T10:02:00.000Z",              updatedAt: "2026-03-09T10:02:00.000Z",              streaming: false,            },          ],        }),        makeThread({          id: ThreadId.make("thread-2"),          createdAt: "2026-03-09T10:05:00.000Z",          updatedAt: "2026-03-09T10:05:00.000Z",          messages: [],        }),      ],      "updated_at",    );     expect(sorted.map((thread) => thread.id)).toEqual([      ThreadId.make("thread-2"),      ThreadId.make("thread-1"),    ]);  });   it("falls back to createdAt when updatedAt is invalid", () => {    const sorted = sortThreads(      [        makeThread({          id: ThreadId.make("thread-1"),          createdAt: "2026-03-09T10:00:00.000Z",          updatedAt: "invalid-date" as never,          messages: [],        }),        makeThread({          id: ThreadId.make("thread-2"),          createdAt: "2026-03-09T09:00:00.000Z",          updatedAt: "2026-03-09T09:30:00.000Z",          messages: [],        }),      ],      "updated_at",    );     expect(sorted.map((thread) => thread.id)).toEqual([      ThreadId.make("thread-1"),      ThreadId.make("thread-2"),    ]);  });   it("falls back to id ordering when threads have no sortable timestamps", () => {    const sorted = sortThreads(      [        makeThread({          id: ThreadId.make("thread-1"),          createdAt: "invalid-created-at" as never,          updatedAt: "invalid-updated-at" as never,          messages: [],        }),        makeThread({          id: ThreadId.make("thread-2"),          createdAt: "invalid-created-at" as never,          updatedAt: "invalid-updated-at" as never,          messages: [],        }),      ],      "updated_at",    );     expect(sorted.map((thread) => thread.id)).toEqual([      ThreadId.make("thread-2"),      ThreadId.make("thread-1"),    ]);  });   it("can sort threads by createdAt when configured", () => {    const sorted = sortThreads(      [        makeThread({          id: ThreadId.make("thread-1"),          createdAt: "2026-03-09T10:05:00.000Z",          updatedAt: "2026-03-09T10:05:00.000Z",        }),        makeThread({          id: ThreadId.make("thread-2"),          createdAt: "2026-03-09T10:00:00.000Z",          updatedAt: "2026-03-09T10:10:00.000Z",        }),      ],      "created_at",    );     expect(sorted.map((thread) => thread.id)).toEqual([      ThreadId.make("thread-1"),      ThreadId.make("thread-2"),    ]);  });   it("uses updatedAt as a fallback for created_at sorting when createdAt is invalid", () => {    const sorted = sortThreads(      [        makeThread({          id: ThreadId.make("thread-1"),          createdAt: "invalid-date" as never,          updatedAt: "2026-03-09T10:05:00.000Z",        }),        makeThread({          id: ThreadId.make("thread-2"),          createdAt: "2026-03-09T10:00:00.000Z",          updatedAt: "2026-03-09T10:10:00.000Z",        }),      ],      "created_at",    );     expect(sorted.map((thread) => thread.id)).toEqual([      ThreadId.make("thread-1"),      ThreadId.make("thread-2"),    ]);  });   it("returns the latest active thread for a project", () => {    const latestThread = getLatestThreadForProject(      [        makeThread({          id: ThreadId.make("thread-1"),          createdAt: "2026-03-09T10:00:00.000Z",          updatedAt: "2026-03-09T10:01:00.000Z",          archivedAt: null,        }),        makeThread({          id: ThreadId.make("thread-2"),          createdAt: "2026-03-09T10:05:00.000Z",          updatedAt: "2026-03-09T10:10:00.000Z",          archivedAt: "2026-03-10T00:00:00.000Z",        }),        makeThread({          id: ThreadId.make("thread-3"),          createdAt: "2026-03-09T10:06:00.000Z",          updatedAt: "2026-03-09T10:06:00.000Z",          archivedAt: null,        }),      ],      PROJECT_ID,      "updated_at",    );     expect(latestThread?.id).toBe(ThreadId.make("thread-3"));  });});