@vitest-agent/mcp 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/vitest-agent-mcp.js +2 -2
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/package.json +2 -2
- package/public/patterns/_meta.json +67 -0
- package/public/patterns/authoring-a-custom-vitest-agent-reporter.md +82 -0
- package/public/patterns/known-issues-and-caveats.md +52 -0
- package/public/patterns/operating-vitest-agent-as-an-agent.md +53 -0
- package/public/patterns/running-tests-via-mcp.md +58 -0
- package/public/patterns/silencing-leaking-output-in-tests.md +91 -0
- package/public/patterns/testing-effect-schema-definitions.md +71 -0
- package/public/patterns/testing-effect-services-with-mock-layers.md +63 -0
- package/public/vendor/vitest-docs/ATTRIBUTION.md +5 -0
- package/public/vendor/vitest-docs/api/advanced/artifacts.md +189 -0
- package/public/vendor/vitest-docs/api/advanced/metadata.md +68 -0
- package/public/vendor/vitest-docs/api/advanced/plugin.md +168 -0
- package/public/vendor/vitest-docs/api/advanced/reporters.md +342 -0
- package/public/vendor/vitest-docs/api/advanced/runner.md +334 -0
- package/public/vendor/vitest-docs/api/advanced/test-case.md +302 -0
- package/public/vendor/vitest-docs/api/advanced/test-collection.md +89 -0
- package/public/vendor/vitest-docs/api/advanced/test-module.md +140 -0
- package/public/vendor/vitest-docs/api/advanced/test-project.md +321 -0
- package/public/vendor/vitest-docs/api/advanced/test-specification.md +96 -0
- package/public/vendor/vitest-docs/api/advanced/test-suite.md +230 -0
- package/public/vendor/vitest-docs/api/advanced/vitest.md +684 -0
- package/public/vendor/vitest-docs/api/assert-type.md +22 -0
- package/public/vendor/vitest-docs/api/assert.md +1960 -0
- package/public/vendor/vitest-docs/api/browser/assertions.md +1277 -0
- package/public/vendor/vitest-docs/api/browser/commands.md +154 -0
- package/public/vendor/vitest-docs/api/browser/context.md +338 -0
- package/public/vendor/vitest-docs/api/browser/interactivity.md +681 -0
- package/public/vendor/vitest-docs/api/browser/locators.md +1171 -0
- package/public/vendor/vitest-docs/api/browser/react.md +346 -0
- package/public/vendor/vitest-docs/api/browser/svelte.md +292 -0
- package/public/vendor/vitest-docs/api/browser/vue.md +222 -0
- package/public/vendor/vitest-docs/api/describe.md +374 -0
- package/public/vendor/vitest-docs/api/expect-typeof.md +571 -0
- package/public/vendor/vitest-docs/api/expect.md +2304 -0
- package/public/vendor/vitest-docs/api/hooks.md +463 -0
- package/public/vendor/vitest-docs/api/mock.md +701 -0
- package/public/vendor/vitest-docs/api/test.md +926 -0
- package/public/vendor/vitest-docs/api/vi.md +1372 -0
- package/public/vendor/vitest-docs/config/alias.md +13 -0
- package/public/vendor/vitest-docs/config/allowonly.md +32 -0
- package/public/vendor/vitest-docs/config/api.md +27 -0
- package/public/vendor/vitest-docs/config/attachmentsdir.md +6 -0
- package/public/vendor/vitest-docs/config/bail.md +9 -0
- package/public/vendor/vitest-docs/config/benchmark.md +65 -0
- package/public/vendor/vitest-docs/config/browser/api.md +23 -0
- package/public/vendor/vitest-docs/config/browser/commands.md +6 -0
- package/public/vendor/vitest-docs/config/browser/connecttimeout.md +10 -0
- package/public/vendor/vitest-docs/config/browser/detailspanelposition.md +38 -0
- package/public/vendor/vitest-docs/config/browser/enabled.md +40 -0
- package/public/vendor/vitest-docs/config/browser/expect.md +250 -0
- package/public/vendor/vitest-docs/config/browser/headless.md +7 -0
- package/public/vendor/vitest-docs/config/browser/instances.md +47 -0
- package/public/vendor/vitest-docs/config/browser/isolate.md +11 -0
- package/public/vendor/vitest-docs/config/browser/locators.md +24 -0
- package/public/vendor/vitest-docs/config/browser/orchestratorscripts.md +39 -0
- package/public/vendor/vitest-docs/config/browser/playwright.md +214 -0
- package/public/vendor/vitest-docs/config/browser/preview.md +32 -0
- package/public/vendor/vitest-docs/config/browser/provider.md +79 -0
- package/public/vendor/vitest-docs/config/browser/screenshotdirectory.md +6 -0
- package/public/vendor/vitest-docs/config/browser/screenshotfailures.md +6 -0
- package/public/vendor/vitest-docs/config/browser/testerhtmlpath.md +5 -0
- package/public/vendor/vitest-docs/config/browser/trace.md +43 -0
- package/public/vendor/vitest-docs/config/browser/trackunhandlederrors.md +10 -0
- package/public/vendor/vitest-docs/config/browser/ui.md +7 -0
- package/public/vendor/vitest-docs/config/browser/viewport.md +6 -0
- package/public/vendor/vitest-docs/config/browser/webdriverio.md +64 -0
- package/public/vendor/vitest-docs/config/cache.md +26 -0
- package/public/vendor/vitest-docs/config/chaiconfig.md +29 -0
- package/public/vendor/vitest-docs/config/clearmocks.md +22 -0
- package/public/vendor/vitest-docs/config/coverage.md +455 -0
- package/public/vendor/vitest-docs/config/css.md +47 -0
- package/public/vendor/vitest-docs/config/dangerouslyignoreunhandlederrors.md +23 -0
- package/public/vendor/vitest-docs/config/deps.md +127 -0
- package/public/vendor/vitest-docs/config/detectasyncleaks.md +39 -0
- package/public/vendor/vitest-docs/config/diff.md +96 -0
- package/public/vendor/vitest-docs/config/dir.md +7 -0
- package/public/vendor/vitest-docs/config/disableconsoleintercept.md +15 -0
- package/public/vendor/vitest-docs/config/env.md +5 -0
- package/public/vendor/vitest-docs/config/environment.md +96 -0
- package/public/vendor/vitest-docs/config/environmentoptions.md +30 -0
- package/public/vendor/vitest-docs/config/exclude.md +49 -0
- package/public/vendor/vitest-docs/config/execargv.md +10 -0
- package/public/vendor/vitest-docs/config/expandsnapshotdiff.md +7 -0
- package/public/vendor/vitest-docs/config/expect.md +38 -0
- package/public/vendor/vitest-docs/config/experimental.md +510 -0
- package/public/vendor/vitest-docs/config/faketimers.md +51 -0
- package/public/vendor/vitest-docs/config/fileparallelism.md +11 -0
- package/public/vendor/vitest-docs/config/forcereruntriggers.md +19 -0
- package/public/vendor/vitest-docs/config/globals.md +42 -0
- package/public/vendor/vitest-docs/config/globalsetup.md +72 -0
- package/public/vendor/vitest-docs/config/hideskippedtests.md +7 -0
- package/public/vendor/vitest-docs/config/hooktimeout.md +7 -0
- package/public/vendor/vitest-docs/config/include-source.md +115 -0
- package/public/vendor/vitest-docs/config/include.md +71 -0
- package/public/vendor/vitest-docs/config/includetasklocation.md +17 -0
- package/public/vendor/vitest-docs/config/index.md +85 -0
- package/public/vendor/vitest-docs/config/isolate.md +13 -0
- package/public/vendor/vitest-docs/config/logheapusage.md +7 -0
- package/public/vendor/vitest-docs/config/maxconcurrency.md +9 -0
- package/public/vendor/vitest-docs/config/maxworkers.md +49 -0
- package/public/vendor/vitest-docs/config/mockreset.md +22 -0
- package/public/vendor/vitest-docs/config/mode.md +7 -0
- package/public/vendor/vitest-docs/config/name.md +111 -0
- package/public/vendor/vitest-docs/config/onconsolelog.md +25 -0
- package/public/vendor/vitest-docs/config/onstacktrace.md +32 -0
- package/public/vendor/vitest-docs/config/onunhandlederror.md +35 -0
- package/public/vendor/vitest-docs/config/open.md +7 -0
- package/public/vendor/vitest-docs/config/outputfile.md +7 -0
- package/public/vendor/vitest-docs/config/passwithnotests.md +7 -0
- package/public/vendor/vitest-docs/config/pool.md +45 -0
- package/public/vendor/vitest-docs/config/printconsoletrace.md +6 -0
- package/public/vendor/vitest-docs/config/projects.md +6 -0
- package/public/vendor/vitest-docs/config/provide.md +45 -0
- package/public/vendor/vitest-docs/config/reporters.md +69 -0
- package/public/vendor/vitest-docs/config/resolvesnapshotpath.md +36 -0
- package/public/vendor/vitest-docs/config/restoremocks.md +22 -0
- package/public/vendor/vitest-docs/config/retry.md +140 -0
- package/public/vendor/vitest-docs/config/root.md +6 -0
- package/public/vendor/vitest-docs/config/runner.md +6 -0
- package/public/vendor/vitest-docs/config/sequence.md +158 -0
- package/public/vendor/vitest-docs/config/server.md +68 -0
- package/public/vendor/vitest-docs/config/setupfiles.md +40 -0
- package/public/vendor/vitest-docs/config/silent.md +9 -0
- package/public/vendor/vitest-docs/config/slowtestthreshold.md +7 -0
- package/public/vendor/vitest-docs/config/snapshotenvironment.md +27 -0
- package/public/vendor/vitest-docs/config/snapshotformat.md +28 -0
- package/public/vendor/vitest-docs/config/snapshotserializers.md +6 -0
- package/public/vendor/vitest-docs/config/stricttags.md +30 -0
- package/public/vendor/vitest-docs/config/tags.md +141 -0
- package/public/vendor/vitest-docs/config/teardowntimeout.md +7 -0
- package/public/vendor/vitest-docs/config/testnamepattern.md +21 -0
- package/public/vendor/vitest-docs/config/testtimeout.md +7 -0
- package/public/vendor/vitest-docs/config/typecheck.md +77 -0
- package/public/vendor/vitest-docs/config/ui.md +15 -0
- package/public/vendor/vitest-docs/config/unstubenvs.md +20 -0
- package/public/vendor/vitest-docs/config/unstubglobals.md +20 -0
- package/public/vendor/vitest-docs/config/update.md +16 -0
- package/public/vendor/vitest-docs/config/vmmemorylimit.md +30 -0
- package/public/vendor/vitest-docs/config/watch.md +11 -0
- package/public/vendor/vitest-docs/config/watchtriggerpatterns.md +29 -0
- package/public/vendor/vitest-docs/guide/advanced/index.md +147 -0
- package/public/vendor/vitest-docs/guide/advanced/pool.md +148 -0
- package/public/vendor/vitest-docs/guide/advanced/reporters.md +93 -0
- package/public/vendor/vitest-docs/guide/advanced/tests.md +125 -0
- package/public/vendor/vitest-docs/guide/browser/aria-snapshots.md +470 -0
- package/public/vendor/vitest-docs/guide/browser/component-testing.md +571 -0
- package/public/vendor/vitest-docs/guide/browser/index.md +630 -0
- package/public/vendor/vitest-docs/guide/browser/multiple-setups.md +121 -0
- package/public/vendor/vitest-docs/guide/browser/trace-view.md +126 -0
- package/public/vendor/vitest-docs/guide/browser/visual-regression-testing.md +734 -0
- package/public/vendor/vitest-docs/guide/cli-generated.md +972 -0
- package/public/vendor/vitest-docs/guide/cli.md +234 -0
- package/public/vendor/vitest-docs/guide/common-errors.md +163 -0
- package/public/vendor/vitest-docs/guide/coverage.md +515 -0
- package/public/vendor/vitest-docs/guide/debugging.md +127 -0
- package/public/vendor/vitest-docs/guide/environment.md +101 -0
- package/public/vendor/vitest-docs/guide/extending-matchers.md +160 -0
- package/public/vendor/vitest-docs/guide/features.md +310 -0
- package/public/vendor/vitest-docs/guide/filtering.md +175 -0
- package/public/vendor/vitest-docs/guide/ide.md +43 -0
- package/public/vendor/vitest-docs/guide/improving-performance.md +245 -0
- package/public/vendor/vitest-docs/guide/in-source.md +159 -0
- package/public/vendor/vitest-docs/guide/index.md +128 -0
- package/public/vendor/vitest-docs/guide/learn/async.md +147 -0
- package/public/vendor/vitest-docs/guide/learn/debugging-tests.md +210 -0
- package/public/vendor/vitest-docs/guide/learn/matchers.md +277 -0
- package/public/vendor/vitest-docs/guide/learn/mock-functions.md +277 -0
- package/public/vendor/vitest-docs/guide/learn/setup-teardown.md +240 -0
- package/public/vendor/vitest-docs/guide/learn/snapshots.md +166 -0
- package/public/vendor/vitest-docs/guide/learn/testing-in-practice.md +430 -0
- package/public/vendor/vitest-docs/guide/learn/writing-tests-with-ai.md +127 -0
- package/public/vendor/vitest-docs/guide/learn/writing-tests.md +231 -0
- package/public/vendor/vitest-docs/guide/lifecycle.md +379 -0
- package/public/vendor/vitest-docs/guide/migration.md +863 -0
- package/public/vendor/vitest-docs/guide/mocking/classes.md +158 -0
- package/public/vendor/vitest-docs/guide/mocking/dates.md +52 -0
- package/public/vendor/vitest-docs/guide/mocking/file-system.md +74 -0
- package/public/vendor/vitest-docs/guide/mocking/functions.md +61 -0
- package/public/vendor/vitest-docs/guide/mocking/globals.md +20 -0
- package/public/vendor/vitest-docs/guide/mocking/modules.md +414 -0
- package/public/vendor/vitest-docs/guide/mocking/requests.md +114 -0
- package/public/vendor/vitest-docs/guide/mocking/timers.md +48 -0
- package/public/vendor/vitest-docs/guide/mocking.md +239 -0
- package/public/vendor/vitest-docs/guide/open-telemetry.md +156 -0
- package/public/vendor/vitest-docs/guide/parallelism.md +82 -0
- package/public/vendor/vitest-docs/guide/profiling-test-performance.md +243 -0
- package/public/vendor/vitest-docs/guide/projects.md +291 -0
- package/public/vendor/vitest-docs/guide/recipes.md +59 -0
- package/public/vendor/vitest-docs/guide/reporters.md +723 -0
- package/public/vendor/vitest-docs/guide/snapshot.md +620 -0
- package/public/vendor/vitest-docs/guide/test-annotations.md +103 -0
- package/public/vendor/vitest-docs/guide/test-context.md +902 -0
- package/public/vendor/vitest-docs/guide/test-tags.md +314 -0
- package/public/vendor/vitest-docs/guide/testing-types.md +149 -0
- package/public/vendor/vitest-docs/guide/ui.md +160 -0
- package/public/vendor/vitest-docs/guide/using-plugins.md +5 -0
- package/public/vendor/vitest-docs/manifest.json +1691 -0
- package/resources/index.js +15 -4
package/bin/vitest-agent-mcp.js
CHANGED
|
@@ -19,8 +19,8 @@ import { Effect, ManagedRuntime } from "effect";
|
|
|
19
19
|
* @internal
|
|
20
20
|
*/
|
|
21
21
|
function checkVersionDrift() {
|
|
22
|
-
if ("1.0.
|
|
23
|
-
if (CURRENT_SDK_VERSION !== "1.0.
|
|
22
|
+
if ("1.0.1" === "0.0.0") return;
|
|
23
|
+
if (CURRENT_SDK_VERSION !== "1.0.1") process.stderr.write(`[@vitest-agent/mcp] version drift: @vitest-agent/mcp@${CURRENT_MCP_VERSION} with @vitest-agent/sdk@${CURRENT_SDK_VERSION}. Reinstall @vitest-agent/* packages so versions match.\n`);
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
26
|
* Resolve the user's project directory.
|
package/index.d.ts
CHANGED
|
@@ -108,7 +108,7 @@ declare const createCallerFactory: import("@trpc/server").TRPCRouterCallerFactor
|
|
|
108
108
|
* @returns an Effect Layer providing all MCP runtime services
|
|
109
109
|
* @public
|
|
110
110
|
*/
|
|
111
|
-
declare const McpLive: (dbPath: string, logLevel?: LogLevel.LogLevel, logFile?: string) => Layer.Layer<import("@vitest-agent/sdk").DataReader | import("@effect/sql/SqlClient").SqlClient | import("@vitest-agent/sdk").DataStore | import("@vitest-agent/sdk").ProjectDiscovery | import("@vitest-agent/sdk").
|
|
111
|
+
declare const McpLive: (dbPath: string, logLevel?: LogLevel.LogLevel, logFile?: string) => Layer.Layer<import("@vitest-agent/sdk").DataReader | import("@effect/sql/SqlClient").SqlClient | import("@vitest-agent/sdk").DataStore | import("@vitest-agent/sdk").ProjectDiscovery | import("@vitest-agent/sdk").FormatSelector | import("@vitest-agent/sdk").ExecutorResolver | import("@vitest-agent/sdk").DetailResolver | import("@vitest-agent/sdk").EnvironmentDetector | import("@vitest-agent/sdk").OutputRenderer | import("@effect/sql-sqlite-node/SqliteClient").SqliteClient | NodeContext.NodeContext, import("@effect/sql/SqlError").SqlError | SqliteMigrator.MigrationError | import("effect/ConfigError").ConfigError, never>;
|
|
112
112
|
//#endregion
|
|
113
113
|
//#region src/router.d.ts
|
|
114
114
|
/**
|
package/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import { startMcpServer } from "./server.js";
|
|
|
13
13
|
*
|
|
14
14
|
* @public
|
|
15
15
|
*/
|
|
16
|
-
const CURRENT_MCP_VERSION = "1.0.
|
|
16
|
+
const CURRENT_MCP_VERSION = "1.0.1";
|
|
17
17
|
|
|
18
18
|
//#endregion
|
|
19
19
|
export { CURRENT_MCP_VERSION, McpLive, appRouter, createCallerFactory, createCurrentSessionIdRef, createSessionContextRef, startMcpServer };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitest-agent/mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Model Context Protocol server for vitest-agent. Exposes 53 tools for agent access to test data, TDD lifecycle, and session management.",
|
|
6
6
|
"keywords": [
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"@effect/sql-sqlite-node": "^0.52.0",
|
|
46
46
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
47
47
|
"@trpc/server": "^11.18.0",
|
|
48
|
-
"@vitest-agent/sdk": "1.0.
|
|
48
|
+
"@vitest-agent/sdk": "1.0.1",
|
|
49
49
|
"effect": "^3.21.4",
|
|
50
50
|
"zod": "^4.4.3"
|
|
51
51
|
},
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"patterns": [
|
|
3
|
+
{
|
|
4
|
+
"slug": "testing-effect-services-with-mock-layers",
|
|
5
|
+
"title": "Testing Effect Services with Mock Layers",
|
|
6
|
+
"summary": "Compose live and test layers, swap platform layers, assert on accumulated writes via state-container test layers.",
|
|
7
|
+
"annotations": {
|
|
8
|
+
"audience": ["assistant"],
|
|
9
|
+
"priority": 0.9
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"slug": "testing-effect-schema-definitions",
|
|
14
|
+
"title": "Testing Effect Schema Definitions",
|
|
15
|
+
"summary": "Round-trip Schema.Class and Schema.Struct, branded types, transformations, and decoding error assertions.",
|
|
16
|
+
"annotations": {
|
|
17
|
+
"audience": ["assistant"],
|
|
18
|
+
"priority": 0.9
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"slug": "authoring-a-custom-vitest-agent-reporter",
|
|
23
|
+
"title": "Authoring a Custom vitest-agent Reporter",
|
|
24
|
+
"summary": "Implement the VitestAgentReporterFactory contract: ReporterKit, RenderedOutput targets, stdOsc8 hyperlinks, composing with the default reporter.",
|
|
25
|
+
"annotations": {
|
|
26
|
+
"audience": ["assistant"],
|
|
27
|
+
"priority": 0.7
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"slug": "running-tests-via-mcp",
|
|
32
|
+
"title": "Running Tests via the MCP run_tests Tool",
|
|
33
|
+
"summary": "Scope runs with project/files/tags (there is no `filter` param), read the four-variant return shape, and understand why subset runs 'fail' coverage thresholds.",
|
|
34
|
+
"annotations": {
|
|
35
|
+
"audience": ["assistant"],
|
|
36
|
+
"priority": 0.9
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"slug": "silencing-leaking-output-in-tests",
|
|
41
|
+
"title": "Silencing Leaking Log / Build Output in Tests",
|
|
42
|
+
"summary": "Decision tree mapping each noise source (Effect logger, console.*, rsbuild, tsdown, API Extractor) to its silencing technique, with an assert-on-output guardrail.",
|
|
43
|
+
"annotations": {
|
|
44
|
+
"audience": ["assistant"],
|
|
45
|
+
"priority": 0.8
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"slug": "known-issues-and-caveats",
|
|
50
|
+
"title": "Known Issues & Instrumentation Caveats",
|
|
51
|
+
"summary": "Distinguishes real vitest-agent issues (DataStoreError UNIQUE race) from upstream Vitest caveats (coverage-dir ENOENT), already-fixed bugs (perf_hooks), and misconceptions (discovery-cache staleness).",
|
|
52
|
+
"annotations": {
|
|
53
|
+
"audience": ["assistant"],
|
|
54
|
+
"priority": 0.7
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"slug": "operating-vitest-agent-as-an-agent",
|
|
59
|
+
"title": "Operating vitest-agent as an Agent",
|
|
60
|
+
"summary": "Read first: the front-loaded facts an agent needs (no run_tests filter param, coverage-in-subset behavior, console output is config-driven not env-driven, attribution is auto-recovered) with links to the deeper patterns.",
|
|
61
|
+
"annotations": {
|
|
62
|
+
"audience": ["assistant"],
|
|
63
|
+
"priority": 0.95
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Authoring a Custom vitest-agent Reporter
|
|
2
|
+
|
|
3
|
+
## When to use
|
|
4
|
+
|
|
5
|
+
You want output behavior that the named factories in `@vitest-agent/reporter` (`defaultReporter`, `markdownReporter`, `jsonReporter`, etc.) do not provide. Examples: SARIF output for code-scanning ingestion, JUnit XML alongside the default markdown, a side-channel that posts to an internal API.
|
|
6
|
+
|
|
7
|
+
## The contract
|
|
8
|
+
|
|
9
|
+
A reporter is a function that takes a `ReporterKit` and returns either a single `VitestAgentReporter` or an array. A reporter has exactly one method: synchronous `render(input)` that returns `RenderedOutput[]`. No I/O, no Vitest lifecycle. The plugin handles persistence, classification, baselines, and trends; reporters render the resulting data and the plugin routes the outputs to their declared targets.
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
type VitestAgentReporterFactory =
|
|
13
|
+
(kit: ReporterKit) =>
|
|
14
|
+
VitestAgentReporter | ReadonlyArray<VitestAgentReporter>;
|
|
15
|
+
|
|
16
|
+
interface VitestAgentReporter {
|
|
17
|
+
readonly render:
|
|
18
|
+
(input: ReporterRenderInput) => ReadonlyArray<RenderedOutput>;
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`ReporterKit` carries `config`, `stdEnv`, and `stdOsc8` (a pre-bound hyperlink helper). `ReporterRenderInput` carries `reports` (one per project), `classifications` (a Map keyed by `TestReport.fullName`), and an optional `trendSummary`.
|
|
23
|
+
|
|
24
|
+
## Minimal example: a SARIF sidecar
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import type { VitestAgentReporterFactory } from "@vitest-agent/sdk";
|
|
28
|
+
|
|
29
|
+
export const sarifReporter: VitestAgentReporterFactory = (kit) => ({
|
|
30
|
+
render: (input) => {
|
|
31
|
+
const sarif = buildSarif(input.reports);
|
|
32
|
+
return [
|
|
33
|
+
{
|
|
34
|
+
target: "file",
|
|
35
|
+
content: JSON.stringify(sarif),
|
|
36
|
+
contentType: "application/sarif+json",
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The `target: "file"` slot is reserved for arbitrary on-disk artifacts; the convention for resolving the path will land in a future minor (currently a no-op routing target). Until then, use `target: "stdout"` for output that should reach the user immediately.
|
|
44
|
+
|
|
45
|
+
## Composing with the default
|
|
46
|
+
|
|
47
|
+
If you want your reporter to layer on top of the default rather than replace it, return an array:
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { defaultReporter, type VitestAgentReporterFactory } from "@vitest-agent/reporter";
|
|
51
|
+
|
|
52
|
+
export const myReporter: VitestAgentReporterFactory = (kit) => {
|
|
53
|
+
const inner = defaultReporter(kit);
|
|
54
|
+
const innerArr = Array.isArray(inner) ? inner : [inner];
|
|
55
|
+
return [...innerArr, sarifReporter(kit)];
|
|
56
|
+
};
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Wiring into the plugin
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// vitest.config.ts
|
|
63
|
+
import { defineConfig } from "vitest/config";
|
|
64
|
+
import { agentPlugin } from "@vitest-agent/plugin";
|
|
65
|
+
import { myReporter } from "./my-reporter.js";
|
|
66
|
+
|
|
67
|
+
export default defineConfig({
|
|
68
|
+
plugins: [agentPlugin({ reporterFactory: myReporter })],
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Anti-patterns
|
|
73
|
+
|
|
74
|
+
- **Don't reach into Vitest's lifecycle from a reporter.** The plugin owns it. If you need a Vitest hook, you have outgrown this contract — fork the plugin instead.
|
|
75
|
+
- **Don't return Promises from `render`.** The contract is sync. If you have async work, do it ahead of time and pass the resolved data into your factory closure.
|
|
76
|
+
- **Don't use `stdOsc8` outside terminal targets.** OSC-8 escapes only render in capable terminals; `kit.stdOsc8` already gates this for you. Calling it from a `target: "github-summary"` reporter is harmless (it returns the bare label) but signals confused intent.
|
|
77
|
+
|
|
78
|
+
## See also
|
|
79
|
+
|
|
80
|
+
- `vitest://docs/api/advanced/reporters` — Vitest's own reporter API (different surface; informative for context)
|
|
81
|
+
- `packages/reporter/src/default.ts` — The composition pattern the default reporter uses
|
|
82
|
+
- `packages/sdk/src/contracts/reporter.ts` — Full type definitions for the contract
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Known Issues & Instrumentation Caveats
|
|
2
|
+
|
|
3
|
+
## When to use
|
|
4
|
+
|
|
5
|
+
A test run surfaced an error or oddity that looks like a vitest-agent bug.
|
|
6
|
+
Check it against this list first — several are upstream Vitest behavior, an
|
|
7
|
+
already-shipped fix, or a misconception, not something to investigate as a
|
|
8
|
+
consumer-side problem.
|
|
9
|
+
|
|
10
|
+
## `DataStoreError` on a UNIQUE collision — live, retry
|
|
11
|
+
|
|
12
|
+
A concurrent agent registration (on `idempotency_key`) or two concurrent turn
|
|
13
|
+
writes (on `(session_id, turn_no)`) can race the persistence layer's
|
|
14
|
+
check-then-insert and surface a `DataStoreError` carrying
|
|
15
|
+
`UNIQUE constraint failed`. It is not a consumer bug and not data corruption —
|
|
16
|
+
retry the operation. The SELECT pre-check narrows but does not close the race.
|
|
17
|
+
|
|
18
|
+
## `coverage/.tmp … ENOENT coverage-0.json` — inherent Vitest, not a bug
|
|
19
|
+
|
|
20
|
+
Running Vitest in two processes at once (for example a CLI run alongside the
|
|
21
|
+
`vitest-vscode` extension) makes them share Vitest's default
|
|
22
|
+
`coverage.reportsDirectory`, and one process can delete the temp dir the other
|
|
23
|
+
is writing. vitest-agent never sets `reportsDirectory`, so this is an upstream
|
|
24
|
+
multi-process Vitest caveat. Give each concurrent process its own
|
|
25
|
+
`coverage.reportsDirectory` (or serialize them).
|
|
26
|
+
|
|
27
|
+
## `MaxPerformanceEntryBufferExceededWarning` — resolved, stop chasing it
|
|
28
|
+
|
|
29
|
+
This `perf_hooks` warning on long runs was fixed in a shipped
|
|
30
|
+
`@vitest-agent/reporter` release: React 19's development reconciler emits a
|
|
31
|
+
`performance.measure()` per render, and the live renderer now drains Node's
|
|
32
|
+
user-timing buffer after each render cycle. If you still see it, update
|
|
33
|
+
`@vitest-agent/reporter`. Do not investigate it as a fault in your own repo.
|
|
34
|
+
|
|
35
|
+
## Stale test counts — re-run, there is no warm cache
|
|
36
|
+
|
|
37
|
+
Moving or adding test files does not require an MCP restart. Discovery is
|
|
38
|
+
re-walked fresh on every Vitest run; the MCP serves counts from the database,
|
|
39
|
+
which only updates when tests are re-run. Stale numbers mean the tests have not
|
|
40
|
+
been re-run — not that a discovery cache is warm. Restarting the MCP would not
|
|
41
|
+
refresh them.
|
|
42
|
+
|
|
43
|
+
## A run that rebuilds `dist/` mid-run — expected
|
|
44
|
+
|
|
45
|
+
A host project's Vitest `globalSetup` may call `AgentPlugin.runScript(...)`
|
|
46
|
+
(for example `pnpm turbo run build:dev`), which can transiently tear down and
|
|
47
|
+
rebuild `dist/` during the run. This is the host project's configuration, not a
|
|
48
|
+
broken workspace.
|
|
49
|
+
|
|
50
|
+
## See also
|
|
51
|
+
|
|
52
|
+
- `vitest-agent://patterns/running-tests-via-mcp` — why subset runs "fail" coverage thresholds
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Operating vitest-agent as an Agent
|
|
2
|
+
|
|
3
|
+
## When to use
|
|
4
|
+
|
|
5
|
+
Read this first when you are an agent driving a project that uses
|
|
6
|
+
`@vitest-agent/*`. It front-loads the handful of facts that otherwise cost
|
|
7
|
+
trial-and-error, then points you at the deeper patterns.
|
|
8
|
+
|
|
9
|
+
## The facts that save the most time
|
|
10
|
+
|
|
11
|
+
1. **Run tests with `run_tests`, not Bash `vitest`.** `run_tests` persists
|
|
12
|
+
results, classifications, coverage, and history and fires the post-tool-use
|
|
13
|
+
hooks. Shelling out to `vitest` bypasses all of that.
|
|
14
|
+
2. **`run_tests` has no `filter` parameter.** Scope with `project` (the Vitest
|
|
15
|
+
project name), `files` (globs), or `tags`. An unknown key like `filter` is
|
|
16
|
+
silently dropped, so the whole suite runs.
|
|
17
|
+
3. **Subset runs "fail" coverage thresholds by design.** A single-file run
|
|
18
|
+
exiting with `ERROR: Coverage … does not meet global threshold` is expected
|
|
19
|
+
— global thresholds applied to partial coverage. There is no per-run
|
|
20
|
+
coverage toggle in `run_tests`; it inherits your `vitest.config`. For
|
|
21
|
+
isolated inspection use the CLI: `vitest run <file> --coverage.enabled=false`.
|
|
22
|
+
4. **You cannot see stray `console.log` through `run_tests`** — it null-routes
|
|
23
|
+
Vitest's stdout. Console behavior is configured via the plugin's `console`
|
|
24
|
+
matrix (e.g. `console: { agent: "passthrough" }`), **not** environment
|
|
25
|
+
variables. To see intercepted output, run Vitest directly with
|
|
26
|
+
`--disableConsoleIntercept` (a native Vitest flag).
|
|
27
|
+
5. **Session attribution is recovered for you.** The SessionStart hook writes
|
|
28
|
+
the `VITEST_AGENT_*` identity into the environment and the SDK recovers it —
|
|
29
|
+
you never set those vars by hand. The only behavioral knobs are
|
|
30
|
+
`VITEST_REPORTER_LOG_LEVEL`, `VITEST_REPORTER_LOG_FILE`, and `NO_COLOR`.
|
|
31
|
+
6. **Stale counts mean tests were not re-run**, not a warm cache. Discovery
|
|
32
|
+
re-walks per Vitest run; the MCP serves counts from the database.
|
|
33
|
+
7. **Some "leaks" are guardrail tests** that assert on their own output. Do not
|
|
34
|
+
silence output a test captures and `expect`s on.
|
|
35
|
+
|
|
36
|
+
## Environment, briefly
|
|
37
|
+
|
|
38
|
+
You do not configure vitest-agent through environment variables. The
|
|
39
|
+
`VITEST_AGENT_*` vars (chat id, conversation id, agent ids, project dir,
|
|
40
|
+
sidecar bin) are attribution plumbing written by the Claude Code plugin's
|
|
41
|
+
SessionStart hook and recovered automatically. The only vars you might set for
|
|
42
|
+
diagnostics are `VITEST_REPORTER_LOG_LEVEL` / `VITEST_REPORTER_LOG_FILE` (a
|
|
43
|
+
diagnostic log file, separate from the console reporter) and `NO_COLOR`.
|
|
44
|
+
|
|
45
|
+
## Where to go next
|
|
46
|
+
|
|
47
|
+
| You want to… | Read |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| Scope a run, read the return shape, understand coverage-in-subset | `vitest-agent://patterns/running-tests-via-mcp` |
|
|
50
|
+
| Silence noisy log/build output in tests | `vitest-agent://patterns/silencing-leaking-output-in-tests` |
|
|
51
|
+
| Tell tooling noise apart from real failures | `vitest-agent://patterns/known-issues-and-caveats` |
|
|
52
|
+
| Read the full console-mode matrix (human prose) | <https://vitest-agent.dev/guide/console-modes> |
|
|
53
|
+
| The human-facing version of this page | <https://vitest-agent.dev/guide/operating-as-an-agent> |
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Running Tests via the MCP `run_tests` Tool
|
|
2
|
+
|
|
3
|
+
## When to use
|
|
4
|
+
|
|
5
|
+
You want to execute the Vitest suite (or a subset) from an agent. Always prefer `run_tests` over shelling out to `vitest` — it persists results, classifications, coverage, and history to the project database and drives the post-tool-use hooks. Re-running `vitest` over Bash bypasses all of that.
|
|
6
|
+
|
|
7
|
+
## Scoping a run — there is no `filter` parameter
|
|
8
|
+
|
|
9
|
+
`run_tests` accepts exactly these inputs:
|
|
10
|
+
|
|
11
|
+
| Field | Type | Default | Scopes by |
|
|
12
|
+
| --- | --- | --- | --- |
|
|
13
|
+
| `files` | `string[]` | `[]` | Vitest file patterns: exact paths or globs |
|
|
14
|
+
| `project` | `string` | unset | The Vitest **project name** from your config |
|
|
15
|
+
| `tags` | `{ all?: string[]; any?: string[]; none?: string[] }` | unset | Vitest tag expression |
|
|
16
|
+
| `passWithNoTests` | `boolean` | config value | Per-call override of `test.passWithNoTests` |
|
|
17
|
+
| `timeout` | `number` (seconds) | `120` | Per-call run timeout |
|
|
18
|
+
|
|
19
|
+
There is **no** `filter` field. Passing one (`run_tests({ filter: "@my/pkg" })`) is silently dropped — the call then runs with no filters, so the **entire** suite executes. To scope to one package, pass its Vitest project name as `project`; to scope to one file, pass it in `files`.
|
|
20
|
+
|
|
21
|
+
`project` matches the project **name** defined in `vitest.config.ts`, not the npm package name. They often differ — check your config for the actual project name.
|
|
22
|
+
|
|
23
|
+
`tags` sub-filters AND together (with each other and with `project`/`files`): `all` → `"a and b"`, `any` → `"(a or b)"`, `none` → `"not a and not b"`.
|
|
24
|
+
|
|
25
|
+
## Return shape
|
|
26
|
+
|
|
27
|
+
`run_tests` returns one of four variants — discriminate on `kind`:
|
|
28
|
+
|
|
29
|
+
| `kind` | Fields | Meaning |
|
|
30
|
+
| --- | --- | --- |
|
|
31
|
+
| `"ok"` | `report`, `classifications` (map of test full-name → `stable`/`new-failure`/`persistent`/`flaky`/`recovered`), optional `project` | The run completed |
|
|
32
|
+
| `"no-match"` | `filter` (the resolved `project`/`files`/`tags`/`resolvedExpression`) | A filter was supplied but matched zero tests |
|
|
33
|
+
| `"timeout"` | `timeoutSeconds` | Exceeded the timeout |
|
|
34
|
+
| `"error"` | `message` | The run errored |
|
|
35
|
+
|
|
36
|
+
A `no-match` is filter-driven: it fires when you supplied a filter and zero tests matched. Treat it as a finding (typo in `project`/`files`?) rather than a pass.
|
|
37
|
+
|
|
38
|
+
## Coverage on subset runs reads as a failure — by design
|
|
39
|
+
|
|
40
|
+
A single-file or single-project run will commonly exit non-zero with:
|
|
41
|
+
|
|
42
|
+
```text
|
|
43
|
+
ERROR: Coverage for lines (59.91%) does not meet global threshold (70%)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This is expected: your global coverage thresholds are applied to a partial run, so partial coverage "fails" them. It is not a real test failure.
|
|
47
|
+
|
|
48
|
+
`run_tests` has **no per-run coverage toggle** — it deliberately inherits your `vitest.config` `coverage.enabled` (forcing it off here used to override intentional "coverage on by default" setups). For clean isolated inspection, drop to the CLI:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
vitest run path/to/one.test.ts --coverage.enabled=false
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## See also
|
|
55
|
+
|
|
56
|
+
- `vitest-agent://patterns/operating-vitest-agent-as-an-agent` — the orientation index
|
|
57
|
+
- `vitest-agent://patterns/known-issues-and-caveats` — coverage-dir races and other instrumentation caveats
|
|
58
|
+
- `vitest://docs/config/index` — Vitest's native config reference
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Silencing Leaking Log / Build Output in Tests
|
|
2
|
+
|
|
3
|
+
## When to use
|
|
4
|
+
|
|
5
|
+
A test run is polluted with stray log lines, build-tool banners, or framework
|
|
6
|
+
diagnostics that drown the signal. The fix differs per source — identify the
|
|
7
|
+
source first, then apply the matching technique.
|
|
8
|
+
|
|
9
|
+
## Guardrail: do not silence output a test asserts on
|
|
10
|
+
|
|
11
|
+
Before silencing anything, confirm the test does not capture and `expect` on
|
|
12
|
+
the output. Some tests deliberately spy on `process.stdout.write` /
|
|
13
|
+
`process.stderr.write` (or `console.*`) and assert on `.mock.calls` to verify
|
|
14
|
+
what a formatter emits. Silencing those breaks the assertion. If you see a
|
|
15
|
+
`vi.spyOn(process.stdout, "write")` whose `.mock.calls` are asserted, that
|
|
16
|
+
output is the test subject — leave it alone.
|
|
17
|
+
|
|
18
|
+
## Decision tree
|
|
19
|
+
|
|
20
|
+
| Source (what the noise looks like) | Technique |
|
|
21
|
+
| --- | --- |
|
|
22
|
+
| Effect default logger (`timestamp=… level=INFO message=…`) | Provide a silent logger layer |
|
|
23
|
+
| Plain `console.log` / `console.warn` / `console.error` | Spy + mock the method |
|
|
24
|
+
| rsbuild (`info build started…`, file-size table) | Set `@rsbuild/core` logger level to silent |
|
|
25
|
+
| tsdown / rolldown (`ℹ entry:`, `✔ Build complete`) | Pass `logLevel: "silent"` to the build |
|
|
26
|
+
| API Extractor (`(ae-missing-release-tag)` to stderr) | Pass an `onMessage` handler that marks messages handled |
|
|
27
|
+
|
|
28
|
+
## Effect default logger
|
|
29
|
+
|
|
30
|
+
Provide a silent logger at the run/layer boundary. This mirrors how the SDK's
|
|
31
|
+
own `LoggerLive` builds its silent variant (`packages/sdk/src/layers/LoggerLive.ts`):
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { Effect, Logger } from "effect";
|
|
35
|
+
|
|
36
|
+
const silent = Logger.replace(Logger.defaultLogger, Logger.none);
|
|
37
|
+
|
|
38
|
+
await Effect.runPromise(program.pipe(Effect.provide(silent)));
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`Logger.minimumLogLevel(LogLevel.None)` is an equivalent gate.
|
|
42
|
+
|
|
43
|
+
## Plain `console.*`
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { beforeEach, vi } from "vitest";
|
|
47
|
+
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
50
|
+
vi.spyOn(console, "error").mockImplementation(() => {});
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## rsbuild reporter
|
|
55
|
+
|
|
56
|
+
rsbuild writes through its own `logger` singleton, **not** `console.*`, so
|
|
57
|
+
console mocks miss it. Set the level and restore it:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { logger } from "@rsbuild/core";
|
|
61
|
+
|
|
62
|
+
const prev = logger.level;
|
|
63
|
+
logger.level = "silent";
|
|
64
|
+
try {
|
|
65
|
+
await build();
|
|
66
|
+
} finally {
|
|
67
|
+
logger.level = prev;
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## tsdown / rolldown
|
|
72
|
+
|
|
73
|
+
Pass `logLevel: "silent"` to the build call (or inject a no-op output writer).
|
|
74
|
+
|
|
75
|
+
## API Extractor
|
|
76
|
+
|
|
77
|
+
Mark diagnostics handled so they are not printed — this is the production quiet
|
|
78
|
+
path:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
extractorResult = Extractor.invoke(config, {
|
|
82
|
+
messageCallback: (message) => {
|
|
83
|
+
message.handled = true;
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## See also
|
|
89
|
+
|
|
90
|
+
- `vitest-agent://patterns/known-issues-and-caveats` — distinguishing tooling noise from real failures
|
|
91
|
+
- `vitest://docs/api/vi` — the `vi.spyOn` / mock API
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Testing Effect Schema Definitions
|
|
2
|
+
|
|
3
|
+
## When to use
|
|
4
|
+
|
|
5
|
+
When you have an Effect Schema (`Schema.Struct`, `Schema.Class`, branded type, transformation) that defines a public-API shape and you want to verify: round-trip encode/decode preserves data, decoding rejects invalid input with useful errors, transformations behave correctly.
|
|
6
|
+
|
|
7
|
+
## Three test patterns
|
|
8
|
+
|
|
9
|
+
### Pattern A: round-trip
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { Schema } from "effect";
|
|
13
|
+
import { describe, expect, it } from "vitest";
|
|
14
|
+
|
|
15
|
+
const User = Schema.Struct({
|
|
16
|
+
id: Schema.Number,
|
|
17
|
+
name: Schema.String,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("round-trips through encode + decode", () => {
|
|
21
|
+
const value = { id: 42, name: "Ada" };
|
|
22
|
+
const encoded = Schema.encodeUnknownSync(User)(value);
|
|
23
|
+
const decoded = Schema.decodeUnknownSync(User)(encoded);
|
|
24
|
+
expect(decoded).toEqual(value);
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Pattern B: decoding error assertions
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { Either, Schema } from "effect";
|
|
32
|
+
import { describe, expect, it } from "vitest";
|
|
33
|
+
|
|
34
|
+
it("rejects invalid input with the expected error path", () => {
|
|
35
|
+
const result = Schema.decodeUnknownEither(User)({ id: "not-a-number", name: "Ada" });
|
|
36
|
+
expect(Either.isLeft(result)).toBe(true);
|
|
37
|
+
if (Either.isLeft(result)) {
|
|
38
|
+
const formatted = String(result.left);
|
|
39
|
+
expect(formatted).toContain("id");
|
|
40
|
+
expect(formatted).toMatch(/expected number/i);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Pattern C: transformations
|
|
46
|
+
|
|
47
|
+
For `Schema.transform` between two shapes (typical for parsing JSON-stringified payloads):
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
const Comma = Schema.transform(Schema.String, Schema.Array(Schema.String), {
|
|
51
|
+
decode: (s) => s.split(","),
|
|
52
|
+
encode: (a) => a.join(","),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("decodes and encodes the inverse", () => {
|
|
56
|
+
expect(Schema.decodeUnknownSync(Comma)("a,b,c")).toEqual(["a", "b", "c"]);
|
|
57
|
+
expect(Schema.encodeUnknownSync(Comma)(["a", "b"])).toBe("a,b");
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Anti-patterns
|
|
62
|
+
|
|
63
|
+
- **Don't assert on stringified errors verbatim.** Effect Schema's error formatting can shift between minor versions; assert on substrings or use `ParseResult.TreeFormatter` for stable output.
|
|
64
|
+
- **Don't write tests that rely on `Schema.transformOrFail` failing _silently_.** When a transform can fail, exercise the failure path explicitly — `Either` results from `decodeUnknownEither` are the right tool.
|
|
65
|
+
- **Don't recreate the schema in the test.** Import the production definition; otherwise the test passes when the production schema drifts.
|
|
66
|
+
|
|
67
|
+
## See also
|
|
68
|
+
|
|
69
|
+
- `vitest://docs/api/expect` — Vitest matcher reference for the assertion patterns
|
|
70
|
+
- `vitest-agent://patterns/testing-effect-services-with-mock-layers` — Companion pattern; services hand around Schema-typed data
|
|
71
|
+
- Effect Schema docs at `https://effect.website/docs/schema/introduction`
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Testing Effect Services with Mock Layers
|
|
2
|
+
|
|
3
|
+
## When to use
|
|
4
|
+
|
|
5
|
+
Reach for this pattern when an Effect service has external dependencies (filesystem, SQLite, network) that you do not want to exercise in unit tests. The codebase under `packages/sdk/src/services/` and `packages/sdk/src/layers/` follows this exact shape.
|
|
6
|
+
|
|
7
|
+
## The shape
|
|
8
|
+
|
|
9
|
+
Every Effect service has three pieces:
|
|
10
|
+
|
|
11
|
+
1. **The service tag** (`Context.Tag`) and interface — under `services/`.
|
|
12
|
+
2. **The live layer** — wires real I/O against `@effect/platform` adapters.
|
|
13
|
+
3. **The test layer** — usually a mutable state container that records what the service was asked to do, with read methods you assert against.
|
|
14
|
+
|
|
15
|
+
You compose the live layer in production wiring (`ReporterLive`, `CliLive`, `McpLive`); you compose the test layer in tests.
|
|
16
|
+
|
|
17
|
+
## Minimal example
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { Effect, Layer, Ref } from "effect";
|
|
21
|
+
import { describe, expect, it } from "vitest";
|
|
22
|
+
import { DataStore } from "@vitest-agent/sdk";
|
|
23
|
+
|
|
24
|
+
// Test layer: accumulates writes into a Ref so tests can read them back.
|
|
25
|
+
const DataStoreTest = Layer.effect(
|
|
26
|
+
DataStore,
|
|
27
|
+
Effect.gen(function* () {
|
|
28
|
+
const writes = yield* Ref.make<{ runs: ReadonlyArray<unknown> }>({ runs: [] });
|
|
29
|
+
return DataStore.of({
|
|
30
|
+
writeRun: (input) =>
|
|
31
|
+
Ref.update(writes, (s) => ({ runs: [...s.runs, input] })).pipe(
|
|
32
|
+
Effect.as(1),
|
|
33
|
+
),
|
|
34
|
+
// ... other methods stubbed similarly
|
|
35
|
+
});
|
|
36
|
+
}),
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
it("records the run", async () => {
|
|
40
|
+
const program = Effect.gen(function* () {
|
|
41
|
+
const store = yield* DataStore;
|
|
42
|
+
yield* store.writeRun({ project: "x" });
|
|
43
|
+
// assert via your accumulator pattern
|
|
44
|
+
});
|
|
45
|
+
await Effect.runPromise(program.pipe(Effect.provide(DataStoreTest)));
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Why not mock the FileSystem directly?
|
|
50
|
+
|
|
51
|
+
Layer-level swapping is the testable seam Effect was designed around. Mocking `@effect/platform`'s FileSystem implementation works but loses the type checking that ensures your test layer matches the live interface. Layer.effect with the same Tag forces you to satisfy every method.
|
|
52
|
+
|
|
53
|
+
## Anti-patterns
|
|
54
|
+
|
|
55
|
+
- **Don't `Effect.runPromise` inside service implementations** — providing `DataStoreTest` and then having the service call `Effect.runPromise` internally bypasses the layer.
|
|
56
|
+
- **Don't share `Ref`s across tests** — construct the test layer per-test (or use `beforeEach`) so accumulated state doesn't bleed.
|
|
57
|
+
- **Don't assert on layer construction order** — Effect normalizes the merge graph.
|
|
58
|
+
|
|
59
|
+
## See also
|
|
60
|
+
|
|
61
|
+
- `vitest://docs/guide/mocking` — Vitest mocking guide (general background)
|
|
62
|
+
- `vitest-agent://patterns/testing-effect-schema-definitions` — Companion pattern; most service inputs are Schema-typed
|
|
63
|
+
- `packages/sdk/src/layers/DataStoreTest.ts` — The canonical example in this codebase
|