784 lines · 24.3 KB · TypeScript
RawDownload
import type {  EnvironmentProject,  EnvironmentThreadShell,} from "@t3tools/client-runtime/state/shell";import { threadSearchMatchKey } from "@t3tools/client-runtime/state/thread-search";import { EnvironmentId, ProjectId, ProviderInstanceId, ThreadId } from "@t3tools/contracts";import { describe, expect, it } from "vite-plus/test"; import {  buildHomeProjectScopes,  buildHomeThreadGroups,  sortHomeProjectScopes,} from "./homeThreadList"; function makeProject(  input: Partial<EnvironmentProject> & Pick<EnvironmentProject, "environmentId" | "id" | "title">,): EnvironmentProject {  return {    workspaceRoot: `/workspaces/${input.id}`,    repositoryIdentity: null,    defaultModelSelection: null,    scripts: [],    createdAt: "2026-06-01T00:00:00.000Z",    updatedAt: "2026-06-01T00:00:00.000Z",    ...input,  };} function makeThread(  input: Partial<EnvironmentThreadShell> &    Pick<EnvironmentThreadShell, "environmentId" | "id" | "projectId" | "title">,): EnvironmentThreadShell {  return {    modelSelection: { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5.4" },    runtimeMode: "full-access",    interactionMode: "default",    branch: null,    worktreePath: null,    pullRequests: [],    latestTurn: null,    createdAt: "2026-06-01T00:00:00.000Z",    updatedAt: "2026-06-01T00:00:00.000Z",    archivedAt: null,    session: null,    latestUserMessageAt: null,    hasPendingApprovals: false,    hasPendingUserInput: false,    hasActionableProposedPlan: false,    ...input,    settledOverride: input.settledOverride ?? null,    settledAt: input.settledAt ?? null,  };} const NOW = Date.parse("2026-06-29T00:00:00.000Z"); function buildGroups(  projects: ReadonlyArray<EnvironmentProject>,  threads: ReadonlyArray<EnvironmentThreadShell>,  overrides: Partial<Parameters<typeof buildHomeThreadGroups>[0]> = {},) {  return buildHomeThreadGroups({    projects,    threads,    environmentId: null,    searchQuery: "",    projectSortOrder: "updated_at",    threadSortOrder: "updated_at",    projectGroupingMode: "repository",    now: NOW,    ...overrides,  });} describe("buildHomeThreadGroups", () => {  it("builds one v2 scope for the same repository across environments", () => {    const localEnvironmentId = EnvironmentId.make("environment-local");    const remoteEnvironmentId = EnvironmentId.make("environment-remote");    const repositoryIdentity = {      canonicalKey: "github.com/pingdotgg/t3code",      locator: {        source: "git-remote" as const,        remoteName: "origin",        remoteUrl: "git@github.com:pingdotgg/t3code.git",      },    };    const projects = [      makeProject({        environmentId: localEnvironmentId,        id: ProjectId.make("project-local"),        title: "t3code",        repositoryIdentity,      }),      makeProject({        environmentId: remoteEnvironmentId,        id: ProjectId.make("project-remote"),        title: "t3code",        repositoryIdentity,      }),    ];     const scopes = buildHomeProjectScopes({      projects,      environmentId: null,      projectGroupingMode: "repository",    });     expect(scopes).toHaveLength(1);    expect(scopes[0]?.title).toBe("t3code");    expect(scopes[0]?.projects).toEqual(projects);    expect(scopes[0]?.projectRefs).toEqual(      projects.map((project) => ({        environmentId: project.environmentId,        projectId: project.id,      })),    );  });   it("routes stale duplicate project refs through the canonical repository group", () => {    const localEnvironmentId = EnvironmentId.make("environment-local");    const remoteEnvironmentId = EnvironmentId.make("environment-remote");    const repositoryIdentity = {      canonicalKey: "github.com/pingdotgg/t3code",      locator: {        source: "git-remote" as const,        remoteName: "origin",        remoteUrl: "git@github.com:pingdotgg/t3code.git",      },    };    const local = makeProject({      id: ProjectId.make("project-local"),      environmentId: localEnvironmentId,      title: "t3code",      workspaceRoot: "/workspaces/t3code",      repositoryIdentity,    });    const stale = makeProject({      environmentId: remoteEnvironmentId,      id: ProjectId.make("project-stale"),      title: "t3code",      workspaceRoot: "/remote/t3code",      updatedAt: "2026-06-01T00:00:00.000Z",    });    const canonicalRemote = makeProject({      environmentId: remoteEnvironmentId,      id: ProjectId.make("project-canonical-remote"),      title: "t3code",      workspaceRoot: "/remote/t3code/",      repositoryIdentity,      updatedAt: "2026-06-02T00:00:00.000Z",    });    const projects = [local, stale, canonicalRemote];    const staleThread = makeThread({      environmentId: remoteEnvironmentId,      id: ThreadId.make("thread-stale-project-ref"),      projectId: stale.id,      title: "Still visible",      updatedAt: "2026-06-03T00:00:00.000Z",    });     const scopes = buildHomeProjectScopes({      projects,      environmentId: null,      projectGroupingMode: "repository",    });    const groups = buildGroups(projects, [staleThread]);     expect(scopes).toHaveLength(1);    expect(scopes[0]?.projects.map((project) => project.id)).toEqual([      local.id,      canonicalRemote.id,    ]);    expect(scopes[0]?.projectRefs.map((projectRef) => projectRef.projectId)).toEqual([      local.id,      stale.id,      canonicalRemote.id,    ]);    expect(groups).toHaveLength(1);    expect(groups[0]?.threads.map((thread) => thread.id)).toEqual([staleThread.id]);    expect(groups[0]?.newThreadTarget?.id).toBe(canonicalRemote.id);  });   it("keeps repository identity from an older duplicate when the freshness winner lacks it", () => {    const localEnvironmentId = EnvironmentId.make("environment-local");    const remoteEnvironmentId = EnvironmentId.make("environment-remote");    const repositoryIdentity = {      canonicalKey: "github.com/pingdotgg/t3code",      locator: {        source: "git-remote" as const,        remoteName: "origin",        remoteUrl: "git@github.com:pingdotgg/t3code.git",      },    };    const projects = [      makeProject({        environmentId: localEnvironmentId,        id: ProjectId.make("project-local"),        title: "t3code",        repositoryIdentity,      }),      makeProject({        environmentId: remoteEnvironmentId,        id: ProjectId.make("project-remote-with-identity"),        title: "t3code",        workspaceRoot: "/remote/t3code",        repositoryIdentity,        updatedAt: "2026-06-01T00:00:00.000Z",      }),      makeProject({        environmentId: remoteEnvironmentId,        id: ProjectId.make("project-remote-fresh"),        title: "t3code",        workspaceRoot: "/remote/t3code/",        updatedAt: "2026-06-02T00:00:00.000Z",      }),    ];     const scopes = buildHomeProjectScopes({      projects,      environmentId: null,      projectGroupingMode: "repository",    });     expect(scopes).toHaveLength(1);    expect(scopes[0]?.representative.id).toBe(ProjectId.make("project-local"));    expect(scopes[0]?.projects.map((project) => project.id)).toContain(      ProjectId.make("project-remote-fresh"),    );    expect(scopes[0]?.projectRefs).toHaveLength(3);  });   it("sorts v2 project scopes by their grouped thread activity", () => {    const environmentId = EnvironmentId.make("environment-1");    const olderProject = makeProject({      environmentId,      id: ProjectId.make("project-older"),      title: "Older project",    });    const newerProject = makeProject({      environmentId,      id: ProjectId.make("project-newer"),      title: "Newer project",    });    const scopes = buildHomeProjectScopes({      projects: [newerProject, olderProject],      environmentId: null,      projectGroupingMode: "separate",    });     expect(      sortHomeProjectScopes({        scopes,        threads: [          makeThread({            environmentId,            id: ThreadId.make("thread-older-project"),            projectId: olderProject.id,            title: "Most recently active",            updatedAt: "2026-06-03T00:00:00.000Z",          }),          makeThread({            environmentId,            id: ThreadId.make("thread-newer-project"),            projectId: newerProject.id,            title: "Less recently active",            updatedAt: "2026-06-02T00:00:00.000Z",          }),        ],        pendingTasks: [],        projectSortOrder: "updated_at",      }).map((scope) => scope.representative.id),    ).toEqual([olderProject.id, newerProject.id]);  });   it("sorts invalid project creation timestamps after valid ones", () => {    const environmentId = EnvironmentId.make("environment-1");    const invalidProject = makeProject({      environmentId,      id: ProjectId.make("project-invalid"),      title: "A invalid timestamp",      createdAt: "invalid",    });    const validProject = makeProject({      environmentId,      id: ProjectId.make("project-valid"),      title: "Z valid timestamp",      createdAt: "2026-06-02T00:00:00.000Z",    });    const scopes = buildHomeProjectScopes({      projects: [invalidProject, validProject],      environmentId: null,      projectGroupingMode: "separate",    });     expect(      sortHomeProjectScopes({        scopes,        threads: [],        pendingTasks: [],        projectSortOrder: "created_at",      }).map((scope) => scope.representative.id),    ).toEqual([validProject.id, invalidProject.id]);  });   it("uses the freshest member when a grouped scope has no activity", () => {    const localEnvironmentId = EnvironmentId.make("environment-local");    const remoteEnvironmentId = EnvironmentId.make("environment-remote");    const repositoryIdentity = {      canonicalKey: "github.com/pingdotgg/t3code",      locator: {        source: "git-remote" as const,        remoteName: "origin",        remoteUrl: "git@github.com:pingdotgg/t3code.git",      },    };    const olderMember = makeProject({      environmentId: localEnvironmentId,      id: ProjectId.make("project-older-member"),      title: "t3code",      updatedAt: "2026-06-01T00:00:00.000Z",      repositoryIdentity,    });    const newerMember = makeProject({      environmentId: remoteEnvironmentId,      id: ProjectId.make("project-newer-member"),      title: "t3code",      updatedAt: "2026-06-03T00:00:00.000Z",      repositoryIdentity,    });    const otherProject = makeProject({      environmentId: localEnvironmentId,      id: ProjectId.make("project-other"),      title: "other",      updatedAt: "2026-06-02T00:00:00.000Z",    });    const scopes = buildHomeProjectScopes({      projects: [olderMember, newerMember, otherProject],      environmentId: null,      projectGroupingMode: "repository",    });     expect(      sortHomeProjectScopes({        scopes,        threads: [],        pendingTasks: [],        projectSortOrder: "updated_at",      })[0]?.key,    ).toBe(scopes.find((scope) => scope.projects.length === 2)?.key);  });   it("does not merge unrelated repositories that share a title", () => {    const environmentId = EnvironmentId.make("environment-1");    const projects = ["one", "two"].map((name) =>      makeProject({        environmentId,        id: ProjectId.make(`project-${name}`),        title: "app",        repositoryIdentity: {          canonicalKey: `github.com/example/${name}`,          locator: {            source: "git-remote" as const,            remoteName: "origin",            remoteUrl: `git@github.com:example/${name}.git`,          },        },      }),    );     expect(      buildHomeProjectScopes({        projects,        environmentId: null,        projectGroupingMode: "repository",      }),    ).toHaveLength(2);  });   it("uses the physical project title for a singleton scope", () => {    const project = makeProject({      environmentId: EnvironmentId.make("environment-1"),      id: ProjectId.make("project-1"),      title: "local-worktree-name",      repositoryIdentity: {        canonicalKey: "github.com/pingdotgg/t3code",        displayName: "codething-mvp",        locator: {          source: "git-remote" as const,          remoteName: "origin",          remoteUrl: "git@github.com:pingdotgg/t3code.git",        },      },    });     const scopes = buildHomeProjectScopes({      projects: [project],      environmentId: null,      projectGroupingMode: "repository",    });    const groups = buildGroups(      [project],      [        makeThread({          environmentId: project.environmentId,          id: ThreadId.make("thread-1"),          projectId: project.id,          title: "Thread",        }),      ],    );     expect(scopes[0]?.title).toBe("local-worktree-name");    expect(groups[0]?.title).toBe("local-worktree-name");  });   it("sorts the newest thread first regardless of snapshot order", () => {    const environmentId = EnvironmentId.make("environment-1");    const project = makeProject({      environmentId,      id: ProjectId.make("project-1"),      title: "T3 Code",    });    const threads = [      makeThread({        environmentId,        id: ThreadId.make("thread-old"),        projectId: project.id,        title: "Older thread",        updatedAt: "2026-06-02T00:00:00.000Z",      }),      makeThread({        environmentId,        id: ThreadId.make("thread-new"),        projectId: project.id,        title: "Newer thread",        updatedAt: "2026-06-03T00:00:00.000Z",      }),    ];     expect(buildGroups([project], threads)[0]?.threads.map((thread) => thread.id)).toEqual([      "thread-new",      "thread-old",    ]);  });   it("supports independent project and thread creation-time sorting", () => {    const environmentId = EnvironmentId.make("environment-1");    const olderProject = makeProject({      environmentId,      id: ProjectId.make("project-older"),      title: "Older project",    });    const newerProject = makeProject({      environmentId,      id: ProjectId.make("project-newer"),      title: "Newer project",    });    const threads = [      makeThread({        environmentId,        id: ThreadId.make("old-created"),        projectId: olderProject.id,        title: "Updated recently",        createdAt: "2026-06-01T00:00:00.000Z",        updatedAt: "2026-06-05T00:00:00.000Z",      }),      makeThread({        environmentId,        id: ThreadId.make("new-created"),        projectId: olderProject.id,        title: "Created recently",        createdAt: "2026-06-04T00:00:00.000Z",        updatedAt: "2026-06-04T00:00:00.000Z",      }),      makeThread({        environmentId,        id: ThreadId.make("newest-project-thread"),        projectId: newerProject.id,        title: "Newest project",        createdAt: "2026-06-06T00:00:00.000Z",      }),    ];     const groups = buildGroups([olderProject, newerProject], threads, {      projectSortOrder: "created_at",      threadSortOrder: "created_at",      projectGroupingMode: "separate",    });     expect(groups.map((group) => group.representative.id)).toEqual([      "project-newer",      "project-older",    ]);    expect(groups[1]?.threads.map((thread) => thread.id)).toEqual(["new-created", "old-created"]);  });   it("filters both projects and threads to one environment", () => {    const localEnvironmentId = EnvironmentId.make("environment-local");    const remoteEnvironmentId = EnvironmentId.make("environment-remote");    const projects = [

Lines 1–500 of 784.