Adds IRateLimit, RateLimitBudget, RateLimitDecision to a new rate-limit sub-module; adds RateLimited<F> brand and isRateLimited predicate following the Captured/ConsentChecked pattern; extends UseCaseManifest with rateLimit?: readonly RateLimitBudget[] so features can declare rate-limit gates in their manifests. Exports new types from @repo/core-shared/rate-limit and @repo/core-shared/conformance. Blocks stories 02, 03, and 04. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
307 lines
8.3 KiB
TypeScript
307 lines
8.3 KiB
TypeScript
import { describe, it, expect, expectTypeOf } from "vitest";
|
|
import {
|
|
defineFeature,
|
|
type FeatureManifest,
|
|
type UseCaseManifest,
|
|
} from "@/conformance/define-feature";
|
|
import type { RateLimitBudget } from "@/rate-limit/rate-limit.interface";
|
|
import {
|
|
DEFAULT_COVERAGE_BANDS,
|
|
DEFAULT_MUTATION_SCORE,
|
|
DEFAULT_MUTATION_TARGETS,
|
|
getCoverageBands,
|
|
getMutationConfig,
|
|
vitestThresholdsFromBands,
|
|
vitestThresholdsFromManifest,
|
|
type CoverageBand,
|
|
} from "@/conformance/coverage";
|
|
|
|
describe("defineFeature", () => {
|
|
it("preserves literal types of a manifest declared with `as const`", () => {
|
|
const manifest = defineFeature({
|
|
name: "auth",
|
|
requiredCores: ["audit"],
|
|
useCases: {
|
|
signIn: {
|
|
mutates: false,
|
|
audits: [],
|
|
publishes: [],
|
|
consumes: [],
|
|
},
|
|
signUp: {
|
|
mutates: true,
|
|
audits: ["user.created"],
|
|
publishes: ["auth.signed-up"],
|
|
consumes: [],
|
|
},
|
|
},
|
|
realtimeChannels: [],
|
|
jobs: [],
|
|
} as const);
|
|
|
|
// Literal preservation: name is the literal "auth", not string
|
|
expectTypeOf(manifest.name).toEqualTypeOf<"auth">();
|
|
// Use-case keys preserved
|
|
expectTypeOf(manifest.useCases.signUp.audits).toEqualTypeOf<
|
|
readonly ["user.created"]
|
|
>();
|
|
expectTypeOf(manifest.useCases.signUp.mutates).toEqualTypeOf<true>();
|
|
expectTypeOf(manifest.useCases.signIn.mutates).toEqualTypeOf<false>();
|
|
});
|
|
|
|
it("FeatureManifest type accepts the shape", () => {
|
|
const manifest = defineFeature({
|
|
name: "blog",
|
|
requiredCores: [],
|
|
useCases: {},
|
|
realtimeChannels: [],
|
|
jobs: [],
|
|
} as const);
|
|
expectTypeOf(manifest).toMatchTypeOf<FeatureManifest>();
|
|
});
|
|
|
|
it("accepts an optional rateLimit array on a use case", () => {
|
|
const manifest = defineFeature({
|
|
name: "auth",
|
|
requiredCores: [],
|
|
useCases: {
|
|
signIn: {
|
|
mutates: false,
|
|
audits: [],
|
|
publishes: [],
|
|
consumes: [],
|
|
rateLimit: [{ name: "login", window: "1m", budget: 5 }],
|
|
},
|
|
},
|
|
realtimeChannels: [],
|
|
jobs: [],
|
|
} as const);
|
|
|
|
expectTypeOf(manifest).toMatchTypeOf<FeatureManifest>();
|
|
expectTypeOf(manifest.useCases.signIn.rateLimit).toMatchTypeOf<
|
|
readonly RateLimitBudget[] | undefined
|
|
>();
|
|
});
|
|
|
|
it("use case without rateLimit satisfies UseCaseManifest", () => {
|
|
const uc: UseCaseManifest = {
|
|
mutates: false,
|
|
audits: [],
|
|
publishes: [],
|
|
consumes: [],
|
|
};
|
|
expectTypeOf(uc.rateLimit).toEqualTypeOf<
|
|
readonly RateLimitBudget[] | undefined
|
|
>();
|
|
});
|
|
|
|
it("accepts an optional coverage section", () => {
|
|
const manifest = defineFeature({
|
|
name: "auth",
|
|
requiredCores: [],
|
|
useCases: {},
|
|
realtimeChannels: [],
|
|
jobs: [],
|
|
coverage: {
|
|
bands: {
|
|
baseline: { statements: 80, branches: 75, functions: 80, lines: 80 },
|
|
entities: {
|
|
statements: 100,
|
|
branches: 100,
|
|
functions: 100,
|
|
lines: 100,
|
|
},
|
|
},
|
|
mutationTargets: ["entities"],
|
|
mutationScore: 85,
|
|
},
|
|
} as const);
|
|
|
|
expectTypeOf(manifest.coverage).toMatchTypeOf<
|
|
| undefined
|
|
| {
|
|
readonly bands: {
|
|
readonly baseline: CoverageBand;
|
|
readonly entities?: CoverageBand;
|
|
readonly "use-cases"?: CoverageBand;
|
|
readonly controllers?: CoverageBand;
|
|
};
|
|
}
|
|
>();
|
|
expect(manifest.coverage?.mutationScore).toBe(85);
|
|
});
|
|
});
|
|
|
|
describe("getCoverageBands", () => {
|
|
const blankManifest: FeatureManifest = {
|
|
name: "test",
|
|
requiredCores: [],
|
|
useCases: {},
|
|
realtimeChannels: [],
|
|
jobs: [],
|
|
};
|
|
|
|
it("returns DEFAULT_COVERAGE_BANDS when manifest has no coverage section", () => {
|
|
expect(getCoverageBands(blankManifest)).toEqual(DEFAULT_COVERAGE_BANDS);
|
|
});
|
|
|
|
it("returns DEFAULT_COVERAGE_BANDS when coverage section is undefined", () => {
|
|
expect(getCoverageBands({ ...blankManifest, coverage: undefined })).toEqual(
|
|
DEFAULT_COVERAGE_BANDS,
|
|
);
|
|
});
|
|
|
|
it("uses declared baseline + fills missing layers from defaults", () => {
|
|
const result = getCoverageBands({
|
|
...blankManifest,
|
|
coverage: {
|
|
bands: {
|
|
baseline: { statements: 90, branches: 85, functions: 90, lines: 90 },
|
|
},
|
|
},
|
|
});
|
|
expect(result.baseline).toEqual({
|
|
statements: 90,
|
|
branches: 85,
|
|
functions: 90,
|
|
lines: 90,
|
|
});
|
|
expect(result.entities).toEqual(DEFAULT_COVERAGE_BANDS.entities);
|
|
expect(result["use-cases"]).toEqual(DEFAULT_COVERAGE_BANDS["use-cases"]);
|
|
expect(result.controllers).toEqual(DEFAULT_COVERAGE_BANDS.controllers);
|
|
});
|
|
|
|
it("uses declared layer bands when present", () => {
|
|
const customEntities: CoverageBand = {
|
|
statements: 95,
|
|
branches: 90,
|
|
functions: 95,
|
|
lines: 95,
|
|
};
|
|
const result = getCoverageBands({
|
|
...blankManifest,
|
|
coverage: {
|
|
bands: {
|
|
baseline: DEFAULT_COVERAGE_BANDS.baseline,
|
|
entities: customEntities,
|
|
},
|
|
},
|
|
});
|
|
expect(result.entities).toEqual(customEntities);
|
|
});
|
|
});
|
|
|
|
describe("getMutationConfig", () => {
|
|
const blankManifest: FeatureManifest = {
|
|
name: "test",
|
|
requiredCores: [],
|
|
useCases: {},
|
|
realtimeChannels: [],
|
|
jobs: [],
|
|
};
|
|
|
|
it("returns defaults when manifest has no coverage section", () => {
|
|
const config = getMutationConfig(blankManifest);
|
|
expect(config.targets).toEqual(DEFAULT_MUTATION_TARGETS);
|
|
expect(config.score).toBe(DEFAULT_MUTATION_SCORE);
|
|
});
|
|
|
|
it("honors declared mutationTargets + mutationScore", () => {
|
|
const config = getMutationConfig({
|
|
...blankManifest,
|
|
coverage: {
|
|
bands: { baseline: DEFAULT_COVERAGE_BANDS.baseline },
|
|
mutationTargets: ["entities"],
|
|
mutationScore: 90,
|
|
},
|
|
});
|
|
expect(config.targets).toEqual(["entities"]);
|
|
expect(config.score).toBe(90);
|
|
});
|
|
});
|
|
|
|
describe("DEFAULT_COVERAGE_BANDS", () => {
|
|
it("matches the ADR-011 / ADR-020 documented bands", () => {
|
|
expect(DEFAULT_COVERAGE_BANDS.baseline).toEqual({
|
|
statements: 80,
|
|
branches: 75,
|
|
functions: 80,
|
|
lines: 80,
|
|
});
|
|
expect(DEFAULT_COVERAGE_BANDS.entities).toEqual({
|
|
statements: 100,
|
|
branches: 100,
|
|
functions: 100,
|
|
lines: 100,
|
|
});
|
|
expect(DEFAULT_COVERAGE_BANDS["use-cases"]).toEqual({
|
|
statements: 100,
|
|
branches: 95,
|
|
functions: 100,
|
|
lines: 100,
|
|
});
|
|
expect(DEFAULT_COVERAGE_BANDS.controllers).toEqual({
|
|
statements: 100,
|
|
branches: 95,
|
|
functions: 100,
|
|
lines: 100,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("vitestThresholdsFromBands", () => {
|
|
it("emits baseline at the top level and each declared layer under its glob", () => {
|
|
const result = vitestThresholdsFromBands(DEFAULT_COVERAGE_BANDS);
|
|
expect(result.statements).toBe(80);
|
|
expect(result.branches).toBe(75);
|
|
expect(result["src/entities/**"]).toEqual({
|
|
statements: 100,
|
|
branches: 100,
|
|
functions: 100,
|
|
lines: 100,
|
|
});
|
|
expect(result["src/application/use-cases/**"]).toEqual({
|
|
statements: 100,
|
|
branches: 95,
|
|
functions: 100,
|
|
lines: 100,
|
|
});
|
|
expect(result["src/interface-adapters/controllers/**"]).toEqual({
|
|
statements: 100,
|
|
branches: 95,
|
|
functions: 100,
|
|
lines: 100,
|
|
});
|
|
});
|
|
|
|
it("omits layer glob when its band is undefined", () => {
|
|
const result = vitestThresholdsFromBands({
|
|
baseline: DEFAULT_COVERAGE_BANDS.baseline,
|
|
entities: DEFAULT_COVERAGE_BANDS.entities,
|
|
});
|
|
expect(result["src/entities/**"]).toBeDefined();
|
|
expect(result["src/application/use-cases/**"]).toBeUndefined();
|
|
expect(result["src/interface-adapters/controllers/**"]).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe("vitestThresholdsFromManifest", () => {
|
|
it("uses DEFAULT_COVERAGE_BANDS when the manifest omits coverage", () => {
|
|
const manifest: FeatureManifest = {
|
|
name: "test",
|
|
requiredCores: [],
|
|
useCases: {},
|
|
realtimeChannels: [],
|
|
jobs: [],
|
|
};
|
|
const result = vitestThresholdsFromManifest(manifest);
|
|
expect(result.statements).toBe(80);
|
|
expect(result["src/entities/**"]).toEqual({
|
|
statements: 100,
|
|
branches: 100,
|
|
functions: 100,
|
|
lines: 100,
|
|
});
|
|
});
|
|
});
|