Skip to content

Commit d4dc940

Browse files
authored
refactor(test): keep coverage at behavioral boundaries (#474)
* refactor(test): keep coverage at behavioral boundaries * fix(ci): restore package workflow temp-root classification
1 parent d65730c commit d4dc940

117 files changed

Lines changed: 1519 additions & 2419 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/testing.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ registries but do not cross production filesystem, process, socket, or browser b
2525
Boundary tests cross one production boundary. Only acceptance tests assemble
2626
the complete runtime or invoke the compiled product surface.
2727

28-
Focused immutable builders shared by a domain test family live beside their
29-
production owner as `src/domain/*.fixture.ts`. They are typechecked with the
30-
suite and excluded from package builds; broader runtime and provider fixtures
31-
remain under `tests/fixtures/**`.
28+
Focused immutable builders and recording ports shared by one test family live
29+
beside their production owner as `src/**/*.fixture.ts`. They are typechecked
30+
with the suite and excluded from package builds; broader runtime and provider
31+
fixtures remain under `tests/fixtures/**`.
3232

3333
`tests/process-global/**` is reserved for cases with a demonstrated dependency
3434
on process-global state. Those tests run without file parallelism. Reusable,
@@ -70,8 +70,10 @@ Local full-suite Vitest runs are intentionally capped at one worker and one
7070
project at a time. Boundary fixtures own real subprocesses, and this local cap
7171
keeps aggregate memory predictable; CI retains the existing two-worker budget.
7272
The pure domain/contracts and recording-port service projects share one worker
73-
module context because their tests own no mutable runtime resources; adapter,
74-
composition, and boundary projects retain per-file isolation.
73+
module context because their tests own no mutable runtime resources. MCP
74+
boundary files also share the immutable server module graph while creating and
75+
closing independent in-memory sessions. Adapter, composition, acceptance,
76+
process-global, and other boundary projects retain per-file isolation.
7577
`npm test`, `npm run docs:check`, and `npm run docs:generate` share
7678
repository-local locks and fail fast when the same class of command is already
7779
running. The `npm test` build is inside that lock. `check:pr` runs its test task

knip.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"$schema": "./node_modules/knip/schema.json",
3-
"entry": ["src/main.ts", "src/cli.ts"],
3+
"entry": ["src/main.ts", "src/cli.ts", "src/contracts/errorSchemas.ts"],
44
"project": ["src/**/*.ts", "tests/**/*.ts", "scripts/**/*.mjs"],
55
"ignoreDependencies": ["electron"],
66
"ignoreBinaries": ["ps"],

tests/composition/analysis-sessions/analysisProviderRegistry.test.ts renamed to src/application/AnalysisProviderRegistry.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { describe, expect, it } from "vitest";
33
import {
44
AnalysisProviderRegistry,
55
type AnalysisProviderBinding,
6-
} from "../../../src/application/AnalysisProviderRegistry.js";
6+
} from "./AnalysisProviderRegistry.js";
77
import {
88
createAnalysisExecution,
99
type AnalysisClient,
@@ -12,15 +12,15 @@ import {
1212
type ProviderAvailability,
1313
type ProviderIdentity,
1414
type ProviderTargetSupport,
15-
} from "../../../src/application/AnalysisProvider.js";
16-
import { createAnalysisProfile } from "../../../src/domain/analysisProfile.js";
17-
import type { BinaryTarget } from "../../../src/domain/binaryTarget.js";
15+
} from "./AnalysisProvider.js";
16+
import { createAnalysisProfile } from "../domain/analysisProfile.js";
17+
import type { BinaryTarget } from "../domain/binaryTarget.js";
1818
import {
1919
ProviderAdapterError,
2020
ProviderSelectionError,
2121
projectAnalysisError,
22-
} from "../../../src/domain/errors.js";
23-
import { err, ok } from "../../../src/domain/result.js";
22+
} from "../domain/errors.js";
23+
import { err, ok } from "../domain/result.js";
2424

