feat(core-shared): extend wireUseCase with analytics arg and Analyzed brand propagation
Add AnalyticsProtocol to bind-protocols, extend WireUseCaseOptions with optional analytics field, and compose the __analyzed brand inline in wireUseCase (innermost, before withAudit) when analytics is provided. Propagate __analyzed through withCapture and withSpan PROPAGATED_BRANDS so the outermost container binding carries the brand for boot-time assertion checks (Story 05). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
||||
isInstrumented,
|
||||
isCaptured,
|
||||
isAudited,
|
||||
isAnalyzed,
|
||||
} from "@/conformance/brand-runtime";
|
||||
import type {
|
||||
ITracer,
|
||||
@@ -13,7 +14,7 @@ import type {
|
||||
SpanOpts,
|
||||
} from "@/instrumentation/tracer.interface";
|
||||
import type { ILogger } from "@/instrumentation/logger.interface";
|
||||
import type { AuditLogProtocol } from "@/di/bind-protocols";
|
||||
import type { AuditLogProtocol, AnalyticsProtocol } from "@/di/bind-protocols";
|
||||
|
||||
function makeTracer() {
|
||||
const calls: SpanOpts[] = [];
|
||||
@@ -42,6 +43,10 @@ function makeAuditLog(): AuditLogProtocol {
|
||||
return { record: vi.fn() };
|
||||
}
|
||||
|
||||
function makeAnalytics(): AnalyticsProtocol {
|
||||
return { track: vi.fn() };
|
||||
}
|
||||
|
||||
const doubleFactory = () => async (x: number) => x * 2;
|
||||
|
||||
describe("wireUseCase — no-audit path", () => {
|
||||
@@ -241,6 +246,107 @@ describe("wireUseCase — container binding", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("wireUseCase — analytics path", () => {
|
||||
it("attaches Analyzed brand when analytics is provided", () => {
|
||||
const { tracer } = makeTracer();
|
||||
const logger = makeLogger();
|
||||
const container = new Container();
|
||||
const sym = Symbol("test.double");
|
||||
const analytics = makeAnalytics();
|
||||
|
||||
const wired = wireUseCase({
|
||||
container,
|
||||
symbol: sym,
|
||||
factory: doubleFactory,
|
||||
deps: [],
|
||||
feature: "test",
|
||||
layer: "use-case",
|
||||
name: "double",
|
||||
tracer,
|
||||
logger,
|
||||
analytics,
|
||||
});
|
||||
|
||||
expect(isAnalyzed(wired)).toBe(true);
|
||||
expect(isInstrumented(wired)).toBe(true);
|
||||
expect(isCaptured(wired)).toBe(true);
|
||||
});
|
||||
|
||||
it("attaches Analyzed + Audited brands when both analytics and auditLog are provided", () => {
|
||||
const { tracer } = makeTracer();
|
||||
const logger = makeLogger();
|
||||
const container = new Container();
|
||||
const sym = Symbol("test.double");
|
||||
const analytics = makeAnalytics();
|
||||
const auditLog = makeAuditLog();
|
||||
|
||||
const wired = wireUseCase({
|
||||
container,
|
||||
symbol: sym,
|
||||
factory: doubleFactory,
|
||||
deps: [],
|
||||
feature: "test",
|
||||
layer: "use-case",
|
||||
name: "double",
|
||||
tracer,
|
||||
logger,
|
||||
analytics,
|
||||
auditLog,
|
||||
});
|
||||
|
||||
expect(isAnalyzed(wired)).toBe(true);
|
||||
expect(isAudited(wired)).toBe(true);
|
||||
expect(isInstrumented(wired)).toBe(true);
|
||||
expect(isCaptured(wired)).toBe(true);
|
||||
});
|
||||
|
||||
it("executes the factory result on invocation (analytics path)", async () => {
|
||||
const { tracer } = makeTracer();
|
||||
const logger = makeLogger();
|
||||
const container = new Container();
|
||||
const sym = Symbol("test.double");
|
||||
const analytics = makeAnalytics();
|
||||
|
||||
const wired = wireUseCase({
|
||||
container,
|
||||
symbol: sym,
|
||||
factory: doubleFactory,
|
||||
deps: [],
|
||||
feature: "test",
|
||||
layer: "use-case",
|
||||
name: "double",
|
||||
tracer,
|
||||
logger,
|
||||
analytics,
|
||||
});
|
||||
|
||||
await expect(wired(7)).resolves.toBe(14);
|
||||
});
|
||||
});
|
||||
|
||||
describe("wireUseCase — no-analytics path", () => {
|
||||
it("does not attach Analyzed brand when analytics is absent", () => {
|
||||
const { tracer } = makeTracer();
|
||||
const logger = makeLogger();
|
||||
const container = new Container();
|
||||
const sym = Symbol("test.double");
|
||||
|
||||
const wired = wireUseCase({
|
||||
container,
|
||||
symbol: sym,
|
||||
factory: doubleFactory,
|
||||
deps: [],
|
||||
feature: "test",
|
||||
layer: "use-case",
|
||||
name: "double",
|
||||
tracer,
|
||||
logger,
|
||||
});
|
||||
|
||||
expect(isAnalyzed(wired)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("wireUseCase — idempotent re-bind", () => {
|
||||
it("unbinds the existing binding and replaces it when the symbol is already bound", () => {
|
||||
const { tracer } = makeTracer();
|
||||
|
||||
Reference in New Issue
Block a user