test(web-next): boot smoke asserts auth-only bindAll
Dev-seed boot smoke for the retrofit floor: runs the REAL auth dev-seed binder (so assertFeatureConformance executes like pnpm dev), resolves every auth manifest use case + controller from the container, signs in a seeded user server-side, and asserts the dispatcher wires exactly one feature's binders — proving no dangling DI symbols from the deleted demo features. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016j8z4VHjedXDTjEDNg7qHK
This commit is contained in:
99
apps/web-next/src/server/bind-production.smoke.test.ts
Normal file
99
apps/web-next/src/server/bind-production.smoke.test.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
// Dev-seed boot smoke (platform-retrofit story 04): prove `bindAll()` boots
|
||||
// the app with exactly the auth feature bound — no dangling DI symbols from
|
||||
// the deleted demo features.
|
||||
//
|
||||
// Unlike bind-production.test.ts (which mocks the per-feature binders to test
|
||||
// dispatcher routing), this file runs the REAL auth dev-seed binder, so the
|
||||
// `assertFeatureConformance` call at its tail executes — the same boot
|
||||
// assertion `pnpm dev` runs. The bind-dev-seed docstring's "tests must not
|
||||
// call this" rule targets auth's own unit tests; this app-level smoke exists
|
||||
// precisely to exercise the real boot path.
|
||||
import { readFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
|
||||
// Production-path imports only — the dev-seed path never touches Payload, but
|
||||
// bind-production.ts imports these at module top for bindAllProduction.
|
||||
vi.mock("@repo/core-cms", () => ({ default: Promise.resolve({}) }));
|
||||
vi.mock("payload", () => ({
|
||||
getPayload: vi.fn(async () => ({ jobs: { queue: vi.fn() } })),
|
||||
}));
|
||||
|
||||
describe("dev-seed boot smoke — bindAll() binds exactly the auth feature", () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
vi.unstubAllEnvs();
|
||||
vi.stubEnv("USE_DEV_SEED", "true");
|
||||
vi.stubEnv("WEB_NEXT_SENTRY_DSN", "");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllEnvs();
|
||||
});
|
||||
|
||||
it("boots through the real auth dev-seed binder, passing its boot conformance assertion", async () => {
|
||||
const { bindAll } = await import("./bind-production");
|
||||
|
||||
await expect(bindAll()).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("resolves every auth manifest use case and controller after boot", async () => {
|
||||
const { bindAll } = await import("./bind-production");
|
||||
await bindAll();
|
||||
|
||||
const { authContainer } = await import("@repo/auth/di/container");
|
||||
const { AUTH_SYMBOLS } = await import("@repo/auth/di/symbols");
|
||||
const { authManifest } = await import("@repo/auth");
|
||||
|
||||
const useCases = Object.keys(authManifest.useCases);
|
||||
expect(useCases.sort()).toEqual(["signIn", "signOut", "signUp"]);
|
||||
|
||||
for (const useCase of useCases) {
|
||||
const cap = useCase[0]!.toUpperCase() + useCase.slice(1);
|
||||
for (const suffix of ["UseCase", "Controller"] as const) {
|
||||
const symbol =
|
||||
AUTH_SYMBOLS[`I${cap}${suffix}` as keyof typeof AUTH_SYMBOLS];
|
||||
expect(symbol, `AUTH_SYMBOLS.I${cap}${suffix} exists`).toBeDefined();
|
||||
expect(
|
||||
typeof authContainer.get(symbol),
|
||||
`authContainer resolves I${cap}${suffix}`,
|
||||
).toBe("function");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("signs in a dev-seed user through the bound controller (server-side smoke)", async () => {
|
||||
const { bindAll } = await import("./bind-production");
|
||||
await bindAll();
|
||||
|
||||
const { authContainer } = await import("@repo/auth/di/container");
|
||||
const { AUTH_SYMBOLS } = await import("@repo/auth/di/symbols");
|
||||
const { SESSION_COOKIE } = await import("@repo/auth");
|
||||
|
||||
const signIn = authContainer.get<import("@repo/auth").ISignInController>(
|
||||
AUTH_SYMBOLS.ISignInController,
|
||||
);
|
||||
const cookie = await signIn({
|
||||
username: "alice",
|
||||
password: "secret_alice",
|
||||
});
|
||||
|
||||
expect(cookie.name).toBe(SESSION_COOKIE);
|
||||
expect(cookie.value).not.toBe("");
|
||||
});
|
||||
|
||||
it("wires auth and nothing else — the dispatcher imports exactly one feature's binders", () => {
|
||||
// vitest runs with cwd at the package root (jsdom rewrites
|
||||
// import.meta.url to an http: URL, so resolve from cwd instead).
|
||||
const source = readFileSync(
|
||||
path.resolve(process.cwd(), "src/server/bind-production.ts"),
|
||||
"utf8",
|
||||
);
|
||||
const features = [...source.matchAll(/@repo\/([\w-]+)\/di\/bind-/g)].map(
|
||||
(match) => match[1],
|
||||
);
|
||||
|
||||
expect(features.length).toBeGreaterThan(0);
|
||||
expect([...new Set(features)].sort()).toEqual(["auth"]);
|
||||
});
|
||||
});
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user