// server/services/apiClients.test.ts import { describe, expect, it, vi } from "vitest"; vi.mock("../db/client.js", () => ({ db: {}, schema: { apiClients: {} }, })); vi.mock("../env.js", () => ({ env: { requireMasterKey: vi.fn() } })); import { apiClientInternals } from "./apiClients.js"; describe("apiClientInternals", () => { it("retombe sur read quand aucun scope n'est fourni", () => { expect(apiClientInternals.normalizeScopes([])).toEqual(["read"]); }); it("déduplique les scopes en gardant l'ordre", () => { expect(apiClientInternals.normalizeScopes(["read", "operate", "read"])).toEqual([ "read", "operate", ]); }); it("rejette un scope inconnu", () => { expect(() => apiClientInternals.normalizeScopes(["root" as never])).toThrow( "Scope API inconnu: root", ); }); it("convertit une ligne DB en vue sans token hash", () => { const view = apiClientInternals.toView({ id: "client_1", name: "App locale", tokenPrefix: "su_abcdefghi", tokenHash: "hash-secret", scopesJson: '["read","operate"]', createdAt: "2026-06-05T08:00:00.000Z", lastUsedAt: null, revokedAt: null, }); expect(view).toEqual({ id: "client_1", name: "App locale", tokenPrefix: "su_abcdefghi", scopes: ["read", "operate"], createdAt: "2026-06-05T08:00:00.000Z", lastUsedAt: null, revokedAt: null, }); expect(JSON.stringify(view)).not.toContain("hash-secret"); }); it("applique les scopes par capacité", () => { expect(apiClientInternals.hasApiScope(["read"], "read")).toBe(true); expect(apiClientInternals.hasApiScope(["read"], "operate")).toBe(false); expect(apiClientInternals.hasApiScope(["operate"], "read")).toBe(true); expect(apiClientInternals.hasApiScope(["operate"], "operate")).toBe(true); expect(apiClientInternals.hasApiScope(["debug"], "debug")).toBe(true); expect(apiClientInternals.hasApiScope(["admin"], "debug")).toBe(true); expect(apiClientInternals.hasApiScope(["admin"], "admin")).toBe(true); }); });