Files
Ekklesia/packages/contracts/tests/identity.test.ts
T
ekklesia 91d8f5946f
CI / verify (pull_request) Successful in 27s
CI / infrastructure-smoke (pull_request) Successful in 19s
CI / migration-smoke (pull_request) Successful in 26s
feat: expose core identity transport
2026-07-21 19:15:14 -05:00

91 lines
3.6 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
ANONYMOUS_PRINCIPAL,
createAuthenticatedPrincipal,
type EkklesiaPrincipal,
type EkklesiaUserId,
isAnonymousPrincipal,
isAuthenticatedPrincipal,
parseEkklesiaUserId,
safeParseEkklesiaUserId,
} from "../src/index.js";
describe("Ekklesia identity contracts", () => {
it("parses a valid opaque user id", () => {
expect(parseEkklesiaUserId("user_123")).toBe("user_123");
});
it("is opaque at the type level: a plain string is not assignable without parsing", () => {
// @ts-expect-error a bare string must not satisfy the branded EkklesiaUserId type.
const notAllowed: EkklesiaUserId = "raw-string";
expect(notAllowed).toBe("raw-string"); // the brand adds no runtime wrapper or tag
});
it("serializes as a plain opaque string despite the type-level brand", () => {
const userId = parseEkklesiaUserId("user_123");
expect(JSON.stringify({ userId })).toBe('{"userId":"user_123"}');
expect(`${userId}`).toBe("user_123");
expect(typeof userId).toBe("string");
});
it("does not assume any particular id format such as UUID", () => {
expect(() => parseEkklesiaUserId("not-a-uuid-at-all")).not.toThrow();
expect(() => parseEkklesiaUserId("2aKp4jPeV1QbXwyz1OH6IcAUfl2n1fc1")).not.toThrow();
});
it("rejects an empty or oversized user id", () => {
expect(() => parseEkklesiaUserId("")).toThrow();
expect(() => parseEkklesiaUserId(" ")).toThrow();
expect(() => parseEkklesiaUserId("x".repeat(129))).toThrow();
expect(() => parseEkklesiaUserId(42)).toThrow();
expect(() => parseEkklesiaUserId(null)).toThrow();
expect(() => parseEkklesiaUserId(undefined)).toThrow();
});
it("exposes a safe-parse variant that never throws", () => {
expect(safeParseEkklesiaUserId("user_123").success).toBe(true);
const failed = safeParseEkklesiaUserId("");
expect(failed.success).toBe(false);
});
it("returns a frozen, stable anonymous principal singleton", () => {
expect(ANONYMOUS_PRINCIPAL).toEqual({ kind: "anonymous" });
expect(Object.isFrozen(ANONYMOUS_PRINCIPAL)).toBe(true);
expect(isAnonymousPrincipal(ANONYMOUS_PRINCIPAL)).toBe(true);
expect(isAuthenticatedPrincipal(ANONYMOUS_PRINCIPAL)).toBe(false);
});
it("creates a frozen authenticated principal carrying only the opaque user id", () => {
const principal = createAuthenticatedPrincipal("user_123");
expect(principal.kind).toBe("authenticated");
expect(principal.userId).toBe("user_123");
expect(Object.isFrozen(principal)).toBe(true);
expect(isAuthenticatedPrincipal(principal)).toBe(true);
expect(isAnonymousPrincipal(principal)).toBe(false);
expect(Object.keys(principal).sort()).toEqual(["kind", "userId"]);
});
it("rejects an invalid user id when creating an authenticated principal", () => {
expect(() => createAuthenticatedPrincipal("")).toThrow();
expect(() => createAuthenticatedPrincipal(42)).toThrow();
});
it("discriminates principals on kind without any role, permission, or profile fields", () => {
const principals: EkklesiaPrincipal[] = [
ANONYMOUS_PRINCIPAL,
createAuthenticatedPrincipal("user_123"),
];
for (const principal of principals) {
expect(["anonymous", "authenticated"]).toContain(principal.kind);
expect(Object.keys(principal)).not.toContain("email");
expect(Object.keys(principal)).not.toContain("roles");
expect(Object.keys(principal)).not.toContain("permissions");
expect(Object.keys(principal)).not.toContain("organizationId");
expect(Object.keys(principal)).not.toContain("sessionToken");
}
});
});