Files
next-tjwater-frontend/src/shared/auth/keycloak-auth.test.ts
T
jiang abef9ffabf
Generic Container CI/CD / test-build-publish (push) Successful in 1m1s
Frontend CI/CD / build-test-publish-and-deploy (push) Successful in 1m31s
fix: bypass blocked Keycloak session iframe
2026-08-19 18:12:21 +08:00

92 lines
3.1 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import { parseRuntimeConfig } from "@/shared/config/env";
import {
initializeAuthentication,
toAuthenticatedUser,
toKeycloakConfig
} from "./keycloak-auth";
const requiredConfig = parseRuntimeConfig({
TJWATER_AUTH_MODE: "required",
TJWATER_KEYCLOAK_ISSUER: "https://auth.example.test/auth/realms/tjwater",
TJWATER_KEYCLOAK_CLIENT_ID: "next-tjwater"
});
describe("Keycloak authentication", () => {
it("derives the Keycloak server and Realm from the issuer", () => {
expect(toKeycloakConfig(requiredConfig.TJWATER_KEYCLOAK_ISSUER, "next-tjwater")).toEqual({
url: "https://auth.example.test/auth",
realm: "tjwater",
clientId: "next-tjwater"
});
});
it("initializes the public SPA flow and exposes the refreshed token", async () => {
const client = {
token: "access-token",
idTokenParsed: {
sub: "user-1",
name: "张调度",
email: "operator@example.test"
},
init: vi.fn().mockResolvedValue(true),
login: vi.fn().mockResolvedValue(undefined),
logout: vi.fn().mockResolvedValue(undefined),
updateToken: vi.fn().mockResolvedValue(false)
};
const authentication = await initializeAuthentication(requiredConfig, () => client);
expect(client.init).toHaveBeenCalledWith({
onLoad: "login-required",
flow: "standard",
pkceMethod: "S256",
checkLoginIframe: false
});
expect(authentication.user).toEqual({
name: "张调度",
role: "operator@example.test"
});
await expect(authentication.getAccessToken()).resolves.toBe("access-token");
expect(client.updateToken).toHaveBeenCalledWith(30);
await authentication.logout?.();
expect(client.logout).toHaveBeenCalledWith({ redirectUri: "http://localhost:3000/" });
});
it("uses stable user claim fallbacks", () => {
expect(toAuthenticatedUser({ sub: "user-2", preferred_username: "dispatcher" })).toEqual({
name: "dispatcher",
role: "统一认证用户"
});
});
it("returns to Keycloak instead of using a token after refresh failure", async () => {
const refreshError = new Error("refresh failed");
const client = {
token: "expired-token",
tokenParsed: { sub: "user-3" },
init: vi.fn().mockResolvedValue(true),
login: vi.fn().mockResolvedValue(undefined),
logout: vi.fn().mockResolvedValue(undefined),
updateToken: vi.fn().mockRejectedValue(refreshError)
};
const authentication = await initializeAuthentication(requiredConfig, () => client);
await expect(authentication.getAccessToken()).rejects.toBe(refreshError);
expect(client.login).toHaveBeenCalledWith({ redirectUri: "http://localhost:3000/" });
});
it("keeps authentication inert when explicitly disabled", async () => {
const createClient = vi.fn();
const authentication = await initializeAuthentication(
parseRuntimeConfig({ TJWATER_AUTH_MODE: "disabled" }),
createClient
);
expect(authentication.enabled).toBe(false);
await expect(authentication.getAccessToken()).resolves.toBeNull();
expect(createClient).not.toHaveBeenCalled();
});
});