/** * Phase D3 — Multi-connection pool wizard tests. * * Tests cover: * 1. PoolCreateSchema accepts/rejects connectionIds combinations * 2. Structural assertions on PoolWizard.tsx (multi-select, POST body, step-3 preview) * 3. i18n parity: new wizard keys exist in both locales * * Node native test runner — no JSdom needed (pure schema + source analysis). */ import test from "node:test"; import assert from "node:assert/strict"; import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { PoolCreateSchema } from "../../src/shared/schemas/quota.js"; const ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", ".."); const WIZARD_PATH = path.join( ROOT, "src", "app", "(dashboard)", "dashboard", "costs", "quota-share", "components", "PoolWizard.tsx" ); const EN_PATH = path.join(ROOT, "src", "i18n", "messages", "en.json"); const PT_PATH = path.join(ROOT, "src", "i18n", "messages", "pt-BR.json"); const wizardSrc = fs.readFileSync(WIZARD_PATH, "utf-8"); const en = JSON.parse(fs.readFileSync(EN_PATH, "utf-8")) as { quotaShare: Record }; const pt = JSON.parse(fs.readFileSync(PT_PATH, "utf-8")) as { quotaShare: Record }; // ── PoolCreateSchema: connectionIds field ───────────────────────────────────── test("PoolCreateSchema accepts multi-connection input when primary is a member", () => { const result = PoolCreateSchema.safeParse({ connectionId: "a", connectionIds: ["a", "b"], name: "x", allocations: [], }); assert.ok(result.success, `Expected success, got: ${result.error?.message}`); if (result.success) { assert.deepEqual(result.data.connectionIds, ["a", "b"]); assert.equal(result.data.connectionId, "a"); } }); test("PoolCreateSchema rejects when primary connectionId is NOT in connectionIds", () => { const result = PoolCreateSchema.safeParse({ connectionId: "z", connectionIds: ["a", "b"], name: "x", allocations: [], }); assert.equal(result.success, false, "Expected refine to reject when primary not in connectionIds"); const msg = result.error?.issues[0]?.message ?? ""; assert.ok( msg.includes("primary connectionId must be one of connectionIds"), `Expected refine message, got: "${msg}"` ); }); test("PoolCreateSchema accepts single-connection input without connectionIds (back-compat)", () => { const result = PoolCreateSchema.safeParse({ connectionId: "c", name: "Pool" }); assert.ok(result.success, `Expected success, got: ${result.error?.message}`); if (result.success) { assert.equal(result.data.connectionIds, undefined); assert.equal(result.data.connectionId, "c"); } }); test("PoolCreateSchema rejects empty connectionIds array", () => { const result = PoolCreateSchema.safeParse({ connectionId: "a", connectionIds: [], name: "x", allocations: [], }); assert.equal(result.success, false, "Expected failure for empty connectionIds"); }); test("PoolCreateSchema accepts connectionIds with single element matching connectionId", () => { const result = PoolCreateSchema.safeParse({ connectionId: "solo", connectionIds: ["solo"], name: "solo pool", allocations: [], }); assert.ok(result.success, `Expected success, got: ${result.error?.message}`); }); // ── PoolWizard.tsx structural assertions ────────────────────────────────────── test("PoolWizard.tsx: connectionIds state is defined (multi-select)", () => { assert.ok( wizardSrc.includes("connectionIds"), "Expected connectionIds state in PoolWizard" ); assert.ok( wizardSrc.includes("useState([])"), "Expected connectionIds initialized as string[] state" ); }); test("PoolWizard.tsx: renders checkboxes for connection selection in step 1", () => { assert.ok( wizardSrc.includes('type="checkbox"'), "Expected checkbox inputs in step 1 for multi-connection selection" ); }); test("PoolWizard.tsx: primaryConnectionId is derived from connectionIds[0]", () => { assert.ok( wizardSrc.includes("primaryConnectionId = connectionIds[0]"), "Expected primaryConnectionId derived from connectionIds[0]" ); }); test("PoolWizard.tsx: POST body sends both connectionId and connectionIds", () => { assert.ok( wizardSrc.includes("connectionId: primaryConnectionId"), "Expected connectionId: primaryConnectionId in POST body" ); assert.ok( wizardSrc.includes("connectionIds,"), "Expected connectionIds spread in POST body" ); }); test("PoolWizard.tsx: step-3 preview maps over connectionIds (previewByProvider)", () => { assert.ok( wizardSrc.includes("previewByProvider"), "Expected previewByProvider useMemo in PoolWizard" ); assert.ok( wizardSrc.includes("connectionIds.map((cid)"), "Expected connectionIds.map to build per-provider preview" ); }); test("PoolWizard.tsx: step-2 shows additional connections note when multiple selected", () => { assert.ok( wizardSrc.includes("wizardAdditionalConnectionsNote"), "Expected wizardAdditionalConnectionsNote i18n key in step 2" ); assert.ok( wizardSrc.includes("connectionIds.length > 1"), "Expected guard connectionIds.length > 1 for the note" ); }); test("PoolWizard.tsx: primary badge rendered for first selected connection", () => { assert.ok( wizardSrc.includes("wizardPrimaryBadge"), "Expected wizardPrimaryBadge i18n key in step 1 checkbox list" ); }); // ── i18n parity: new wizard keys ───────────────────────────────────────────── const NEW_KEYS = [ "wizardConnectionsLabel", "wizardPrimaryBadge", "wizardAdditionalConnectionsNote", "wizardPreviewMoreModels", ]; for (const key of NEW_KEYS) { test(`i18n en.json has key quotaShare.${key}`, () => { assert.ok( Object.prototype.hasOwnProperty.call(en.quotaShare, key), `en.json missing quotaShare.${key}` ); assert.equal(typeof en.quotaShare[key], "string", `quotaShare.${key} must be a string in en.json`); assert.ok(en.quotaShare[key].length > 0, `quotaShare.${key} must not be empty in en.json`); }); test(`i18n pt-BR.json has key quotaShare.${key}`, () => { assert.ok( Object.prototype.hasOwnProperty.call(pt.quotaShare, key), `pt-BR.json missing quotaShare.${key}` ); assert.equal(typeof pt.quotaShare[key], "string", `quotaShare.${key} must be a string in pt-BR.json`); assert.ok(pt.quotaShare[key].length > 0, `quotaShare.${key} must not be empty in pt-BR.json`); }); } test("QuotaSharePageClient wires the multi-provider icons (providers prop is passed, not dead code)", () => { const pageClientPath = path.join( ROOT, "src", "app", "(dashboard)", "dashboard", "costs", "quota-share", "QuotaSharePageClient.tsx" ); const src = fs.readFileSync(pageClientPath, "utf8"); // The PoolCard providers prop must actually be populated by the parent from // the pool's connectionIds — otherwise the multi-provider icon row never renders. assert.ok(src.includes("providers={"), "parent must pass a providers prop to the card"); assert.ok( /connectionIds\s*\?\?\s*\[pool\.connectionId\]/.test(src), "providers must be derived from pool.connectionIds (falling back to the primary)" ); }); test("i18n parity: all quotaShare.wizard* keys are in sync between en and pt-BR", () => { const enWizardKeys = Object.keys(en.quotaShare) .filter((k) => k.startsWith("wizard")) .sort(); const ptWizardKeys = Object.keys(pt.quotaShare) .filter((k) => k.startsWith("wizard")) .sort(); assert.deepEqual( enWizardKeys, ptWizardKeys, `wizard* key parity mismatch.\nen: ${JSON.stringify(enWizardKeys)}\npt: ${JSON.stringify(ptWizardKeys)}` ); });