| 1 | import { describe, expect, it } from "vite-plus/test"; |
| 2 | |
| 3 | import { |
| 4 | deriveAvailableInstanceId, |
| 5 | getProviderIdentityDraft, |
| 6 | isConfiguredAcpRegistryAgent, |
| 7 | resolveAcpRegistryWizardNavigation, |
| 8 | resolveWizardNavigation, |
| 9 | updateProviderIdentityDraft, |
| 10 | } from "./AddProviderInstanceDialog.logic"; |
| 11 | |
| 12 | describe("resolveWizardNavigation", () => { |
| 13 | const invalidId = { instanceIdError: "Instance ID is required." }; |
| 14 | const validId = { instanceIdError: null }; |
| 15 | |
| 16 | it("allows moving from Driver to Identity before the instance id is valid", () => { |
| 17 | expect(resolveWizardNavigation(0, 1, 3, invalidId)).toEqual({ kind: "navigate", step: 1 }); |
| 18 | }); |
| 19 | |
| 20 | it("blocks Next from Identity to Config while the instance id is invalid", () => { |
| 21 | expect(resolveWizardNavigation(1, 2, 3, invalidId)).toEqual({ |
| 22 | kind: "blocked", |
| 23 | step: 1, |
| 24 | error: "Instance ID is required.", |
| 25 | }); |
| 26 | }); |
| 27 | |
| 28 | it("stops a direct Driver-to-Config skip at Identity and surfaces its error", () => { |
| 29 | expect(resolveWizardNavigation(0, 2, 3, invalidId)).toEqual({ |
| 30 | kind: "blocked", |
| 31 | step: 1, |
| 32 | error: "Instance ID is required.", |
| 33 | }); |
| 34 | }); |
| 35 | |
| 36 | it("allows advancing and skipping forward once the instance id is valid", () => { |
| 37 | expect(resolveWizardNavigation(1, 2, 3, validId)).toEqual({ kind: "navigate", step: 2 }); |
| 38 | expect(resolveWizardNavigation(0, 2, 3, validId)).toEqual({ kind: "navigate", step: 2 }); |
| 39 | }); |
| 40 | |
| 41 | it("always preserves backward Driver and Identity navigation", () => { |
| 42 | expect(resolveWizardNavigation(2, 1, 3, invalidId)).toEqual({ kind: "navigate", step: 1 }); |
| 43 | expect(resolveWizardNavigation(2, 0, 3, invalidId)).toEqual({ kind: "navigate", step: 0 }); |
| 44 | expect(resolveWizardNavigation(1, 0, 3, invalidId)).toEqual({ kind: "navigate", step: 0 }); |
| 45 | }); |
| 46 | |
| 47 | it("clamps requested steps to the wizard bounds", () => { |
| 48 | expect(resolveWizardNavigation(2, 8, 3, validId)).toEqual({ kind: "navigate", step: 2 }); |
| 49 | expect(resolveWizardNavigation(0, -1, 3, invalidId)).toEqual({ kind: "navigate", step: 0 }); |
| 50 | }); |
| 51 | }); |
| 52 | |
| 53 | describe("ACP Registry wizard", () => { |
| 54 | it("requires a prepared result or valid manual configuration before Identity", () => { |
| 55 | expect( |
| 56 | resolveAcpRegistryWizardNavigation(0, 1, { |
| 57 | instanceIdError: null, |
| 58 | selectionError: "Select an ACP or configure one manually.", |
| 59 | }), |
| 60 | ).toEqual({ |
| 61 | kind: "blocked", |
| 62 | step: 0, |
| 63 | error: "Select an ACP or configure one manually.", |
| 64 | }); |
| 65 | |
| 66 | expect( |
| 67 | resolveAcpRegistryWizardNavigation(0, 1, { |
| 68 | instanceIdError: null, |
| 69 | selectionError: null, |
| 70 | }), |
| 71 | ).toEqual({ kind: "navigate", step: 1 }); |
| 72 | }); |
| 73 | |
| 74 | it("derives a collision-free instance id without exceeding the slug limit", () => { |
| 75 | const derive = (label: string) => `acpRegistry_${label}`; |
| 76 | const existing = new Set(["acpRegistry_gemini", "acpRegistry_gemini_2"]); |
| 77 | |
| 78 | expect(deriveAvailableInstanceId(derive, "gemini", existing)).toBe("acpRegistry_gemini_3"); |
| 79 | |
| 80 | const longBase = `acpRegistry_${"a".repeat(48)}`; |
| 81 | expect(deriveAvailableInstanceId(() => longBase, "ignored", new Set([longBase]))).toHaveLength( |
| 82 | 62, |
| 83 | ); |
| 84 | }); |
| 85 | |
| 86 | it("only marks matching ACP Registry instances as already added", () => { |
| 87 | const instances = { |
| 88 | codex: { driver: "codex", config: { agentId: "gemini" } }, |
| 89 | registry: { driver: "acpRegistry", config: { agentId: "gemini" } }, |
| 90 | }; |
| 91 | |
| 92 | expect(isConfiguredAcpRegistryAgent(instances, "gemini")).toBe(true); |
| 93 | expect(isConfiguredAcpRegistryAgent(instances, "codex")).toBe(false); |
| 94 | }); |
| 95 | |
| 96 | it("keeps registry-prefilled identity separate from other drivers", () => { |
| 97 | const registryDrafts = updateProviderIdentityDraft({}, "acpRegistry", { |
| 98 | label: "Gemini CLI", |
| 99 | instanceIdOverride: "acpRegistry_gemini_cli", |
| 100 | }); |
| 101 | |
| 102 | expect(getProviderIdentityDraft(registryDrafts, "codex")).toEqual({ |
| 103 | label: "", |
| 104 | accentColor: "", |
| 105 | instanceIdOverride: null, |
| 106 | }); |
| 107 | |
| 108 | const drafts = updateProviderIdentityDraft(registryDrafts, "codex", { |
| 109 | label: "Work", |
| 110 | instanceIdOverride: "codex_work", |
| 111 | }); |
| 112 | expect(getProviderIdentityDraft(drafts, "acpRegistry")).toMatchObject({ |
| 113 | label: "Gemini CLI", |
| 114 | instanceIdOverride: "acpRegistry_gemini_cli", |
| 115 | }); |
| 116 | }); |
| 117 | }); |