import { afterEach, describe, expect, test, vi } from "vitest"; import { changeExternalPassword, listExternalApps, normalizeSession } from "./auth.js"; afterEach(() => { vi.unstubAllGlobals(); }); describe("normalizeSession", () => { test("normalizes account objects, fixed app and capabilities without stringifying objects", () => { expect(normalizeSession({ account: { id: "ext-1", username: "fami-manager" }, app: { appCode: "fami", appName: "Fami" }, capabilities: ["user:list"], passwordChangeRequired: true })).toEqual(expect.objectContaining({ account: "fami-manager", appCode: "fami", appName: "Fami", capabilities: ["user:list"], passwordChangeRequired: true })); }); test("merges permission aliases from root and bound user", () => { const session = normalizeSession({ appCode: "lalu", permissions: ["host:list"], user: { permissions: ["team:view"], username: "lalu-ops" } }); expect(session.capabilities).toEqual(["host:list", "team:view"]); }); test("keeps the external account separate from the linked App user", () => { const session = normalizeSession({ account: { id: "external-account-8", username: "fami-manager" }, app: { appCode: "fami", appName: "Fami" }, user: { userId: "app-user-9", username: "App Nickname" } }); expect(session.account).toBe("fami-manager"); expect(session.displayName).toBe("App Nickname"); expect(session.id).toBe("external-account-8"); }); }); describe("external auth API contract", () => { test("reads appName from auth/apps items", async () => { vi.stubGlobal("fetch", vi.fn(async () => jsonResponse({ code: 0, data: { items: [{ appCode: "fami", appName: "Fami" }], total: 1 } }))); await expect(listExternalApps()).resolves.toEqual([{ appCode: "fami", name: "Fami" }]); }); test("change-password sends currentPassword and never oldPassword", async () => { const fetchMock = vi.fn(async () => jsonResponse({ code: 0, data: {} })); vi.stubGlobal("fetch", fetchMock); await changeExternalPassword({ newPassword: "new-password", oldPassword: "current-password" }); expect(JSON.parse(fetchMock.mock.calls[0][1].body)).toEqual({ currentPassword: "current-password", newPassword: "new-password" }); }); }); function jsonResponse(body, status = 200) { return new Response(JSON.stringify(body), { headers: { "content-type": "application/json" }, status }); }