Open History to see the last commit
1import { EnvironmentId, ProjectId } from "@t3tools/contracts";
2import { describe, expect, it } from "vite-plus/test";
3
4import type {
5 SidebarProjectGroupMember,
6 SidebarProjectSnapshot,
7} from "../../sidebarProjectGrouping";
8import { resolveSettingsScope, validateSettingsScopeSearch } from "./settingsScope";
9
10const laptopId = EnvironmentId.make("laptop");
11const serverId = EnvironmentId.make("server");
12const environments = [
13 { environmentId: laptopId, label: "Laptop" },
14 { environmentId: serverId, label: "Server" },
15];
16
17function member(id: string, environmentId: EnvironmentId): SidebarProjectGroupMember {
18 return {
19 id: ProjectId.make(id),
20 environmentId,
21 title: "T3 Code",
22 workspaceRoot: `/repos/${id}`,
23 physicalProjectKey: `${environmentId}:/repos/${id}`,
24 environmentLabel:
25 environments.find((environment) => environment.environmentId === environmentId)?.label ??
26 null,
27 defaultModelSelection: null,
28 scripts: [],
29 createdAt: "2026-09-07T00:00:00.000Z",
30 updatedAt: "2026-09-07T00:00:00.000Z",
31 };
32}
33
34const first = member("first", laptopId);
35const second = member("second", laptopId);
36const third = member("third", serverId);
37const other = member("other", serverId);
38
39function group(
40 projectKey: string,
41 members: readonly SidebarProjectGroupMember[],
42): SidebarProjectSnapshot {
43 return {
44 ...members[0]!,
45 projectKey,
46 displayName: projectKey,
47 memberProjects: members,
48 memberProjectRefs: members.map((project) => ({
49 environmentId: project.environmentId,
50 projectId: project.id,
51 })),
52 groupedProjectCount: members.length,
53 environmentPresence: "mixed",
54 allRemoteMembersAreDesktopLocal: false,
55 allRemoteMembersAreWsl: false,
56 remoteEnvironmentLabels: [],
57 pinned: false,
58 archived: false,
59 };
60}
61
62const groups = [group("t3code", [first, second, third]), group("other", [other])];
63
64describe("settings scope search", () => {
65 it("retains an orphan checkout so it cannot turn into all environments", () => {
66 const search = validateSettingsScopeSearch({ checkout: first.physicalProjectKey });
67 expect(search).toEqual({ checkout: first.physicalProjectKey });
68 expect(resolveSettingsScope(search, groups, environments)).toMatchObject({
69 kind: "unavailable",
70 reason: "project-required",
71 members: [],
72 environmentIds: [],
73 });
74 });
75});
76
77describe("settings scope resolution", () => {
78 it("keeps all physical members in a project aggregate, including several on one environment", () => {
79 expect(resolveSettingsScope({ project: "t3code" }, groups, environments)).toMatchObject({
80 kind: "project",
81 environmentId: null,
82 members: [first, second, third],
83 environmentIds: [laptopId, serverId],
84 });
85 });
86
87 it("narrows a checkout target to exactly one member, deriving its environment when omitted", () => {
88 expect(
89 resolveSettingsScope(
90 { project: "t3code", checkout: second.physicalProjectKey },
91 groups,
92 environments,
93 ),
94 ).toMatchObject({
95 kind: "checkout",
96 checkout: second,
97 environmentId: laptopId,
98 label: "t3code / Laptop · /repos/second",
99 members: [second],
100 environmentIds: [laptopId],
101 });
102 });
103
104 it.each([
105 { project: "missing" },
106 { machine: "removed" },
107 { project: "t3code", machine: "removed" },
108 { project: "t3code", checkout: "deleted" },
109 { project: "other", machine: laptopId },
110 { project: "other", checkout: first.physicalProjectKey },
111 { project: "t3code", machine: serverId, checkout: first.physicalProjectKey },
112 ])("never widens an invalid or stale target: %j", (search) => {
113 expect(resolveSettingsScope(search, groups, environments)).toMatchObject({
114 kind: "unavailable",
115 members: [],
116 environmentIds: [],
117 });
118 });
119
120 it("leaves a removed checkout unavailable while sibling checkouts remain", () => {
121 const search = {
122 project: "t3code",
123 machine: laptopId,
124 checkout: first.physicalProjectKey,
125 };
126 expect(resolveSettingsScope(search, groups, environments)).toMatchObject({
127 kind: "checkout",
128 members: [first],
129 });
130 expect(
131 resolveSettingsScope(search, [group("t3code", [second, third])], environments),
132 ).toMatchObject({ kind: "unavailable", members: [], environmentIds: [] });
133 });
134
135 it("does not select another environment after removing a project's last local checkout", () => {
136 const search = { project: "t3code", machine: laptopId };
137 expect(resolveSettingsScope(search, groups, environments)).toMatchObject({
138 kind: "project",
139 members: [first, second],
140 });
141 expect(resolveSettingsScope(search, [group("t3code", [third])], environments)).toMatchObject({
142 kind: "unavailable",
143 members: [],
144 environmentIds: [],
145 });
146 });
147
148 it("rejects a cached checkout whose environment was removed", () => {
149 expect(
150 resolveSettingsScope(
151 { project: "t3code", checkout: third.physicalProjectKey },
152 groups,
153 environments.slice(0, 1),
154 ),
155 ).toMatchObject({
156 kind: "unavailable",
157 reason: "environment-missing",
158 members: [],
159 environmentIds: [],
160 });
161 });
162});