refactor: strip Phase/Plan/R-number references from source comments
This commit is contained in:
@@ -22,66 +22,65 @@ export const CONTRACT_HEADER_SEED: Header = {
|
||||
* Header is a singleton (Payload Global). The interface exposes only
|
||||
* getHeader(). The contract verifies the shape, count, and order of items.
|
||||
*/
|
||||
export const headerRepositoryContract =
|
||||
defineContractSuite<IHeaderRepository>(
|
||||
"IHeaderRepository",
|
||||
({ buildSubject, getTracer }) => {
|
||||
let repo: IHeaderRepository;
|
||||
export const headerRepositoryContract = defineContractSuite<IHeaderRepository>(
|
||||
"IHeaderRepository",
|
||||
({ buildSubject, getTracer }) => {
|
||||
let repo: IHeaderRepository;
|
||||
|
||||
beforeEach(async () => {
|
||||
repo = await buildSubject();
|
||||
beforeEach(async () => {
|
||||
repo = await buildSubject();
|
||||
});
|
||||
|
||||
// --- getHeader ---
|
||||
|
||||
it("getHeader returns an object with an items array of the seeded length", async () => {
|
||||
const header = await repo.getHeader();
|
||||
expect(header).toBeDefined();
|
||||
expect(header.items).toBeInstanceOf(Array);
|
||||
expect(header.items).toHaveLength(3);
|
||||
});
|
||||
|
||||
it("getHeader items appear in the seeded order with correct shape", async () => {
|
||||
const header = await repo.getHeader();
|
||||
expect(header.items[0]?.label).toBe("Home");
|
||||
expect(header.items[0]?.href).toBe("/");
|
||||
expect(header.items[0]?.external).toBe(false);
|
||||
expect(header.items[1]?.label).toBe("Blog");
|
||||
expect(header.items[1]?.href).toBe("/blog");
|
||||
expect(header.items[1]?.external).toBe(false);
|
||||
expect(header.items[2]?.label).toBe("Docs");
|
||||
expect(header.items[2]?.href).toBe("/docs");
|
||||
expect(header.items[2]?.external).toBe(true);
|
||||
});
|
||||
|
||||
it("getHeader items have label, href, and external fields", async () => {
|
||||
const header = await repo.getHeader();
|
||||
for (const item of header.items) {
|
||||
expect(typeof item.label).toBe("string");
|
||||
expect(item.label.length).toBeGreaterThan(0);
|
||||
expect(typeof item.href).toBe("string");
|
||||
expect(item.href.length).toBeGreaterThan(0);
|
||||
expect(typeof item.external).toBe("boolean");
|
||||
}
|
||||
});
|
||||
|
||||
it("getHeader logoId is string or undefined", async () => {
|
||||
const header = await repo.getHeader();
|
||||
expect(
|
||||
header.logoId === undefined || typeof header.logoId === "string",
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
describe("span emission", () => {
|
||||
it("getHeader emits header.getHeader span with op=repository", async () => {
|
||||
if (!getTracer) return;
|
||||
const tracer = getTracer();
|
||||
tracer.reset();
|
||||
await repo.getHeader();
|
||||
const span = tracer.findSpan("header.getHeader");
|
||||
expect(span).toBeDefined();
|
||||
expect(span!.op).toBe("repository");
|
||||
});
|
||||
|
||||
// --- getHeader ---
|
||||
|
||||
it("getHeader returns an object with an items array of the seeded length", async () => {
|
||||
const header = await repo.getHeader();
|
||||
expect(header).toBeDefined();
|
||||
expect(header.items).toBeInstanceOf(Array);
|
||||
expect(header.items).toHaveLength(3);
|
||||
});
|
||||
|
||||
it("getHeader items appear in the seeded order with correct shape", async () => {
|
||||
const header = await repo.getHeader();
|
||||
expect(header.items[0]?.label).toBe("Home");
|
||||
expect(header.items[0]?.href).toBe("/");
|
||||
expect(header.items[0]?.external).toBe(false);
|
||||
expect(header.items[1]?.label).toBe("Blog");
|
||||
expect(header.items[1]?.href).toBe("/blog");
|
||||
expect(header.items[1]?.external).toBe(false);
|
||||
expect(header.items[2]?.label).toBe("Docs");
|
||||
expect(header.items[2]?.href).toBe("/docs");
|
||||
expect(header.items[2]?.external).toBe(true);
|
||||
});
|
||||
|
||||
it("getHeader items have label, href, and external fields", async () => {
|
||||
const header = await repo.getHeader();
|
||||
for (const item of header.items) {
|
||||
expect(typeof item.label).toBe("string");
|
||||
expect(item.label.length).toBeGreaterThan(0);
|
||||
expect(typeof item.href).toBe("string");
|
||||
expect(item.href.length).toBeGreaterThan(0);
|
||||
expect(typeof item.external).toBe("boolean");
|
||||
}
|
||||
});
|
||||
|
||||
it("getHeader logoId is string or undefined", async () => {
|
||||
const header = await repo.getHeader();
|
||||
expect(
|
||||
header.logoId === undefined || typeof header.logoId === "string",
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
describe("span emission (R50)", () => {
|
||||
it("getHeader emits header.getHeader span with op=repository", async () => {
|
||||
if (!getTracer) return;
|
||||
const tracer = getTracer();
|
||||
tracer.reset();
|
||||
await repo.getHeader();
|
||||
const span = tracer.findSpan("header.getHeader");
|
||||
expect(span).toBeDefined();
|
||||
expect(span!.op).toBe("repository");
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@@ -12,7 +12,7 @@ describe("getHeaderUseCase", () => {
|
||||
expect(result.items[0]?.label).toBe("Home");
|
||||
});
|
||||
|
||||
it("(R25) throws ZodError when repository returns malformed header", async () => {
|
||||
it("throws ZodError when repository returns malformed header", async () => {
|
||||
const malformedRepo = {
|
||||
getHeader: async () =>
|
||||
({ items: [{ label: "", href: "/", external: false }] }) as never,
|
||||
|
||||
@@ -37,8 +37,12 @@ export async function bindDevSeedNavigation(ctx: BindContext): Promise<void> {
|
||||
if (navigationContainer.isBound(INSTRUMENTATION_SYMBOLS.LOGGER)) {
|
||||
navigationContainer.unbind(INSTRUMENTATION_SYMBOLS.LOGGER);
|
||||
}
|
||||
navigationContainer.bind<ITracer>(INSTRUMENTATION_SYMBOLS.TRACER).toConstantValue(tracer);
|
||||
navigationContainer.bind<ILogger>(INSTRUMENTATION_SYMBOLS.LOGGER).toConstantValue(logger);
|
||||
navigationContainer
|
||||
.bind<ITracer>(INSTRUMENTATION_SYMBOLS.TRACER)
|
||||
.toConstantValue(tracer);
|
||||
navigationContainer
|
||||
.bind<ILogger>(INSTRUMENTATION_SYMBOLS.LOGGER)
|
||||
.toConstantValue(logger);
|
||||
|
||||
if (navigationContainer.isBound(NAVIGATION_SYMBOLS.IHeaderRepository)) {
|
||||
navigationContainer.unbind(NAVIGATION_SYMBOLS.IHeaderRepository);
|
||||
@@ -54,7 +58,11 @@ export async function bindDevSeedNavigation(ctx: BindContext): Promise<void> {
|
||||
{ name: "navigation.getHeader", op: "use-case" },
|
||||
withCapture(
|
||||
logger,
|
||||
{ feature: "navigation", layer: "use-case", name: "navigation.getHeader" },
|
||||
{
|
||||
feature: "navigation",
|
||||
layer: "use-case",
|
||||
name: "navigation.getHeader",
|
||||
},
|
||||
getHeaderUseCase(repo),
|
||||
),
|
||||
);
|
||||
@@ -77,13 +85,16 @@ export async function bindDevSeedNavigation(ctx: BindContext): Promise<void> {
|
||||
{ name: "navigation.getHeader", op: "controller" },
|
||||
withCapture(
|
||||
logger,
|
||||
{ feature: "navigation", layer: "controller", name: "navigation.getHeader" },
|
||||
{
|
||||
feature: "navigation",
|
||||
layer: "controller",
|
||||
name: "navigation.getHeader",
|
||||
},
|
||||
getHeaderController(wrappedGetHeader),
|
||||
),
|
||||
),
|
||||
);
|
||||
// bus + queue are accept-and-forward in Phase 6; consumed by Phase 7 generator
|
||||
// output at the <gen:event-handlers> / <gen:jobs> anchors below.
|
||||
// bus + queue are passed through; generated handlers consume them at the anchors below.
|
||||
void bus;
|
||||
void queue;
|
||||
void realtime;
|
||||
|
||||
@@ -15,7 +15,8 @@ import { getHeaderUseCase } from "../application/use-cases/get-header.use-case";
|
||||
import { getHeaderController } from "../interface-adapters/controllers/get-header.controller";
|
||||
|
||||
export function bindProductionNavigation(ctx: BindProductionContext): void {
|
||||
const { config, tracer, logger, bus, queue, realtime, realtimeRegistry } = ctx;
|
||||
const { config, tracer, logger, bus, queue, realtime, realtimeRegistry } =
|
||||
ctx;
|
||||
|
||||
// Bind shared instrumentation into feature container
|
||||
if (navigationContainer.isBound(INSTRUMENTATION_SYMBOLS.TRACER)) {
|
||||
@@ -24,8 +25,12 @@ export function bindProductionNavigation(ctx: BindProductionContext): void {
|
||||
if (navigationContainer.isBound(INSTRUMENTATION_SYMBOLS.LOGGER)) {
|
||||
navigationContainer.unbind(INSTRUMENTATION_SYMBOLS.LOGGER);
|
||||
}
|
||||
navigationContainer.bind<ITracer>(INSTRUMENTATION_SYMBOLS.TRACER).toConstantValue(tracer);
|
||||
navigationContainer.bind<ILogger>(INSTRUMENTATION_SYMBOLS.LOGGER).toConstantValue(logger);
|
||||
navigationContainer
|
||||
.bind<ITracer>(INSTRUMENTATION_SYMBOLS.TRACER)
|
||||
.toConstantValue(tracer);
|
||||
navigationContainer
|
||||
.bind<ILogger>(INSTRUMENTATION_SYMBOLS.LOGGER)
|
||||
.toConstantValue(logger);
|
||||
|
||||
// Real repository
|
||||
if (navigationContainer.isBound(NAVIGATION_SYMBOLS.IHeaderRepository)) {
|
||||
@@ -42,7 +47,11 @@ export function bindProductionNavigation(ctx: BindProductionContext): void {
|
||||
{ name: "navigation.getHeader", op: "use-case" },
|
||||
withCapture(
|
||||
logger,
|
||||
{ feature: "navigation", layer: "use-case", name: "navigation.getHeader" },
|
||||
{
|
||||
feature: "navigation",
|
||||
layer: "use-case",
|
||||
name: "navigation.getHeader",
|
||||
},
|
||||
getHeaderUseCase(repo),
|
||||
),
|
||||
);
|
||||
@@ -66,13 +75,16 @@ export function bindProductionNavigation(ctx: BindProductionContext): void {
|
||||
{ name: "navigation.getHeader", op: "controller" },
|
||||
withCapture(
|
||||
logger,
|
||||
{ feature: "navigation", layer: "controller", name: "navigation.getHeader" },
|
||||
{
|
||||
feature: "navigation",
|
||||
layer: "controller",
|
||||
name: "navigation.getHeader",
|
||||
},
|
||||
getHeaderController(wrappedGetHeader),
|
||||
),
|
||||
),
|
||||
);
|
||||
// bus + queue are accept-and-forward in Phase 6; consumed by Phase 7 generator
|
||||
// output at the <gen:event-handlers> / <gen:jobs> anchors below.
|
||||
// bus + queue are passed through; generated handlers consume them at the anchors below.
|
||||
void bus;
|
||||
void queue;
|
||||
void realtime;
|
||||
|
||||
@@ -3,7 +3,7 @@ export type { NavigationRouter } from "./integrations/api/router";
|
||||
export { HeaderNotFoundError } from "./entities/errors/header";
|
||||
export { InputParseError } from "./entities/errors/common";
|
||||
|
||||
// Use case schemas + types (Plan 9 R18)
|
||||
// Use case schemas + types
|
||||
export {
|
||||
getHeaderInputSchema,
|
||||
getHeaderOutputSchema,
|
||||
@@ -17,4 +17,7 @@ export type { IGetHeaderController } from "./interface-adapters/controllers/get-
|
||||
|
||||
// <gen:events>
|
||||
// <gen:realtime-channels>
|
||||
export { navigationManifest, type NavigationManifest } from "./feature.manifest";
|
||||
export {
|
||||
navigationManifest,
|
||||
type NavigationManifest,
|
||||
} from "./feature.manifest";
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { RecordingTracer, RecordingLogger } from "@repo/core-testing/instrumentation";
|
||||
import {
|
||||
RecordingTracer,
|
||||
RecordingLogger,
|
||||
} from "@repo/core-testing/instrumentation";
|
||||
import { MockHeaderRepository } from "@/infrastructure/repositories/header.repository.mock";
|
||||
|
||||
// Mock repo also wraps in spans (R42).
|
||||
describe("MockHeaderRepository emits spans (R42)", () => {
|
||||
// Mock repo also wraps in spans.
|
||||
describe("MockHeaderRepository emits spans", () => {
|
||||
it("getHeader emits one span with op='repository'", async () => {
|
||||
const tracer = new RecordingTracer();
|
||||
const logger = new RecordingLogger();
|
||||
|
||||
@@ -30,7 +30,7 @@ describe("navigationRouter", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("navigationRouter (R26 error mapping)", () => {
|
||||
describe("navigationRouter error mapping", () => {
|
||||
beforeEach(() => {
|
||||
navigationContainer.unbindAll();
|
||||
navigationContainer.load(NavigationModule);
|
||||
@@ -43,7 +43,10 @@ describe("navigationRouter (R26 error mapping)", () => {
|
||||
it("translates InputParseError → BAD_REQUEST when extra fields are passed", async () => {
|
||||
const caller = navigationRouter.createCaller({});
|
||||
try {
|
||||
await caller.header({ unexpected: "field" } as unknown as Record<string, never>);
|
||||
await caller.header({ unexpected: "field" } as unknown as Record<
|
||||
string,
|
||||
never
|
||||
>);
|
||||
throw new Error("expected throw");
|
||||
} catch (e) {
|
||||
expect(e).toBeInstanceOf(TRPCError);
|
||||
@@ -66,12 +69,16 @@ describe("navigationRouter (R26 error mapping)", () => {
|
||||
navigationContainer
|
||||
.bind(NAVIGATION_SYMBOLS.IGetHeaderUseCase)
|
||||
.toDynamicValue((ctx) =>
|
||||
getHeaderUseCase(ctx.container.get(NAVIGATION_SYMBOLS.IHeaderRepository)),
|
||||
getHeaderUseCase(
|
||||
ctx.container.get(NAVIGATION_SYMBOLS.IHeaderRepository),
|
||||
),
|
||||
);
|
||||
navigationContainer
|
||||
.bind(NAVIGATION_SYMBOLS.IGetHeaderController)
|
||||
.toDynamicValue((ctx) =>
|
||||
getHeaderController(ctx.container.get(NAVIGATION_SYMBOLS.IGetHeaderUseCase)),
|
||||
getHeaderController(
|
||||
ctx.container.get(NAVIGATION_SYMBOLS.IGetHeaderUseCase),
|
||||
),
|
||||
);
|
||||
|
||||
const caller = navigationRouter.createCaller({});
|
||||
|
||||
Reference in New Issue
Block a user