/** * Reusable conformance assertions every concrete `AuthProviderAdapter` MUST * pass. Implemented as a set of pure functions so the runner-agnostic * assertions in this package can be driven from `@suaveplan/testing/runner` * without forcing concrete providers onto a specific runner. * * `runAuthProviderConformance` returns a per-category list of scenario * results; concrete providers wrap it in `describe`/`it` from their own test * file (`import { describe, it, expect } from "@suaveplan/testing/runner";`). */ import { detectAuthPathCollisions } from "../collision-detector/collision-detector.js"; import { AUTH_PROVIDER_CODES } from "../error-codes/error-codes.js"; import { PathOwnership } from "../path-ownership/path-ownership.js"; import { isAuthPathPrefix } from "../provider/provider.js"; import type { AuthProviderAdapter } from "../provider/provider.types.js"; import { listProviderRouteNames } from "../route-surface/route-surface.js"; import type { AuthProviderConformanceCategory, AuthProviderConformanceOptions, ConformanceScenarioResult, } from "./conformance.types.js"; /** * Returns `true` when re-registering the same provider on an existing * registry does NOT throw (the documented idempotency invariant). */ export function isRegisterIdempotent( registry: PathOwnership, provider: AuthProviderAdapter ): boolean { try { registry.register(provider); return true; } catch { return false; } } /** * Returns `true` when attempting to register `incoming` against a registry * already owned by another provider throws * `AUTH_PROVIDER.MULTIPLE_PROVIDERS_REGISTERED`. Any other thrown shape — or * no throw at all — returns `false`. */ export function rejectsSecondProvider( registry: PathOwnership, incoming: AuthProviderAdapter ): boolean { try { registry.register(incoming); return false; } catch (err) { const code = err instanceof Error && "code" in err ? (err as { readonly code: unknown }).code : undefined; return code === AUTH_PROVIDER_CODES.MULTIPLE_PROVIDERS_REGISTERED; } } /** * Drive the harness against the supplied factory. Returns one * `ConformanceScenarioResult` per scenario, grouped per category. Concrete * providers iterate the result list and turn each entry into an `it()` * assertion. */ export function runAuthProviderConformance( options: AuthProviderConformanceOptions ): ReadonlyArray { const results: ConformanceScenarioResult[] = []; // --- path-ownership ---------------------------------------------------- const pathOwnership: AuthProviderConformanceCategory = "path-ownership"; { const provider = options.factory(); const registry = new PathOwnership(); registry.register(provider); results.push({ category: pathOwnership, name: "registers provider on first call", passed: registry.hasProvider(), }); results.push({ category: pathOwnership, name: "re-registering same provider is idempotent", passed: isRegisterIdempotent(registry, provider), }); const second = options.factory(); const distinct: typeof second = { ...second, id: `${second.id}.alt` }; results.push({ category: pathOwnership, name: "second provider rejected with MULTIPLE_PROVIDERS_REGISTERED", passed: rejectsSecondProvider(registry, distinct), }); } // --- route-surface ----------------------------------------------------- const routeSurface: AuthProviderConformanceCategory = "route-surface"; { const provider = options.factory(); results.push({ category: routeSurface, name: "pathPrefix is a legal prefix", passed: isAuthPathPrefix(provider.pathPrefix), }); const names = listProviderRouteNames(); results.push({ category: routeSurface, name: "canonical surface enumerates every documented name", passed: names.length >= 9, detail: `canonical names: ${names.join(", ")}`, }); } // --- lifecycle --------------------------------------------------------- const lifecycle: AuthProviderConformanceCategory = "lifecycle"; { const provider = options.factory(); const lc = provider.lifecycle; results.push({ category: lifecycle, name: "lifecycle quartet present", passed: typeof lc.install === "function" && typeof lc.start === "function" && typeof lc.stop === "function" && typeof lc.uninstall === "function", }); } // --- collision --------------------------------------------------------- const collision: AuthProviderConformanceCategory = "collision"; { const provider = options.factory(); const innerReport = detectAuthPathCollisions({ provider, plugins: [ { name: "plugin-x", routes: [{ path: `${provider.pathPrefix}/custom` }], }, ], }); results.push({ category: collision, name: "plugin route inside auth prefix is reported", passed: innerReport.hasCollisions, }); const outerReport = detectAuthPathCollisions({ provider, plugins: [ { name: "plugin-y", routes: [{ path: "/billing/invoices" }], }, ], }); results.push({ category: collision, name: "plugin route outside auth prefix is not reported", passed: !outerReport.hasCollisions, }); } return Object.freeze(results); }