import { EnvironmentId, ProjectId } from "@t3tools/contracts";import { describe, expect, it } from "vite-plus/test"; import type { EnvironmentProject } from "@t3tools/client-runtime/state/shell";import type { HomeProjectScope } from "../home/homeThreadList";import { getProjectScopeSelectionTarget, resolveDraftProjectSelection, resolveEnvironmentProjectMatch,} from "./new-task-project-selection"; function makeProject( id: string, environmentId = "environment", options: { readonly title?: string; readonly workspaceRoot?: string; readonly repositoryKey?: string; } = {},): EnvironmentProject { return { environmentId: EnvironmentId.make(environmentId), id: ProjectId.make(id), title: options.title ?? id, workspaceRoot: options.workspaceRoot ?? `/work/${id}`, repositoryIdentity: options.repositoryKey ? { canonicalKey: options.repositoryKey, locator: { source: "git-remote", remoteName: "origin", remoteUrl: `https://${options.repositoryKey}.git`, }, } : null, defaultModelSelection: null, scripts: [], createdAt: "2026-07-01T00:00:00.000Z", updatedAt: "2026-07-01T00:00:00.000Z", };} function makeScope(projects: ReadonlyArray<EnvironmentProject>): HomeProjectScope { return { key: "github.com/t3tools/t3code", title: "T3 Code", representative: projects[0]!, projects, projectRefs: projects.map((project) => ({ environmentId: project.environmentId, projectId: project.id, })), };} describe("getProjectScopeSelectionTarget", () => { it("keeps the current environment when it hosts the selected logical project", () => { const projects = [makeProject("t3code-mac", "mac"), makeProject("t3code-server", "server")]; expect(getProjectScopeSelectionTarget(makeScope(projects), EnvironmentId.make("server"))).toBe( projects[1], ); }); it("falls back to the representative when the current environment does not host the project", () => { const projects = [makeProject("t3code-mac", "mac"), makeProject("t3code-server", "server")]; expect(getProjectScopeSelectionTarget(makeScope(projects), EnvironmentId.make("other"))).toBe( projects[0], ); });}); describe("resolveEnvironmentProjectMatch", () => { it("follows the same repository onto the target machine", () => { const selected = makeProject("t3code", "mac", { repositoryKey: "github.com/t3tools/t3code" }); const target = [ makeProject("other", "server", { repositoryKey: "github.com/t3tools/other" }), makeProject("t3code-clone", "server", { repositoryKey: "github.com/t3tools/t3code" }), ]; expect(resolveEnvironmentProjectMatch(target, selected)).toBe(target[1]); }); it("falls back to workspace basename, then title, for unindexed projects", () => { const selected = makeProject("t3code", "mac", { workspaceRoot: "/Users/me/t3code" }); const byBasename = [ makeProject("other", "server"), makeProject("srv", "server", { workspaceRoot: "/home/me/t3code" }), ]; expect(resolveEnvironmentProjectMatch(byBasename, selected)).toBe(byBasename[1]); const byTitle = [ makeProject("other", "server"), makeProject("srv", "server", { title: "t3code" }), ]; expect(resolveEnvironmentProjectMatch(byTitle, selected)).toBe(byTitle[1]); }); it("does not treat a known different repository as a basename or title match", () => { const selected = makeProject("t3code", "mac", { repositoryKey: "github.com/t3tools/t3code", workspaceRoot: "/Users/me/t3code", }); const fork = makeProject("fork", "server", { repositoryKey: "github.com/someone/t3code", title: "t3code", workspaceRoot: "/home/me/t3code", }); const unindexed = makeProject("unindexed", "server", { workspaceRoot: "/srv/t3code" }); expect(resolveEnvironmentProjectMatch([fork, unindexed], selected)).toBe(unindexed); // Without any weaker match the fork is still the first-project fallback. expect(resolveEnvironmentProjectMatch([fork], selected)).toBe(fork); }); it("falls back to the first project on the target so the draft has a key to carry over to", () => { const selected = makeProject("t3code", "mac", { repositoryKey: "github.com/t3tools/t3code" }); const target = [makeProject("unrelated", "server"), makeProject("also-unrelated", "server")]; expect(resolveEnvironmentProjectMatch(target, selected)).toBe(target[0]); expect(resolveEnvironmentProjectMatch([], selected)).toBeNull(); });}); describe("resolveDraftProjectSelection", () => { it("preserves an explicit project selection", () => { const project = makeProject("t3code"); expect( resolveDraftProjectSelection("environment:t3code", [project], [makeScope([project])]), ).toEqual({ kind: "preserve" }); }); it("selects the only physical project when no project was explicitly selected", () => { const project = makeProject("t3code"); expect(resolveDraftProjectSelection(null, [project], [makeScope([project])])).toEqual({ kind: "select", project, }); }); it("selects one logical project even when it has multiple physical workspaces", () => { const projects = [makeProject("t3code"), makeProject("t3code-2"), makeProject("t3code-3")]; expect(resolveDraftProjectSelection(null, projects, [makeScope(projects)])).toEqual({ kind: "select", project: projects[0], }); }); it("does not preserve a project key that is missing from the catalog", () => { const project = makeProject("t3code"); expect( resolveDraftProjectSelection("environment:removed", [project], [makeScope([project])]), ).toEqual({ kind: "select", project, }); });});
import { EnvironmentId, ProjectId } from "@t3tools/contracts";
import { describe, expect, it } from "vite-plus/test";
import type { EnvironmentProject } from "@t3tools/client-runtime/state/shell";
import type { HomeProjectScope } from "../home/homeThreadList";
import {
getProjectScopeSelectionTarget,
resolveDraftProjectSelection,
resolveEnvironmentProjectMatch,
} from "./new-task-project-selection";
function makeProject(
id: string,
environmentId = "environment",
options: {
readonly title?: string;
readonly workspaceRoot?: string;
readonly repositoryKey?: string;
} = {},
): EnvironmentProject {
return {
environmentId: EnvironmentId.make(environmentId),
id: ProjectId.make(id),
title: options.title ?? id,
workspaceRoot: options.workspaceRoot ?? `/work/${id}`,
repositoryIdentity: options.repositoryKey
? {
canonicalKey: options.repositoryKey,
locator: {
source: "git-remote",
remoteName: "origin",
remoteUrl: `https://${options.repositoryKey}.git`,
},
}
: null,
defaultModelSelection: null,
scripts: [],
createdAt: "2026-07-01T00:00:00.000Z",
updatedAt: "2026-07-01T00:00:00.000Z",
};
}
function makeScope(projects: ReadonlyArray<EnvironmentProject>): HomeProjectScope {
return {
key: "github.com/t3tools/t3code",
title: "T3 Code",
representative: projects[0]!,
projects,
projectRefs: projects.map((project) => ({
environmentId: project.environmentId,
projectId: project.id,
})),
};
}
describe("getProjectScopeSelectionTarget", () => {
it("keeps the current environment when it hosts the selected logical project", () => {
const projects = [makeProject("t3code-mac", "mac"), makeProject("t3code-server", "server")];
expect(getProjectScopeSelectionTarget(makeScope(projects), EnvironmentId.make("server"))).toBe(
projects[1],
);
});
it("falls back to the representative when the current environment does not host the project", () => {
const projects = [makeProject("t3code-mac", "mac"), makeProject("t3code-server", "server")];
expect(getProjectScopeSelectionTarget(makeScope(projects), EnvironmentId.make("other"))).toBe(
projects[0],
);
});
});
describe("resolveEnvironmentProjectMatch", () => {
it("follows the same repository onto the target machine", () => {
const selected = makeProject("t3code", "mac", { repositoryKey: "github.com/t3tools/t3code" });
const target = [
makeProject("other", "server", { repositoryKey: "github.com/t3tools/other" }),
makeProject("t3code-clone", "server", { repositoryKey: "github.com/t3tools/t3code" }),
];
expect(resolveEnvironmentProjectMatch(target, selected)).toBe(target[1]);
});
it("falls back to workspace basename, then title, for unindexed projects", () => {
const selected = makeProject("t3code", "mac", { workspaceRoot: "/Users/me/t3code" });
const byBasename = [
makeProject("other", "server"),
makeProject("srv", "server", { workspaceRoot: "/home/me/t3code" }),
];
expect(resolveEnvironmentProjectMatch(byBasename, selected)).toBe(byBasename[1]);
const byTitle = [
makeProject("other", "server"),
makeProject("srv", "server", { title: "t3code" }),
];
expect(resolveEnvironmentProjectMatch(byTitle, selected)).toBe(byTitle[1]);
});
it("does not treat a known different repository as a basename or title match", () => {
const selected = makeProject("t3code", "mac", {
repositoryKey: "github.com/t3tools/t3code",
workspaceRoot: "/Users/me/t3code",
});
const fork = makeProject("fork", "server", {
repositoryKey: "github.com/someone/t3code",
title: "t3code",
workspaceRoot: "/home/me/t3code",
});
const unindexed = makeProject("unindexed", "server", { workspaceRoot: "/srv/t3code" });
expect(resolveEnvironmentProjectMatch([fork, unindexed], selected)).toBe(unindexed);
// Without any weaker match the fork is still the first-project fallback.
expect(resolveEnvironmentProjectMatch([fork], selected)).toBe(fork);
});
it("falls back to the first project on the target so the draft has a key to carry over to", () => {
const selected = makeProject("t3code", "mac", { repositoryKey: "github.com/t3tools/t3code" });
const target = [makeProject("unrelated", "server"), makeProject("also-unrelated", "server")];
expect(resolveEnvironmentProjectMatch(target, selected)).toBe(target[0]);
expect(resolveEnvironmentProjectMatch([], selected)).toBeNull();
});
});
describe("resolveDraftProjectSelection", () => {
it("preserves an explicit project selection", () => {
const project = makeProject("t3code");
expect(
resolveDraftProjectSelection("environment:t3code", [project], [makeScope([project])]),
).toEqual({ kind: "preserve" });
});
it("selects the only physical project when no project was explicitly selected", () => {
const project = makeProject("t3code");
expect(resolveDraftProjectSelection(null, [project], [makeScope([project])])).toEqual({
kind: "select",
project,
});
});
it("selects one logical project even when it has multiple physical workspaces", () => {
const projects = [makeProject("t3code"), makeProject("t3code-2"), makeProject("t3code-3")];
expect(resolveDraftProjectSelection(null, projects, [makeScope(projects)])).toEqual({
kind: "select",
project: projects[0],
});
});
it("does not preserve a project key that is missing from the catalog", () => {
const project = makeProject("t3code");
expect(
resolveDraftProjectSelection("environment:removed", [project], [makeScope([project])]),
).toEqual({
kind: "select",
project,
});
});
});