2525
const DATABASE_TARGET: BinaryTarget = {
2626
path: "/tmp/fixture.hop",

src/application/Doctor.fixture.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { CATALOG_IDENTITY } from "../catalogIdentity.js";
2+
import { PRODUCT_IDENTITY } from "../identity.js";
3+
import type { DoctorHost } from "./Doctor.js";
4+
5+
/** Healthy recording host for focused doctor and projection tests. */
6+
export const createDoctorHostFixture = (
7+
overrides: Partial<DoctorHost> = {},
8+
): DoctorHost => ({
9+
platform: "darwin",
10+
architecture: "x64",
11+
nodeVersion: "24.18.0",
12+
macosVersion: () => Promise.resolve("14.0"),
13+
linuxDistribution: () => Promise.resolve(undefined),
14+
validTarget: (path) => Promise.resolve(path.includes("Hopper")),
15+
executable: (path) => Promise.resolve(path.includes("Hopper")),
16+
supportedLinuxHopper: () => Promise.resolve(true),
17+
linuxDemoRuntimeCheck: () =>
18+
Promise.resolve({
19+
name: "hopper-demo-runtime",
20+
ok: true,
21+
classification: "healthy",
22+
}),
23+
brewHopperPath: () => Promise.resolve(undefined),
24+
manualHopperPaths: () => Promise.resolve([]),
25+
installedSkillIdentity: () =>
26+
Promise.resolve({
27+
version: PRODUCT_IDENTITY.skillVersion,
28+
toolCount: CATALOG_IDENTITY.counts.mcp_tools,
29+
catalogDigest: CATALOG_IDENTITY.digests.combined_sha256,
30+
}),
31+
...overrides,
32+
});

tests/boundary/cli/doctorRuntimeExecutables.test.ts renamed to src/application/Doctor.runtimeExecutables.test.ts

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,7 @@
11
import { describe, expect, it } from "vitest";
22

3-
import { runDoctor, type DoctorHost } from "../../../src/application/Doctor.js";
4-
import { CATALOG_IDENTITY } from "../../../src/catalogIdentity.js";
5-
import { PRODUCT_IDENTITY } from "../../../src/identity.js";
6-
7-
const host = (overrides: Partial<DoctorHost> = {}): DoctorHost => ({
8-
platform: "darwin",
9-
architecture: "x64",
10-
nodeVersion: "24.18.0",
11-
macosVersion: () => Promise.resolve("12.0"),
12-
linuxDistribution: () => Promise.resolve(undefined),
13-
validTarget: (path) => Promise.resolve(path.includes("Hopper")),
14-
executable: (path) => Promise.resolve(path.includes("Hopper")),
15-
supportedLinuxHopper: () => Promise.resolve(true),
16-
linuxDemoRuntimeCheck: () =>
17-
Promise.resolve({
18-
name: "hopper-demo-runtime",
19-
ok: true,
20-
classification: "healthy",
21-
}),
22-
brewHopperPath: () => Promise.resolve(undefined),
23-
manualHopperPaths: () => Promise.resolve([]),
24-
installedSkillIdentity: () =>
25-
Promise.resolve({
26-
version: PRODUCT_IDENTITY.skillVersion,
27-
toolCount: CATALOG_IDENTITY.counts.mcp_tools,
28-
catalogDigest: CATALOG_IDENTITY.digests.combined_sha256,
29-
}),
30-
...overrides,
31-
});
3+
import { createDoctorHostFixture as host } from "./Doctor.fixture.js";
4+
import { runDoctor } from "./Doctor.js";
325

336
describe("doctor runtime executable diagnostics", () => {
347
it("reports a broken shadowed Node candidate without hiding the healthy launcher", async () => {
Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,7 @@
11
import { describe, expect, it } from "vitest";
2-
import type { ClientRegistrationStatus } from "../../../src/application/ClientRegistrationStatus.js";
3-
import { runDoctor, type DoctorHost } from "../../../src/application/Doctor.js";
4-
import { CATALOG_IDENTITY } from "../../../src/catalogIdentity.js";
5-
import { PRODUCT_IDENTITY } from "../../../src/identity.js";
6-
7-
const host = (overrides: Partial<DoctorHost> = {}): DoctorHost => ({
8-
platform: "darwin",
9-
architecture: "x64",
10-
nodeVersion: "24.18.0",
11-
macosVersion: () => Promise.resolve("12.0"),
12-
linuxDistribution: () => Promise.resolve(undefined),
13-
validTarget: (path) => Promise.resolve(path.includes("Hopper")),
14-
executable: (path) => Promise.resolve(path.includes("Hopper")),
15-
supportedLinuxHopper: () => Promise.resolve(true),
16-
linuxDemoRuntimeCheck: () =>
17-
Promise.resolve({
18-
name: "hopper-demo-runtime",
19-
ok: true,
20-
classification: "healthy",
21-
}),
22-
brewHopperPath: () => Promise.resolve(undefined),
23-
manualHopperPaths: () => Promise.resolve([]),
24-
installedSkillIdentity: () =>
25-
Promise.resolve({
26-
version: PRODUCT_IDENTITY.skillVersion,
27-
toolCount: CATALOG_IDENTITY.counts.mcp_tools,
28-
catalogDigest: CATALOG_IDENTITY.digests.combined_sha256,
29-
}),
30-
...overrides,
31-
});
2+
import type { ClientRegistrationStatus } from "./ClientRegistrationStatus.js";
3+
import { createDoctorHostFixture as host } from "./Doctor.fixture.js";
4+
import { runDoctor } from "./Doctor.js";
325

336
describe("doctor", () => {
347
it("returns exact recovery for every failed diagnostic", async () => {

tests/boundary/providers/hopper/linuxHopper.test.ts renamed to src/application/LinuxHopper.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
type LinuxHopperInstallHost,
1313
type LinuxHopperLauncherStatus,
1414
type LinuxPackageFamily,
15-
} from "../../../../src/application/LinuxHopper.js";
15+
} from "./LinuxHopper.js";
1616

1717
class RecordingLinuxHost implements LinuxHopperInstallHost {
1818
distributionValue: LinuxDistribution | undefined = {

tests/boundary/providers/hopper/macHopper.test.ts renamed to src/application/MacHopper.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
installMacHopper,
77
macHopperInstallDisclosure,
88
type MacHopperInstallHost,
9-
} from "../../../../src/application/MacHopper.js";
9+
} from "./MacHopper.js";
1010

1111
class FakeMacHopperHost implements MacHopperInstallHost {
1212
readonly archive = new TextEncoder().encode("hopper-dmg");

tests/boundary/providers/managed/managedReconstruction.test.ts renamed to src/application/ManagedReconstructionService.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ import { createHash } from "node:crypto";
22

33
import { describe, expect, it } from "vitest";
44

5-
import { importManagedReconstructionEvidence } from "../../../../src/application/ManagedReconstructionService.js";
6-
import { MANAGED_RECONSTRUCTION_IMPORT_EXAMPLE } from "../../../../src/contracts/managedWorkflowExamples.js";
5+
import { MANAGED_RECONSTRUCTION_IMPORT_EXAMPLE } from "../contracts/managedWorkflowExamples.js";
76
import {
87
importManagedReconstruction,
98
managedReconstructionImportInputSchema,
10-
} from "../../../../src/domain/managedReconstruction.js";
11-
import { managedMemberInspectionSchema } from "../../../../src/domain/managedArtifact.js";
12-
import { createEvidence } from "../../../../src/domain/evidence.js";
9+
} from "../domain/managedReconstruction.js";
10+
import { managedMemberInspectionSchema } from "../domain/managedArtifact.js";
11+
import { createEvidence } from "../domain/evidence.js";
12+
import { importManagedReconstructionEvidence } from "./ManagedReconstructionService.js";
1313

1414
const exampleInput = () =>
1515
managedReconstructionImportInputSchema.parse(

tests/boundary/providers/native/nativeApiInspection.test.ts renamed to src/application/NativeApiInspection.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { describe, expect, it } from "vitest";
22

3-
import { projectNativeApiInspection } from "../../../../src/application/NativeApiInspection.js";
4-
import { functionDossierSchema } from "../../../../src/domain/hopperValues.js";
5-
import { nativeApiBoundarySchema } from "../../../../src/domain/nativeApiBoundary.js";
6-
import { ghidraFunctionDossier } from "../../../fixtures/ghidraFunction.js";
3+
import { functionDossierSchema } from "../domain/hopperValues.js";
4+
import { ghidraFunctionDossier } from "../domain/hopperValues.fixture.js";
5+
import { nativeApiBoundarySchema } from "../domain/nativeApiBoundary.js";
6+
import { projectNativeApiInspection } from "./NativeApiInspection.js";
77

88
describe("native API inspection", () => {
99
it("projects structured boundary evidence through inspectable substeps", () => {

0 commit comments

Comments
 (0)