1import {
2 DEFAULT_SERVER_SETTINGS,
3 ProviderDriverKind,
4 ProviderInstanceId,
5 type ServerProvider,
6} from "@t3tools/contracts";
7import { describe, expect, it } from "vite-plus/test";
8
9import {
10 getOnboardingProviderState,
11 resolveOnboardingProviderInstallCommand,
12 resolveOnboardingProviderLoginCommand,
13 selectOnboardingProvidersByDriver,
14} from "./providerReadiness.logic";
15
16const readyCodex: ServerProvider = {
17 instanceId: ProviderInstanceId.make("codex"),
18 driver: ProviderDriverKind.make("codex"),
19 enabled: true,
20 installed: true,
21 version: "1.0.0",
22 status: "ready",
23 auth: { status: "unknown" },
24 checkedAt: "2026-08-23T00:00:00.000Z",
25 models: [],
26 slashCommands: [],
27 skills: [],
28};
29
30describe("getOnboardingProviderState", () => {
31 it("treats an enabled Codex provider with ready status and unknown authentication as ready", () => {
32 expect(getOnboardingProviderState(readyCodex)).toBe("ready");
33 });
34
35 it("treats authenticated providers as ready only when their provider status is ready", () => {
36 expect(getOnboardingProviderState({ ...readyCodex, auth: { status: "authenticated" } })).toBe(
37 "ready",
38 );
39 expect(
40 getOnboardingProviderState({
41 ...readyCodex,
42 auth: { status: "authenticated" },
43 status: "error",
44 }),
45 ).toBe("attention");
46 expect(
47 getOnboardingProviderState({
48 ...readyCodex,
49 auth: { status: "authenticated" },
50 status: "warning",
51 }),
52 ).toBe("attention");
53 });
54
55 it("offers sign-in only when the server reports an authentication failure", () => {
56 expect(
57 getOnboardingProviderState({
58 ...readyCodex,
59 status: "error",
60 auth: { status: "unauthenticated" },
61 }),
62 ).toBe("signIn");
63 expect(getOnboardingProviderState({ ...readyCodex, status: "error" })).toBe("attention");
64 expect(getOnboardingProviderState({ ...readyCodex, status: "warning" })).toBe("attention");
65 });
66
67 it("does not offer installation or sign-in for disabled providers", () => {
68 expect(getOnboardingProviderState({ ...readyCodex, enabled: false, installed: false })).toBe(
69 "disabled",
70 );
71 expect(getOnboardingProviderState({ ...readyCodex, status: "disabled" })).toBe("disabled");
72 });
73
74 it("offers installation only when an enabled provider is missing", () => {
75 expect(getOnboardingProviderState({ ...readyCodex, installed: false, status: "error" })).toBe(
76 "install",
77 );
78 });
79
80 it("waits for a provider snapshot before offering an action", () => {
81 expect(getOnboardingProviderState(undefined)).toBe("checking");
82 });
83});
84
85describe("selectOnboardingProvidersByDriver", () => {
86 it("prefers a ready instance with unknown authentication to an unauthenticated instance", () => {
87 const signedOutCodex: ServerProvider = {
88 ...readyCodex,
89 instanceId: ProviderInstanceId.make("codex_work"),
90 status: "error",
91 auth: { status: "unauthenticated" },
92 };
93
94 expect(selectOnboardingProvidersByDriver([signedOutCodex, readyCodex]).get("codex")).toBe(
95 readyCodex,
96 );
97 });
98
99 it("prefers a provider with an actionable sign-in over a failed provider", () => {
100 const failedCodex: ServerProvider = { ...readyCodex, status: "error" };
101 const signedOutCodex: ServerProvider = {
102 ...readyCodex,
103 instanceId: ProviderInstanceId.make("codex_work"),
104 status: "error",
105 auth: { status: "unauthenticated" },
106 };
107
108 expect(selectOnboardingProvidersByDriver([failedCodex, signedOutCodex]).get("codex")).toBe(
109 signedOutCodex,
110 );
111 });
112
113 it("prefers installed providers over missing or disabled instances", () => {
114 const disabledCodex: ServerProvider = { ...readyCodex, enabled: false };
115 const missingCodex: ServerProvider = {
116 ...readyCodex,
117 instanceId: ProviderInstanceId.make("codex_work"),
118 installed: false,
119 status: "error",
120 };
121
122 expect(
123 selectOnboardingProvidersByDriver([disabledCodex, missingCodex, readyCodex]).get("codex"),
124 ).toBe(readyCodex);
125 });
126
127 it("handles provider snapshots that have not arrived", () => {
128 expect(selectOnboardingProvidersByDriver(undefined).size).toBe(0);
129 });
130
131 it("keeps a ready custom account when the default account is signed out", () => {
132 const signedOutDefault: ServerProvider = {
133 ...readyCodex,
134 status: "error",
135 auth: { status: "unauthenticated" },
136 };
137 const readyCustom: ServerProvider = {
138 ...readyCodex,
139 instanceId: ProviderInstanceId.make("codex_work"),
140 };
141
142 expect(selectOnboardingProvidersByDriver([signedOutDefault, readyCustom]).get("codex")).toBe(
143 readyCustom,
144 );
145 });
146});
147
148describe("resolveOnboardingProviderLoginCommand", () => {
149 it("uses the selected Codex account binary", () => {
150 const provider = { ...readyCodex, instanceId: ProviderInstanceId.make("codex_work") };
151
152 expect(
153 resolveOnboardingProviderLoginCommand(
154 provider,
155 {
156 ...DEFAULT_SERVER_SETTINGS,
157 providerInstances: {
158 [provider.instanceId]: {
159 driver: provider.driver,
160 config: { binaryPath: "/opt/codex-work/bin/codex" },
161 },
162 },
163 },
164 "linux",
165 ),
166 ).toBe("/opt/codex-work/bin/codex login");
167 });
168
169 it("uses the selected Claude account binary", () => {
170 const provider: ServerProvider = {
171 ...readyCodex,
172 driver: ProviderDriverKind.make("claudeAgent"),
173 instanceId: ProviderInstanceId.make("claude_work"),
174 };
175
176 expect(
177 resolveOnboardingProviderLoginCommand(
178 provider,
179 {
180 ...DEFAULT_SERVER_SETTINGS,
181 providerInstances: {
182 [provider.instanceId]: {
183 driver: provider.driver,
184 config: { binaryPath: "/opt/claude-work/bin/claude" },
185 },
186 },
187 },
188 "linux",
189 ),
190 ).toBe("/opt/claude-work/bin/claude auth login");
191 });
192
193 it("quotes a Codex path with spaces for PowerShell", () => {
194 expect(
195 resolveOnboardingProviderLoginCommand(
196 readyCodex,
197 {
198 ...DEFAULT_SERVER_SETTINGS,
199 providerInstances: {
200 [ProviderInstanceId.make("codex")]: {
201 driver: ProviderDriverKind.make("codex"),
202 config: {
203 binaryPath: "C:\\Program Files\\Codex & Tools\\codex.exe",
204 },
205 },
206 },
207 },
208 "windows",
209 ),
210 ).toBe("& 'C:\\Program Files\\Codex & Tools\\codex.exe' login");
211 });
212
213 it("quotes a Claude path with shell metacharacters on POSIX", () => {
214 const provider: ServerProvider = {
215 ...readyCodex,
216 driver: ProviderDriverKind.make("claudeAgent"),
217 instanceId: ProviderInstanceId.make("claude"),
218 };
219
220 expect(
221 resolveOnboardingProviderLoginCommand(
222 provider,
223 {
224 ...DEFAULT_SERVER_SETTINGS,
225 providerInstances: {
226 [ProviderInstanceId.make("claude")]: {
227 driver: ProviderDriverKind.make("claudeAgent"),
228 config: {
229 binaryPath: "/opt/Claude Tools/$current/claude",
230 },
231 },
232 },
233 },
234 "linux",
235 ),
236 ).toBe("'/opt/Claude Tools/$current/claude' auth login");
237 });
238
239 it.each([
240 ["~/my tools/codex", "~/'my tools/codex' login"],
241 ["~\\my tools/codex", "~/'my tools/codex' login"],
242 ["~/tools/codex's build", `~/'tools/codex'"'"'s build' login`],
243 ["~\\tools\\codex's build", `~/'tools\\codex'"'"'s build' login`],
244 ["~/tools/codex; echo unsafe", "~/'tools/codex; echo unsafe' login"],
245 ])("keeps the home prefix expandable while quoting %s", (binaryPath, expectedCommand) => {
246 expect(
247 resolveOnboardingProviderLoginCommand(
248 readyCodex,
249 {
250 ...DEFAULT_SERVER_SETTINGS,
251 providerInstances: {
252 [ProviderInstanceId.make("codex")]: {
253 driver: ProviderDriverKind.make("codex"),
254 config: {
255 binaryPath,
256 },
257 },
258 },
259 },
260 "linux",
261 ),
262 ).toBe(expectedCommand);
263 });
264
265 it.each(["darwin", "linux"] as const)("quotes backslashes in a Codex path on %s", (platform) => {
266 expect(
267 resolveOnboardingProviderLoginCommand(
268 readyCodex,
269 {
270 ...DEFAULT_SERVER_SETTINGS,
271 providerInstances: {
272 [ProviderInstanceId.make("codex")]: {
273 driver: ProviderDriverKind.make("codex"),
274 config: {
275 binaryPath: "/opt/codex\\work/codex",
276 },
277 },
278 },
279 },
280 platform,
281 ),
282 ).toBe("'/opt/codex\\work/codex' login");
283 });
284
285 it("keeps a plain Windows path unquoted", () => {
286 expect(
287 resolveOnboardingProviderLoginCommand(
288 readyCodex,
289 {
290 ...DEFAULT_SERVER_SETTINGS,
291 providerInstances: {
292 [ProviderInstanceId.make("codex")]: {
293 driver: ProviderDriverKind.make("codex"),
294 config: {
295 binaryPath: "C:\\Tools\\codex.exe",
296 },
297 },
298 },
299 },
300 "windows",
301 ),
302 ).toBe("C:\\Tools\\codex.exe login");
303 });
304
305 it("uses the default command when an old server reports an unknown shell", () => {
306 expect(
307 resolveOnboardingProviderLoginCommand(
308 readyCodex,
309 {
310 ...DEFAULT_SERVER_SETTINGS,
311 providerInstances: {
312 [ProviderInstanceId.make("codex")]: {
313 driver: ProviderDriverKind.make("codex"),
314 config: {
315 binaryPath: "/opt/Codex Tools/codex",
316 },
317 },
318 },
319 },
320 "unknown",
321 ),
322 ).toBe("codex login");
323 });
324});
325
326describe("resolveOnboardingProviderInstallCommand", () => {
327 it("uses the PowerShell installer on Windows environments", () => {
328 expect(resolveOnboardingProviderInstallCommand("codex", "windows")).toBe(
329 "irm https://chatgpt.com/codex/install.ps1 | iex",
330 );
331 expect(resolveOnboardingProviderInstallCommand("claudeAgent", "windows")).toBe(
332 "irm https://claude.ai/install.ps1 | iex",
333 );
334 });
335
336 it.each(["darwin", "linux", "unknown"] as const)("uses the shell installer on %s", (platform) => {
337 expect(resolveOnboardingProviderInstallCommand("codex", platform)).toBe(
338 "curl -fsSL https://chatgpt.com/codex/install.sh | sh",
339 );
340 expect(resolveOnboardingProviderInstallCommand("claudeAgent", platform)).toBe(
341 "curl -fsSL https://claude.ai/install.sh | bash",
342 );
343 });
344});