import { describe, it, expect } from "vitest"; import { atproto, type AtprotoAuthConfig } from "../src/auth.js"; const AUTH_ROUTES_RE = /^@emdash-cms\/auth-atproto\/routes\//; describe("atproto auth config", () => { describe("AuthProviderDescriptor contract", () => { it("returns id 'atproto'", () => { const descriptor = atproto(); expect(descriptor.id).toBe("atproto"); }); it("has label 'Atmosphere'", () => { const descriptor = atproto(); expect(descriptor.label).toBe("Atmosphere"); }); it("points adminEntry to the admin module", () => { const descriptor = atproto(); expect(descriptor.adminEntry).toBe("@emdash-cms/auth-atproto/admin"); }); it("defaults config to empty object when no options provided", () => { const descriptor = atproto(); expect(descriptor.config).toEqual({}); }); it("defaults config to empty object when undefined is passed", () => { const descriptor = atproto(undefined); expect(descriptor.config).toEqual({}); }); it("declares routes pointing to auth package", () => { const descriptor = atproto(); expect(descriptor.routes).toBeDefined(); expect(descriptor.routes!.length).toBe(4); for (const route of descriptor.routes!) { expect(route.entrypoint).toMatch(AUTH_ROUTES_RE); } }); it("declares storage collections for OAuth state", () => { const descriptor = atproto(); expect(descriptor.storage).toBeDefined(); expect(descriptor.storage).toHaveProperty("states"); expect(descriptor.storage).toHaveProperty("sessions"); }); it("declares publicRoutes with specific paths", () => { const descriptor = atproto(); expect(descriptor.publicRoutes).toBeDefined(); expect(descriptor.publicRoutes).toContain("/_emdash/api/auth/atproto/"); // Should not have overly broad prefixes expect(descriptor.publicRoutes).not.toContain("/_emdash/.well-known/"); }); }); describe("config passthrough", () => { it("passes allowedDIDs through", () => { const config: AtprotoAuthConfig = { allowedDIDs: ["did:plc:abc123", "did:web:example.com"], }; const descriptor = atproto(config); const result = descriptor.config as AtprotoAuthConfig; expect(result.allowedDIDs).toEqual(["did:plc:abc123", "did:web:example.com"]); }); it("passes defaultRole through", () => { const descriptor = atproto({ defaultRole: 20 }); const result = descriptor.config as AtprotoAuthConfig; expect(result.defaultRole).toBe(20); }); it("passes allowedHandles through", () => { const config: AtprotoAuthConfig = { allowedHandles: ["*.example.com", "alice.bsky.social"], }; const descriptor = atproto(config); const result = descriptor.config as AtprotoAuthConfig; expect(result.allowedHandles).toEqual(["*.example.com", "alice.bsky.social"]); }); it("passes full config through unchanged", () => { const config: AtprotoAuthConfig = { allowedDIDs: ["did:plc:me123"], allowedHandles: ["*.example.com"], defaultRole: 40, }; const descriptor = atproto(config); expect(descriptor.config).toEqual(config); }); it("does not mutate the input config", () => { const config: AtprotoAuthConfig = { allowedDIDs: ["did:plc:alice123"], allowedHandles: ["*.example.com"], defaultRole: 30, }; const original = { ...config, allowedDIDs: [...config.allowedDIDs!], allowedHandles: [...config.allowedHandles!], }; atproto(config); expect(config).toEqual(original); }); }); describe("descriptor shape invariants", () => { it("id is always a non-empty string", () => { const descriptor = atproto(); expect(typeof descriptor.id).toBe("string"); expect(descriptor.id.length).toBeGreaterThan(0); }); it("label is always a non-empty string", () => { const descriptor = atproto(); expect(typeof descriptor.label).toBe("string"); expect(descriptor.label.length).toBeGreaterThan(0); }); it("adminEntry is always a non-empty string", () => { const descriptor = atproto(); expect(typeof descriptor.adminEntry).toBe("string"); expect(descriptor.adminEntry!.length).toBeGreaterThan(0); }); }); });