70 lines
2.8 KiB
JavaScript
70 lines
2.8 KiB
JavaScript
import { afterEach, describe, expect, test, vi } from "vitest";
|
|
import { changeExternalPassword, loginExternal, 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", logoUrl: "https://media.example.com/fami.png" },
|
|
capabilities: ["user:list"],
|
|
passwordChangeRequired: true
|
|
})).toEqual(expect.objectContaining({
|
|
account: "fami-manager",
|
|
appCode: "fami",
|
|
appName: "Fami",
|
|
logoUrl: "https://media.example.com/fami.png",
|
|
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("login sends only account and password so the server resolves the App", async () => {
|
|
const fetchMock = vi.fn(async () => jsonResponse({ code: 0, data: { account: "fami-manager", appCode: "fami" } }));
|
|
vi.stubGlobal("fetch", fetchMock);
|
|
|
|
await loginExternal({ account: " fami-manager ", appCode: "must-not-be-sent", password: " password bytes " });
|
|
|
|
expect(fetchMock.mock.calls[0][0]).toBe("/api/v1/external/auth/login");
|
|
expect(JSON.parse(fetchMock.mock.calls[0][1].body)).toEqual({ account: "fami-manager", password: " password bytes " });
|
|
});
|
|
|
|
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 });
|
|
}
|