Code Testing Extensions This reference-only catalog lists bundled guidance for the code-testing pipeline. Read the matching file directly; do not invoke this catalog as a skill. Resource paths are relative to this catalog, not the project workspace. Available Extension Files | File | Language | Contents | |------|----------|----------| | extensions/dotnet.md | .NET (C#/F#/VB) | Build commands, test commands, project reference validation, common CS error codes, MSTest template | | extensions/python.md | Python | Framework-adaptive test commands (pytest, custom runners), project layout detectio…

./internal/billing\n```\n\n**Rerun:** SUCCESS\n\n## Sample Final Report\n\n```markdown\n## Test Generation Report\n\n**Project**: billing (Go)\n**Strategy**: Direct (single source file in scope)\n\n### Results\n| Metric | Value |\n|----------------|-------|\n| Tests created | 11 |\n| Tests passing | 11 |\n| Tests failing | 0 |\n| Files created | 1 |\n\n### Files Created\n- `internal/billing/invoice_service_test.go` (3 top-level tests, 11 subtests including 5 table cases)\n\n### Coverage\n- InvoiceService.CalculateTotal — 3 happy + 2 error cases (table-driven)\n- InvoiceService.GetByID — happy + missing + repo-error\n- InvoiceService.MarkAsPaid — happy (with fixed clock) + already-paid + missing\n\n### Build / Test Validation\n- `go vet ./...`: ✅\n- `go test -count=1 ./internal/billing`: ✅ PASS\n\n### Next Steps\n- Add fuzz test (`FuzzCalculateTotal`) if rounding correctness is critical\n- Consider extracting a `Clock` interface if more time-dependent logic appears\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":12656,"content_sha256":"e3f2ba1c40c0f1bde5cb62aec46aaad6674de0fc9813038f7845d1512658bf71"},{"filename":"extensions/go.md","content":"# Go Extension\n\nLanguage-specific guidance for Go test generation.\n\n## Rule #1: Investigate the Repo First\n\nBefore writing any test or running any command, read:\n\n1. **Existing tests** — find `*_test.go` files and copy their style (table-driven layout, helper usage, assertion library, build tags)\n2. **`go.mod` / `go.sum`** — module path, Go version, dependencies (e.g. `testify`, `gomock`, `mockery`)\n3. **Build/CI scripts** — `Makefile`, `magefile.go`, `Taskfile.yml`, `.github/workflows/*.yml`\n4. **`go.work`** — if present, you are in a workspace; tests for a module must run from that module's directory or use `-C` (Go 1.20+)\n\nUse whatever assertion style and test layout the repo already uses. Do not introduce `testify` if the repo uses the standard library only.\n\n## Toolchain Detection\n\n| Indicator | Meaning |\n|-----------|---------|\n| `go.mod` `go 1.x` directive | Minimum Go version — match it locally with `go version` |\n| `go.work` at the root | Multi-module workspace; commands resolve dependent modules from sibling directories |\n| `vendor/` directory | Vendored deps; many commands implicitly add `-mod=vendor` |\n| `tools.go` with `//go:build tools` | Tool versions pinned in `go.mod` (e.g. `mockgen`); install with `go install` from the listed paths |\n\n## Build Commands\n\n| Scope | Command |\n|-------|---------|\n| Compile a package | `go build ./path/to/pkg` |\n| Vet (static analysis) | `go vet ./...` |\n| Compile tests without running | `go test -count=1 -run=^$ ./path/to/pkg` |\n| Whole module | `go build ./...` |\n\n`go build ./...` is the closest thing to a \"does it compile\" gate. It does not exercise test files — use `go test -run=^ code-testing-extensions — Skillopedia to type-check tests as well.\n\n## Test Commands\n\n| Scope | Command |\n|-------|---------|\n| All tests in a package | `go test ./path/to/pkg` |\n| All tests in module | `go test ./...` |\n| Single test | `go test -run '^TestName

Code Testing Extensions This reference-only catalog lists bundled guidance for the code-testing pipeline. Read the matching file directly; do not invoke this catalog as a skill. Resource paths are relative to this catalog, not the project workspace. Available Extension Files | File | Language | Contents | |------|----------|----------| | extensions/dotnet.md | .NET (C#/F#/VB) | Build commands, test commands, project reference validation, common CS error codes, MSTest template | | extensions/python.md | Python | Framework-adaptive test commands (pytest, custom runners), project layout detectio…

./path/to/pkg` |\n| Subtest | `go test -run '^TestName$/^subname

Code Testing Extensions This reference-only catalog lists bundled guidance for the code-testing pipeline. Read the matching file directly; do not invoke this catalog as a skill. Resource paths are relative to this catalog, not the project workspace. Available Extension Files | File | Language | Contents | |------|----------|----------| | extensions/dotnet.md | .NET (C#/F#/VB) | Build commands, test commands, project reference validation, common CS error codes, MSTest template | | extensions/python.md | Python | Framework-adaptive test commands (pytest, custom runners), project layout detectio…

./path/to/pkg` |\n| Verbose | `go test -v ./path/to/pkg` |\n| Race detector | `go test -race ./...` |\n| Disable cache | `go test -count=1 ./...` |\n| Short mode | `go test -short ./...` |\n\n- `-run` arguments are **regular expressions anchored** with `^... code-testing-extensions — Skillopedia ; without anchors the pattern matches as a substring\n- `go test -count=1` is the canonical way to bypass the test result cache; never use a fake `-count=2` or environment hacks\n- `-race` significantly slows tests and requires CGO — only enable if the repo's CI does\n\n## Lint Command\n\nUse the repo's lint script first (`make lint`, `task lint`). Otherwise detect from `.golangci.yml`/`.golangci.yaml`:\n\n- `.golangci.yml` present → `golangci-lint run ./...`\n- No config → `gofmt -w .` and `go vet ./...`\n- `goimports` config / pre-commit hook → `goimports -w path/to/file.go`\n\nNever disable existing linters in the test files you generate.\n\n## Project Layout and Imports\n\nGo uses package paths derived from the module path in `go.mod`.\n\n| Scenario | Test placement | Package declaration |\n|----------|----------------|----------------------|\n| Internal-only test (white-box) | `foo_test.go` next to `foo.go` | `package foo` (same as production) |\n| External-only test (black-box) | `foo_test.go` next to `foo.go` | `package foo_test` (forces use of public API) |\n| Integration / build-tag gated | `foo_integration_test.go` | Add `//go:build integration` at top |\n\n- Test files **must** end with `_test.go` — the toolchain ignores other names\n- A package directory may contain both `package foo` and `package foo_test` test files simultaneously\n- Helpers shared across tests in one package go in `helpers_test.go` — do not export them; put them in the `_test` package only if integration tests in another package need them\n- Imports use the full module path: `import \"github.com/org/module/pkg\"` — copy the exact module path from `go.mod`\n\n## Test Function Signatures\n\n| Kind | Signature |\n|------|-----------|\n| Standard test | `func TestThing(t *testing.T)` |\n| Subtests | `t.Run(\"name\", func(t *testing.T) { ... })` |\n| Benchmark | `func BenchmarkThing(b *testing.B)` |\n| Example (godoc) | `func ExampleThing()` with `// Output:` comment |\n| Fuzz (Go 1.18+) | `func FuzzThing(f *testing.F)` |\n| Per-package setup | `func TestMain(m *testing.M)` — call `m.Run()` and `os.Exit` with its code |\n\nUse **table-driven tests** when generating multiple cases for the same behavior — this is idiomatic Go and matches what most repos already use:\n\n```go\nfunc TestAdd(t *testing.T) {\n tests := []struct {\n name string\n a, b int\n want int\n }{\n {\"positives\", 2, 3, 5},\n {\"negatives\", -1, -1, -2},\n }\n for _, tt := range tests {\n t.Run(tt.name, func(t *testing.T) {\n if got := Add(tt.a, tt.b); got != tt.want {\n t.Errorf(\"Add(%d,%d) = %d, want %d\", tt.a, tt.b, got, tt.want)\n }\n })\n }\n}\n```\n\nWhen iterating with `t.Run` over a loop variable on Go \u003c 1.22, capture it with `tt := tt` to avoid closure-over-loop-variable bugs.\n\n## Common Errors\n\n| Error | Fix |\n|-------|-----|\n| `package X is not in std` / `cannot find module providing package X` | Add the import to `go.mod`: `go get path/to/module@version`, then `go mod tidy` |\n| `import cycle not allowed in test` | Move shared helpers to a separate package, or switch to a `_test` package for black-box tests |\n| `undefined: X` in `_test` package | The symbol is unexported; either use `package foo` (white-box) or export it intentionally |\n| `t.Parallel called multiple times` | Each subtest can call `t.Parallel()` once; do not call it twice in the same test |\n| `panic: test executed panic(nil) or runtime.Goexit` | A goroutine called `t.Fatal` outside the test goroutine; only the main test goroutine may call `Fatal`/`FailNow` |\n| `flag provided but not defined: -X` | Flags registered in `init()` of test files must use `flag.NewFlagSet` carefully; place test-only flags in `TestMain` |\n| `go: cannot find main module` | Run inside the module directory (where `go.mod` lives), or use `-C path` (Go 1.20+) |\n| `build constraints exclude all Go files in...` | Build tags filtered out every file — match the repo's tag with `-tags=integration` etc. |\n| `missing go.sum entry for module` | Run `go mod download` or `go mod tidy` |\n| Race detector reports data race | Fix the race; do not silence it. CGO must be enabled |\n\n## Mocking Rules\n\nGo has no reflection-based mocking framework that's universally adopted. Pick what the repo already uses:\n\n- **Interfaces + hand-written fakes** (most idiomatic) — define a small interface in the consumer package and pass a struct that implements it\n- **`gomock` / `mockgen`** — if the repo has `//go:generate mockgen ...` directives or `mocks/` directories, regenerate via `go generate ./...` rather than editing generated files\n- **`testify/mock`** — used in many repos; instantiate with `new(MockX)` and chain `.On(\"Method\", ...).Return(...)`\n- **`httptest`** — for HTTP clients/servers; spin up `httptest.NewServer` instead of mocking `http.Client`\n\nAlways prefer dependency injection over global function patching. If a test needs more than 3 mocks, flag it as a design smell.\n\n## Concurrency and Cleanup\n\n- Use `t.Cleanup(func() { ... })` instead of deferring in test bodies — runs even if `t.FailNow` fires\n- Use `t.TempDir()` for temp files — auto-cleaned at test end\n- Use `t.Context()` (Go 1.24+) or pass an explicit `context.Background()` — never call real network or filesystem APIs without one in long-running tests\n\n## Dependency Installation (Last Resort)\n\nOnly install packages after investigation confirms they are missing:\n\n```\ngo get github.com/stretchr/testify@latest\ngo mod tidy\n```\n\nRun `go mod tidy` after any `go get` to keep `go.sum` consistent. Never edit `go.sum` by hand.\n\n## Skip Coverage Tools\n\nDo not configure or run coverage tools (`-cover`, `-coverprofile`, `go tool cover`). Coverage is measured separately by the evaluation harness.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":8006,"content_sha256":"4024cdf22cc092cb120406a74c49e58ab5af4802fa446e12416f399df7a94807"},{"filename":"extensions/java-examples.md","content":"# Java Pipeline Examples\n\nConcrete input→output examples for the test generation pipeline targeting a Java codebase using JUnit 5 + Mockito. These show what each pipeline phase produces for a small project.\n\n## Source Under Test\n\nA simple `InvoiceService` in a Maven project using JUnit 5:\n\n```text\npom.xml\nsrc/main/java/com/contoso/billing/\n InvoiceService.java\n Invoice.java (mutable POJO with status, taxRate, lineItems and setStatus / setPaidDate mutators)\n InvoiceStatus.java (enum: PENDING, PAID)\n InvoiceRepository.java (interface)\nsrc/test/java/com/contoso/billing/ (exists, empty)\n```\n\n```java\n// src/main/java/com/contoso/billing/InvoiceService.java\npackage com.contoso.billing;\n\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\nimport java.time.Clock;\nimport java.time.LocalDateTime;\nimport java.util.Optional;\n\npublic class InvoiceService {\n\n private final InvoiceRepository repository;\n private final Clock clock;\n\n public InvoiceService(InvoiceRepository repository) {\n this(repository, Clock.systemUTC());\n }\n\n public InvoiceService(InvoiceRepository repository, Clock clock) {\n this.repository = repository;\n this.clock = clock;\n }\n\n public BigDecimal calculateTotal(Invoice invoice) {\n if (invoice == null) {\n throw new IllegalArgumentException(\"invoice must not be null\");\n }\n if (invoice.lineItems().isEmpty()) {\n throw new IllegalStateException(\"Invoice has no line items.\");\n }\n BigDecimal subtotal = invoice.lineItems().stream()\n .map(li -> li.unitPrice().multiply(BigDecimal.valueOf(li.quantity())))\n .reduce(BigDecimal.ZERO, BigDecimal::add);\n BigDecimal tax = subtotal.multiply(invoice.taxRate());\n return subtotal.add(tax).setScale(2, RoundingMode.HALF_UP);\n }\n\n public Invoice getById(int id) {\n Optional\u003cInvoice> invoice = repository.find(id);\n return invoice.orElseThrow(\n () -> new IllegalArgumentException(\"Invoice \" + id + \" not found.\"));\n }\n\n public void markAsPaid(int id) {\n Invoice invoice = repository.find(id)\n .orElseThrow(() -> new IllegalArgumentException(\"Invoice \" + id + \" not found.\"));\n if (invoice.status() == InvoiceStatus.PAID) {\n throw new IllegalStateException(\"Invoice is already paid.\");\n }\n invoice.setStatus(InvoiceStatus.PAID);\n invoice.setPaidDate(LocalDateTime.now(clock));\n repository.update(invoice);\n }\n}\n```\n\n## Sample Research Output\n\nWhat `code-testing-researcher` produces in `\u003cTESTAGENT_DIR>/research.md`:\n\n```markdown\n# Test Generation Research\n\n## Project Overview\n- **Path**: /work/billing\n- **Language**: Java 21 (`\u003cmaven.compiler.release>21\u003c/maven.compiler.release>`)\n- **Build Tool**: Maven (wrapper `./mvnw` present)\n- **Test Framework**: JUnit 5 (Jupiter 5.10) + Mockito 5.x (detected in pom.xml)\n- **Assertion library**: built-in `Assertions` (no AssertJ/Hamcrest in deps)\n\n## Coverage Baseline\n- **Initial Line Coverage**: unknown\n- **Strategy**: broad\n- **Existing Test Count**: 0 tests across 0 files\n\n## Build & Test Commands\n- **Compile**: `./mvnw -q test-compile`\n- **Test**: `./mvnw -q test`\n- **Single class**: `./mvnw -q test -Dtest=InvoiceServiceTest`\n- **Single method**: `./mvnw -q test -Dtest=InvoiceServiceTest#calculateTotal_validLineItems_returnsExpectedTotal`\n\n## Project Structure\n- Source: `src/main/java/com/contoso/billing/`\n- Tests: `src/test/java/com/contoso/billing/` (exists, empty)\n\n## Files to Test\n\n### High Priority\n| File | Classes/Methods | Testability | Notes |\n|------|-----------------|-------------|-------|\n| InvoiceService.java | calculateTotal, getById, markAsPaid | High | Repository dependency mockable via Mockito; clock injection available for time-dependent test |\n\n## Testing Patterns\n- No existing patterns; recommend JUnit 5 + Mockito with `@ExtendWith(MockitoExtension.class)`, `@Mock` / `@InjectMocks` fields, `@ParameterizedTest` + `@CsvSource` for table-driven `calculateTotal`, and `Clock.fixed(...)` for `markAsPaid` timestamp.\n\n## Recommendations\n- Test class lives in the same package (`com.contoso.billing`) for package-private access if needed\n- Inject `Clock.fixed(...)` rather than mocking `LocalDateTime.now(...)` — the service already accepts a Clock\n```\n\n## Sample Plan Output\n\n```markdown\n# Test Implementation Plan\n\n## Overview\nGenerate JUnit 5 + Mockito tests for InvoiceService, covering all three public\nmethods across happy path, edge case, and error scenarios. Single phase since\nthere is only one source file.\n\n## Commands\n- **Compile**: `./mvnw -q test-compile`\n- **Test**: `./mvnw -q test -Dtest=InvoiceServiceTest`\n\n## Phase 1: InvoiceService\n\n### Files to Test\n\n#### 1. InvoiceService.java\n- **Source**: `src/main/java/com/contoso/billing/InvoiceService.java`\n- **Test File**: `src/test/java/com/contoso/billing/InvoiceServiceTest.java`\n\n**Methods to Test**:\n1. `calculateTotal` — pure logic (parameterized)\n - Happy paths: single item w/ tax, multi-quantity zero tax, rounding-half-up\n - Error cases: null invoice → IllegalArgumentException; empty line items → IllegalStateException\n2. `getById` — happy + missing\n3. `markAsPaid` — happy (verify status + paid date via fixed clock + verify update) + already-paid + missing\n```\n\n## Sample Generated Test File\n\n```java\n// src/test/java/com/contoso/billing/InvoiceServiceTest.java\npackage com.contoso.billing;\n\nimport java.math.BigDecimal;\nimport java.time.Clock;\nimport java.time.Instant;\nimport java.time.LocalDateTime;\nimport java.time.ZoneOffset;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.CsvSource;\n\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\n@ExtendWith(MockitoExtension.class)\nclass InvoiceServiceTest {\n\n @Mock\n InvoiceRepository repository;\n\n @InjectMocks\n InvoiceService sut;\n\n // --- calculateTotal ---\n\n @ParameterizedTest(name = \"qty={0} unitPrice={1} taxRate={2} -> {3}\")\n @CsvSource({\n \"1, 100.00, 0.10, 110.00\",\n \"3, 25.00, 0.00, 75.00\",\n \"2, 9.99, 0.07, 21.38\"\n })\n void calculateTotal_validLineItems_returnsExpectedTotal(\n int quantity, BigDecimal unitPrice, BigDecimal taxRate, BigDecimal expected\n ) {\n Invoice invoice = new Invoice(1, InvoiceStatus.PENDING, taxRate,\n List.of(new LineItem(quantity, unitPrice)));\n\n BigDecimal total = sut.calculateTotal(invoice);\n\n assertEquals(0, total.compareTo(expected),\n () -> \"expected \" + expected + \" but got \" + total);\n }\n\n @Test\n @DisplayName(\"null invoice throws IllegalArgumentException\")\n void calculateTotal_nullInvoice_throws() {\n assertThrows(IllegalArgumentException.class, () -> sut.calculateTotal(null));\n }\n\n @Test\n void calculateTotal_emptyLineItems_throws() {\n Invoice invoice = new Invoice(1, InvoiceStatus.PENDING, BigDecimal.ZERO, List.of());\n\n IllegalStateException ex = assertThrows(IllegalStateException.class,\n () -> sut.calculateTotal(invoice));\n assertEquals(\"Invoice has no line items.\", ex.getMessage());\n }\n\n // --- getById ---\n\n @Test\n void getById_existingId_returnsInvoice() {\n Invoice expected = new Invoice(42, InvoiceStatus.PENDING, BigDecimal.ZERO, List.of());\n when(repository.find(42)).thenReturn(Optional.of(expected));\n\n assertSame(expected, sut.getById(42));\n }\n\n @Test\n void getById_missingId_throws() {\n when(repository.find(999)).thenReturn(Optional.empty());\n\n IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,\n () -> sut.getById(999));\n assertEquals(\"Invoice 999 not found.\", ex.getMessage());\n }\n\n // --- markAsPaid (uses an injected fixed Clock instead of @InjectMocks) ---\n\n @Test\n void markAsPaid_pendingInvoice_transitionsToPaidAndPersists() {\n Clock fixed = Clock.fixed(Instant.parse(\"2025-01-01T12:00:00Z\"), ZoneOffset.UTC);\n InvoiceService service = new InvoiceService(repository, fixed);\n Invoice invoice = new Invoice(1, InvoiceStatus.PENDING, BigDecimal.ZERO, List.of());\n when(repository.find(1)).thenReturn(Optional.of(invoice));\n\n service.markAsPaid(1);\n\n assertEquals(InvoiceStatus.PAID, invoice.status());\n assertEquals(LocalDateTime.ofInstant(fixed.instant(), ZoneOffset.UTC), invoice.paidDate());\n verify(repository).update(invoice);\n }\n\n @Test\n void markAsPaid_alreadyPaid_throwsAndDoesNotUpdate() {\n Invoice invoice = new Invoice(1, InvoiceStatus.PAID, BigDecimal.ZERO, List.of());\n when(repository.find(1)).thenReturn(Optional.of(invoice));\n\n assertThrows(IllegalStateException.class, () -> sut.markAsPaid(1));\n verify(repository, never()).update(any());\n }\n\n @Test\n void markAsPaid_missingId_throws() {\n when(repository.find(999)).thenReturn(Optional.empty());\n\n assertThrows(IllegalArgumentException.class, () -> sut.markAsPaid(999));\n }\n}\n```\n\n## Sample Fix Cycle\n\nWhen the implementer hits a compile or runtime error, the fixer agent diagnoses and resolves it.\n\n**Test output:**\n\n```text\n[ERROR] No tests found for given includes: [com.contoso.billing.InvoiceServiceTest]\n```\n\n**Fixer diagnosis:** Surefire only includes `**/*Test.class` (default). The class is `InvoiceServiceTest` (correct) but it was created under `src/test/java/com/contoso/billing/` with **no** package declaration. Maven compiles it into the default package, so `-Dtest=com.contoso.billing.InvoiceServiceTest` doesn't match.\n\n**Fix applied:** Add `package com.contoso.billing;` at the top of the test file so it lands in the expected package.\n\n**Rebuild + rerun:** `./mvnw -q test -Dtest=InvoiceServiceTest` → SUCCESS\n\n---\n\n**Another common cycle — wrong Mockito setup:**\n\n**Test output:**\n\n```text\norg.mockito.exceptions.misusing.UnnecessaryStubbingException:\nUnnecessary stubbings detected.\n 1. -> at InvoiceServiceTest.calculateTotal_nullInvoice_throws(InvoiceServiceTest.java:55)\n```\n\n**Fixer diagnosis:** `@MockitoExtension` runs in strict mode by default — stubbed calls (`when(repository.find(...)).thenReturn(...)`) must be used. The test stubbed `repository` in a `@BeforeEach` for every test, but `calculateTotal_nullInvoice_throws` never touches the repository.\n\n**Fix applied:** Move stubs into the tests that actually need them (as shown in the generated file above), rather than a single shared `@BeforeEach`.\n\n**Rebuild + rerun:** SUCCESS\n\n## Sample Final Report\n\n```markdown\n## Test Generation Report\n\n**Project**: billing (Java / Maven)\n**Strategy**: Direct (single source file in scope)\n\n### Results\n| Metric | Value |\n|----------------|-------|\n| Tests created | 8 |\n| Tests passing | 8 |\n| Tests failing | 0 |\n| Files created | 1 |\n\n### Files Created\n- `src/test/java/com/contoso/billing/InvoiceServiceTest.java` (8 tests, 3 parameterized cases via @CsvSource)\n\n### Coverage\n- InvoiceService.calculateTotal — 3 happy path, 2 error cases\n- InvoiceService.getById — happy + missing\n- InvoiceService.markAsPaid — happy (fixed Clock) + already-paid + missing\n\n### Build / Test Validation\n- `./mvnw -q test-compile`: ✅\n- `./mvnw -q test`: ✅ Tests run: 8, Failures: 0, Errors: 0\n\n### Next Steps\n- Add AssertJ if the team standardises on it (more expressive assertions)\n- Consider Testcontainers for true repository integration tests\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":12165,"content_sha256":"c6b0f5662b6a1c1526fd1315d77ab07539e688abeb64e6ccce747ff8b70ae492"},{"filename":"extensions/java.md","content":"# Java Extension\n\nLanguage-specific guidance for Java test generation.\n\n## Rule #1: Investigate the Repo First\n\nBefore writing any test or running any command, read:\n\n1. **Existing tests** — find `*Test.java` / `*Tests.java` / `*IT.java` (integration) files and copy their style (JUnit version, assertion library, mock library, lifecycle methods)\n2. **Build file** — `pom.xml` (Maven), `build.gradle` / `build.gradle.kts` (Gradle), `BUILD` / `BUILD.bazel` (Bazel)\n3. **Java version** — `\u003cmaven.compiler.release>`, `sourceCompatibility`, or `toolchains` block\n4. **Wrapper scripts** — always prefer `./mvnw` or `./gradlew` over a system-installed Maven/Gradle so you match the project's pinned version\n\nUse whatever framework the repo already uses (JUnit 4, JUnit 5/Jupiter, TestNG). Do not migrate to a different framework as a side effect of writing tests.\n\n## Build Tool Detection\n\n| Indicator | Build tool | Default test command |\n|-----------|------------|----------------------|\n| `pom.xml` | Maven | `./mvnw test` |\n| `build.gradle` / `build.gradle.kts` | Gradle | `./gradlew test` |\n| `settings.gradle*` with `include 'subproject'` | Gradle multi-project | `./gradlew :subproject:test` |\n| `BUILD` / `BUILD.bazel` | Bazel | `bazel test //path/to:test` |\n\nIf both `pom.xml` and `build.gradle` exist, pick the one used by CI.\n\n## Build Commands\n\n| Scope | Maven | Gradle |\n|-------|-------|--------|\n| Compile main + test | `./mvnw test-compile` | `./gradlew testClasses` |\n| Compile only | `./mvnw compile` | `./gradlew classes` |\n| Full build | `./mvnw verify` | `./gradlew build` |\n| Skip tests during build | `./mvnw -DskipTests package` | `./gradlew assemble` |\n\n- Use `-q` (Maven) / `--console=plain` (Gradle) to reduce output noise\n- For Gradle, prefer `--no-daemon` only in CI; locally the daemon makes incremental builds far faster\n\n## Test Commands\n\n| Scope | Maven | Gradle |\n|-------|-------|--------|\n| All unit tests | `./mvnw test` | `./gradlew test` |\n| Single class | `./mvnw test -Dtest=MyClassTest` | `./gradlew test --tests MyClassTest` |\n| Single method | `./mvnw test -Dtest=MyClassTest#myMethod` | `./gradlew test --tests MyClassTest.myMethod` |\n| Tag filter (JUnit 5) | `./mvnw test -Dgroups=fast` | `./gradlew test -PincludeTags=fast` (if configured) or `--tests` |\n| Integration tests | `./mvnw verify -DskipUnitTests` (with failsafe-plugin) | `./gradlew integrationTest` (if registered) |\n\n- `Surefire` runs unit tests (`*Test.java`); `Failsafe` runs integration tests (`*IT.java`) — do not put long integration tests under Surefire\n- Gradle's `--tests` accepts wildcards: `--tests \"*MyMethod*\"`\n- Use `--rerun-tasks` (Gradle) or `-DforkCount=...` (Surefire) only when troubleshooting cache issues\n\n## Lint Command\n\nUse the repo's existing lint task first. Otherwise check for:\n\n- Checkstyle (`checkstyle.xml`, `\u003cplugin>checkstyle\u003c/plugin>`) → `./mvnw checkstyle:check` or `./gradlew checkstyleMain`\n- Spotless (`spotless` block / plugin) → `./mvnw spotless:apply` or `./gradlew spotlessApply`\n- ErrorProne / NullAway → integrated into compilation; run a normal build\n- google-java-format / palantir-java-format → use the repo's configured formatter\n\nNever disable existing checks in the test files you generate.\n\n## Project Layout and Imports\n\nMaven/Gradle conventional layout:\n\n```\nsrc/\n├── main/java/com/example/foo/Bar.java\n├── main/resources/\n├── test/java/com/example/foo/BarTest.java\n└── test/resources/\n```\n\n| Layout | Test placement |\n|--------|----------------|\n| Standard | `src/test/java/\u003csame package as production class>/\u003cClassName>Test.java` |\n| Integration tests separated | `src/integrationTest/java/...` (Gradle) or `src/it/java/...` (Maven w/ failsafe) |\n| Multi-module Maven | Tests live in the same module as the code under test |\n\n- Test classes must mirror the production class's **package** to access package-private members\n- Avoid wildcard imports unless the repo already uses them — match the explicit imports shown in the templates below\n- For JUnit 5: import `org.junit.jupiter.api.Test` (and other annotations as needed) and `org.junit.jupiter.api.Assertions.assertEquals` etc. as static imports\n- For JUnit 4: import `org.junit.Test`, `org.junit.Before`, etc., and `org.junit.Assert.assertEquals` etc. as static imports\n\n## Test Framework Detection\n\n| Indicator | Framework | Annotations | Assertion style |\n|-----------|-----------|-------------|------------------|\n| `junit-jupiter-*` deps | JUnit 5 | `@Test`, `@ParameterizedTest`, `@BeforeEach`, `@DisplayName` | `Assertions.assertEquals(expected, actual)` |\n| `junit:junit:4.x` | JUnit 4 | `@Test`, `@Before`, `@RunWith` | `Assert.assertEquals(expected, actual)` |\n| `org.testng:testng` | TestNG | `@Test(groups=...)`, `@BeforeMethod` | `Assert.assertEquals(actual, expected)` (note **reversed** order) |\n| `org.assertj:assertj-core` | AssertJ (assertions only) | n/a | `assertThat(actual).isEqualTo(expected)` |\n| `org.hamcrest:hamcrest` | Hamcrest matchers | n/a | `assertThat(actual, is(equalTo(expected)))` |\n\n**Argument order matters**: JUnit/AssertJ use `(expected, actual)`; TestNG uses `(actual, expected)`. Reversing them produces confusing failure messages.\n\n## JUnit 5 Template\n\n```java\npackage com.example.foo;\n\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.CsvSource;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\nclass CalculatorTest {\n\n @Test\n @DisplayName(\"add returns sum of two positive numbers\")\n void add_positiveNumbers_returnsSum() {\n Calculator sut = new Calculator();\n assertEquals(5, sut.add(2, 3));\n }\n\n @ParameterizedTest\n @CsvSource({\n \"2, 3, 5\",\n \"-1, 1, 0\"\n })\n void add_validInputs_returnsSum(int a, int b, int expected) {\n assertEquals(expected, new Calculator().add(a, b));\n }\n\n @Test\n void divide_byZero_throws() {\n Calculator sut = new Calculator();\n assertThrows(ArithmeticException.class, () -> sut.divide(1, 0));\n }\n}\n```\n\n## Common Errors\n\n| Error | Fix |\n|-------|-----|\n| `package X does not exist` | Add the dependency to `pom.xml` / `build.gradle`; run `./mvnw dependency:resolve` or `./gradlew --refresh-dependencies` |\n| `cannot find symbol` | Verify class name and import path; check that the test source set sees the production source set |\n| `No tests found for given includes` (Gradle) | `--tests` pattern doesn't match; verify the class/method names, that test methods are annotated with `@Test`, and that the class name matches the test task's `include` pattern (default `**/*Test*.class`). For JUnit 4 only, the class must also be `public` with a public no-arg constructor — JUnit 5 allows package-private classes and methods |\n| `Test class should have exactly one public zero-argument constructor` (JUnit 4) | Remove constructors with parameters; use `@Before` for setup |\n| `org.junit.runners.model.InvalidTestClassError` (JUnit 4) | Class is missing `public`, has wrong constructor, or method signature is wrong |\n| Mixing `org.junit.Test` (4) and `org.junit.jupiter.api.Test` (5) | Pick one framework per test class — imports must match the framework annotation |\n| `java.lang.NoClassDefFoundError` at runtime | Test runtime classpath is missing a transitive dep; add it to `testRuntimeOnly` (Gradle) or `\u003cscope>test\u003c/scope>` (Maven) |\n| `UnsupportedClassVersionError` | JDK used to run tests is older than the JDK used to compile; align toolchains |\n| `Mockito cannot mock final class` | Use Mockito's inline mock maker — Mockito 5+ uses it by default; for Mockito 3.x/4.x add the `mockito-inline` artifact (replaces `mockito-core`). Or switch to MockK for Kotlin. `mockito-subclass` does **not** mock final classes |\n| `WrongTypeOfReturnValue` (Mockito) | The stubbed method returns a different type than the mock was set up for — check return type signatures |\n\n## Mocking Rules\n\n- Use whatever the repo already uses: **Mockito** (most common), **EasyMock**, **JMockit**, or hand-written fakes\n- For JUnit 5 + Mockito, use `@ExtendWith(MockitoExtension.class)` with `@Mock` / `@InjectMocks` fields\n- For JUnit 4 + Mockito, use `@RunWith(MockitoJUnitRunner.class)` or `MockitoAnnotations.openMocks(this)` in `@Before`\n- Use `when(mock.method(...)).thenReturn(...)` for stubs and `verify(mock).method(...)` for interactions\n- Use `ArgumentCaptor` to assert on complex argument values rather than over-specifying matchers\n- Prefer constructor injection so production code stays testable without `@InjectMocks`\n- If a test needs more than 3 mocks, flag it as a design smell\n\n## Spring Boot\n\nIf the repo uses Spring Boot:\n\n- `@SpringBootTest` loads the full context — slow; use only when needed\n- Slice tests are faster: `@WebMvcTest`, `@DataJpaTest`, `@JsonTest`\n- Use `@MockBean` (Spring) only inside Spring tests; in plain unit tests use `@Mock`\n- Use `@Testcontainers` for real-DB integration tests if the repo already has it on the classpath\n\n## Dependency Installation (Last Resort)\n\nOnly add dependencies after investigation confirms they are missing.\n\nMaven (`pom.xml`):\n\n```xml\n\u003cdependency>\n \u003cgroupId>org.junit.jupiter\u003c/groupId>\n \u003cartifactId>junit-jupiter\u003c/artifactId>\n \u003cversion>5.10.2\u003c/version>\n \u003cscope>test\u003c/scope>\n\u003c/dependency>\n```\n\nGradle (`build.gradle.kts`):\n\n```kotlin\ntestImplementation(\"org.junit.jupiter:junit-jupiter:5.10.2\")\ntestRuntimeOnly(\"org.junit.platform:junit-platform-launcher\")\n```\n\nIf the repo uses BOMs (`\u003cdependencyManagement>` or Gradle platforms), reuse them — don't pin a different version than the BOM publishes.\n\n## Skip Coverage Tools\n\nDo not configure or run coverage tools (JaCoCo, Cobertura, OpenClover). Coverage is measured separately by the evaluation harness.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":9933,"content_sha256":"25b32c779b49e17dff4cbb44fa3003cd33edba27592a9f1d0ab28c135eb04125"},{"filename":"extensions/kotlin-examples.md","content":"# Kotlin Pipeline Examples\n\nConcrete input→output examples for the test generation pipeline targeting a Kotlin JVM codebase using Gradle + JUnit 5. These show what each pipeline phase produces for a small project.\n\n> `kotlin.test` follows the same shape for multiplatform projects. Replace JUnit Jupiter parameterization with `@Test` methods or the repo's established KMP data pattern, and place tests under `src/commonTest/kotlin` or the matching target source set.\n\n## Source Under Test\n\nA simple `InvoiceService` in a Gradle Kotlin JVM project:\n\n```text\nsettings.gradle.kts\nbuild.gradle.kts\nsrc/main/kotlin/com/contoso/billing/\n Invoice.kt\n InvoiceRepository.kt\n InvoiceService.kt\nsrc/test/kotlin/com/contoso/billing/ (exists, empty)\n```\n\n```kotlin\n// src/main/kotlin/com/contoso/billing/InvoiceService.kt\npackage com.contoso.billing\n\nimport java.math.BigDecimal\nimport java.math.RoundingMode\nimport java.time.Clock\nimport java.time.LocalDateTime\n\nclass InvoiceService(\n private val repository: InvoiceRepository,\n private val clock: Clock = Clock.systemUTC(),\n) {\n fun calculateTotal(invoice: Invoice): BigDecimal {\n require(invoice.lineItems.isNotEmpty()) { \"Invoice has no line items.\" }\n\n val subtotal = invoice.lineItems\n .map { it.unitPrice.multiply(BigDecimal.valueOf(it.quantity.toLong())) }\n .fold(BigDecimal.ZERO, BigDecimal::add)\n val tax = subtotal.multiply(invoice.taxRate)\n return subtotal.add(tax).setScale(2, RoundingMode.HALF_UP)\n }\n\n fun getById(id: Int): Invoice = repository.find(id)\n ?: throw NoSuchElementException(\"Invoice $id not found.\")\n\n fun markAsPaid(id: Int) {\n val invoice = getById(id)\n check(invoice.status != InvoiceStatus.PAID) { \"Invoice is already paid.\" }\n\n invoice.status = InvoiceStatus.PAID\n invoice.paidAt = LocalDateTime.now(clock)\n repository.update(invoice)\n }\n}\n```\n\n## Sample Research Output\n\nWhat `code-testing-researcher` produces in `\u003cTESTAGENT_DIR>/research.md`:\n\n```markdown\n# Test Generation Research\n\n## Project Overview\n- **Path**: /work/contoso-billing\n- **Language**: Kotlin 2.0 JVM\n- **Build Tool**: Gradle wrapper present (`./gradlew`)\n- **Test Framework**: JUnit 5 + kotlin.test assertions (`useJUnitPlatform()` and `junit-jupiter-params` detected)\n- **Mocking**: MockK is not present; repository is an interface and can be faked directly\n\n## Coverage Baseline\n- **Initial Line Coverage**: unknown\n- **Strategy**: broad\n- **Existing Test Count**: 0 tests across 0 files\n\n## Build & Test Commands\n- **Compile tests**: `./gradlew compileTestKotlin --console=plain`\n- **Single class**: `./gradlew test --tests \"com.contoso.billing.InvoiceServiceTest\" --console=plain`\n- **All tests**: `./gradlew test --console=plain`\n\n## Files to Test\n\n### High Priority\n| File | Classes/Methods | Testability | Notes |\n|------|-----------------|-------------|-------|\n| src/main/kotlin/com/contoso/billing/InvoiceService.kt | calculateTotal, getById, markAsPaid | High | Repository interface is fakeable; Clock is injectable |\n\n## Testing Patterns\n- No existing patterns; recommend JUnit 5 `@Test`, `@ParameterizedTest` + `@CsvSource`, backticked test names, `kotlin.test` assertions, and a hand-written fake repository.\n```\n\n## Sample Plan Output\n\n```markdown\n# Test Implementation Plan\n\n## Overview\nGenerate JUnit 5 tests for InvoiceService covering calculation, lookup, and\npaid-state transition behavior.\n\n## Commands\n- **Compile tests**: `./gradlew compileTestKotlin --console=plain`\n- **Test**: `./gradlew test --tests \"com.contoso.billing.InvoiceServiceTest\" --console=plain`\n\n## Phase 1: InvoiceService\n\n### Files to Test\n- **Source**: `src/main/kotlin/com/contoso/billing/InvoiceService.kt`\n- **Test File**: `src/test/kotlin/com/contoso/billing/InvoiceServiceTest.kt`\n\n**Methods to Test**:\n1. `calculateTotal` — parameterized tax, zero tax, rounding, empty-line-items error\n2. `getById` — existing invoice and missing invoice\n3. `markAsPaid` — success with fixed clock, already-paid, missing\n```\n\n## Sample Generated Test File\n\n```kotlin\n// src/test/kotlin/com/contoso/billing/InvoiceServiceTest.kt\npackage com.contoso.billing\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.assertThrows\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.CsvSource\nimport java.math.BigDecimal\nimport java.time.Clock\nimport java.time.Instant\nimport java.time.LocalDateTime\nimport java.time.ZoneOffset\nimport kotlin.test.assertEquals\nimport kotlin.test.assertSame\nimport kotlin.test.assertTrue\n\nclass InvoiceServiceTest {\n\n private class FakeRepository : InvoiceRepository {\n val invoices = mutableMapOf\u003cInt, Invoice>()\n var updated: Invoice? = null\n\n override fun find(id: Int): Invoice? = invoices[id]\n\n override fun update(invoice: Invoice) {\n updated = invoice\n invoices[invoice.id] = invoice\n }\n }\n\n @ParameterizedTest(name = \"qty={0}, unitPrice={1}, taxRate={2} -> {3}\")\n @CsvSource(\n \"1, 100.00, 0.10, 110.00\",\n \"3, 25.00, 0.00, 75.00\",\n \"2, 9.99, 0.07, 21.38\",\n )\n fun `calculateTotal returns expected total for valid line items`(\n quantity: Int,\n unitPrice: BigDecimal,\n taxRate: BigDecimal,\n expected: BigDecimal,\n ) {\n val service = InvoiceService(FakeRepository())\n val invoice = invoice(\n taxRate = taxRate,\n lineItems = mutableListOf(LineItem(quantity = quantity, unitPrice = unitPrice)),\n )\n\n val total = service.calculateTotal(invoice)\n\n assertEquals(0, total.compareTo(expected), \"expected $expected but got $total\")\n }\n\n @Test\n fun `calculateTotal throws for empty line items`() {\n val service = InvoiceService(FakeRepository())\n val invoice = invoice(lineItems = mutableListOf())\n\n val exception = assertThrows\u003cIllegalArgumentException> { service.calculateTotal(invoice) }\n\n assertTrue(exception.message!!.contains(\"no line items\", ignoreCase = true))\n }\n\n @Test\n fun `getById returns existing invoice`() {\n val repository = FakeRepository()\n val expected = invoice(id = 42)\n repository.invoices[42] = expected\n val service = InvoiceService(repository)\n\n val result = service.getById(42)\n\n assertSame(expected, result)\n }\n\n @Test\n fun `getById throws for missing invoice`() {\n val service = InvoiceService(FakeRepository())\n\n val exception = assertThrows\u003cNoSuchElementException> { service.getById(999) }\n\n assertTrue(exception.message!!.contains(\"999\"))\n }\n\n @Test\n fun `markAsPaid updates status date and repository`() {\n val repository = FakeRepository()\n val invoice = invoice(id = 1)\n repository.invoices[1] = invoice\n val fixedClock = Clock.fixed(Instant.parse(\"2025-01-01T12:00:00Z\"), ZoneOffset.UTC)\n val service = InvoiceService(repository, fixedClock)\n\n service.markAsPaid(1)\n\n assertEquals(InvoiceStatus.PAID, invoice.status)\n assertEquals(LocalDateTime.ofInstant(fixedClock.instant(), ZoneOffset.UTC), invoice.paidAt)\n assertSame(invoice, repository.updated)\n }\n\n @Test\n fun `markAsPaid throws and does not update already paid invoice`() {\n val repository = FakeRepository()\n repository.invoices[1] = invoice(id = 1, status = InvoiceStatus.PAID)\n val service = InvoiceService(repository)\n\n val exception = assertThrows\u003cIllegalStateException> { service.markAsPaid(1) }\n\n assertTrue(exception.message!!.contains(\"already paid\", ignoreCase = true))\n assertEquals(null, repository.updated)\n }\n\n @Test\n fun `markAsPaid throws and does not update missing invoice`() {\n val repository = FakeRepository()\n val service = InvoiceService(repository)\n\n val exception = assertThrows\u003cNoSuchElementException> { service.markAsPaid(999) }\n\n assertEquals(\"Invoice 999 not found.\", exception.message)\n assertEquals(null, repository.updated)\n }\n\n private fun invoice(\n id: Int = 1,\n status: InvoiceStatus = InvoiceStatus.PENDING,\n taxRate: BigDecimal = BigDecimal(\"0.10\"),\n lineItems: MutableList\u003cLineItem> = mutableListOf(LineItem(quantity = 1, unitPrice = BigDecimal(\"100.00\"))),\n ): Invoice = Invoice(id = id, status = status, taxRate = taxRate, lineItems = lineItems, paidAt = null)\n}\n```\n\n## Sample Fix Cycle\n\nWhen the implementer hits a Gradle or Kotlin compile issue, the fixer agent diagnoses and resolves it.\n\n**Build output:**\n\n```text\nNo tests found for given includes: [com.contoso.billing.InvoiceServiceTest]\n```\n\n**Fixer diagnosis:** The test file was created under `src/test/java` with Kotlin source, so the Kotlin JVM source set did not compile it into the expected package.\n\n**Fix applied:** Move the file to `src/test/kotlin/com/contoso/billing/InvoiceServiceTest.kt` and keep `package com.contoso.billing` at the top.\n\n**Rebuild + rerun:** `./gradlew test --tests \"com.contoso.billing.InvoiceServiceTest\" --console=plain` → SUCCESS\n\n---\n\n**Another common cycle — JUnit Platform not enabled:**\n\n**Test output:**\n\n```text\n0 tests completed\n```\n\n**Fixer diagnosis:** The project has JUnit Jupiter dependencies but the Gradle `test` task is not configured with `useJUnitPlatform()`.\n\n**Fix applied:** Match the repo's build convention and add `tasks.test { useJUnitPlatform() }` if it is missing.\n\n**Rerun:** SUCCESS\n\n## Sample Final Report\n\n```markdown\n## Test Generation Report\n\n**Project**: contoso-billing (Kotlin / Gradle)\n**Strategy**: Direct (single source file in scope)\n\n### Results\n| Metric | Value |\n|----------------|-------|\n| Tests created | 9 |\n| Tests passing | 9 |\n| Tests failing | 0 |\n| Files created | 1 |\n\n### Files Created\n- `src/test/kotlin/com/contoso/billing/InvoiceServiceTest.kt` (9 JUnit 5 invocations: 6 ordinary `@Test` methods and 3 parameterized rows)\n\n### Coverage\n- InvoiceService.calculateTotal — 3 happy path, 1 error case\n- InvoiceService.getById — found and missing branches\n- InvoiceService.markAsPaid — success, already-paid and missing-invoice branches; error cases do not update the repository\n\n### Build / Test Validation\n- Compile tests: ✅ `./gradlew compileTestKotlin --console=plain`\n- Test run: ✅ `./gradlew test --tests \"com.contoso.billing.InvoiceServiceTest\" --console=plain`\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":10521,"content_sha256":"7edadb6affff115d9bcb50271ec8f75a52c6b3a406d0ccc6e1671f0aa3454ef5"},{"filename":"extensions/kotlin.md","content":"# Kotlin Extension\n\nLanguage-specific guidance for Kotlin test generation. For pure-Java codebases, use `java.md` instead.\n\n## Rule #1: Investigate the Repo First\n\nBefore writing any test or running any command, read:\n\n1. **Existing tests** — find files in `src/test/kotlin/`, `src/commonTest/kotlin/`, `src/jvmTest/kotlin/`, etc., and copy their style (framework, assertion library, mock library, coroutine helpers)\n2. **Build file** — `build.gradle.kts` / `build.gradle` — note Kotlin version, plugins (`kotlin(\"jvm\")`, `kotlin(\"multiplatform\")`, `kotlin(\"android\")`), and `dependencies { testImplementation(...) }`\n3. **`gradle/libs.versions.toml`** — the version catalog if the repo uses one; reference aliases instead of hard-coded versions\n4. **Wrapper script** — always invoke `./gradlew` (Unix) or `.\\gradlew.bat` (Windows), never a system-installed Gradle\n5. **Multiplatform layout** — `src/\u003csourceSet>/kotlin/` indicates KMP; tests live in matching `*Test` source sets (`commonTest`, `jvmTest`, `nativeTest`)\n\nUse whatever framework the repo already uses (JUnit Jupiter, JUnit 4, Kotest, kotlin.test). Do not switch.\n\n## Project Type Detection\n\n| Indicator | Project type |\n|-----------|--------------|\n| `kotlin(\"jvm\")` plugin | Plain JVM Kotlin |\n| `kotlin(\"multiplatform\")` plugin with `kotlin { jvm(); js(); ... }` | Kotlin Multiplatform |\n| `com.android.application` / `com.android.library` plugin | Android |\n| `org.springframework.boot` plugin | Spring Boot Kotlin |\n| `kotlin(\"jvm\")` + `application` plugin | Kotlin CLI / server |\n\nFor **Android**, see also platform-specific test types: `src/test/` for unit tests on the JVM, `src/androidTest/` for instrumented tests on a device/emulator. They use different runners and gradle tasks.\n\n## Build Commands\n\n| Scope | Command |\n|-------|---------|\n| Compile main + test (JVM) | `./gradlew compileTestKotlin` |\n| Full build | `./gradlew build` |\n| Skip tests | `./gradlew assemble` |\n| Single module | `./gradlew :module-name:build` |\n| KMP target only | `./gradlew :module:jvmTest` (or `linuxX64Test`, etc.) |\n\n- Use `--console=plain` to suppress Gradle's animated output\n- Use `--build-cache` (often default in CI) to reuse outputs\n- For Android: `./gradlew assembleDebug` (build APK) and `./gradlew testDebugUnitTest` (run unit tests)\n\n## Test Commands\n\n| Scope | Command |\n|-------|---------|\n| All tests (JVM) | `./gradlew test` |\n| Single class | `./gradlew test --tests \"com.example.WidgetTest\"` |\n| Single method | `./gradlew test --tests \"com.example.WidgetTest.add returns sum\"` |\n| KMP all targets | `./gradlew allTests` |\n| KMP one target | `./gradlew jvmTest`, `./gradlew jsTest`, `./gradlew linuxX64Test` |\n| Android unit tests | `./gradlew testDebugUnitTest` |\n| Android instrumented | `./gradlew connectedDebugAndroidTest` (requires device/emulator) |\n\n- `--tests` accepts wildcards: `--tests \"*Widget*\"`. Method names with spaces or backticks must be quoted: `--tests \"com.example.WidgetTest.creates a widget\"`\n- Use `--rerun-tasks` only when troubleshooting cache issues\n- For Kotest, the runner is registered with JUnit Platform — the standard `./gradlew test` and `--tests` flags work the same way\n\n## Lint Command\n\nUse the repo's lint tooling first:\n\n- `./gradlew ktlintCheck` (autoformat: `./gradlew ktlintFormat`) when ktlint is configured\n- `./gradlew detekt` when detekt is configured\n- `./gradlew spotlessCheck` / `spotlessApply` for the Spotless plugin\n- Android Studio's IDE inspections; `./gradlew lint` (Android-only) for the Android Lint task\n\n## Project Layout\n\n```\nsrc/\n├── main/kotlin/com/example/foo/Bar.kt\n├── main/resources/\n├── test/kotlin/com/example/foo/BarTest.kt # mirrors production package\n└── test/resources/\n```\n\nKMP layout:\n\n```\nsrc/\n├── commonMain/kotlin/... # shared\n├── commonTest/kotlin/... # shared tests using kotlin.test\n├── jvmMain/kotlin/...\n├── jvmTest/kotlin/...\n├── jsMain/kotlin/...\n└── jsTest/kotlin/...\n```\n\n- Test classes mirror the production class's package so they can access `internal` members (Kotlin's `internal` is module-scoped — within the same Gradle module, including the test source set)\n- For KMP common tests, you can only import from `kotlin.test` and other multiplatform-aware libraries (e.g. `kotlinx.coroutines.test`, Kotest multiplatform, MockK on JVM only)\n\n## Test Framework Detection\n\n| Dependency | Framework | Annotations / DSL |\n|------------|-----------|--------------------|\n| `org.jetbrains.kotlin:kotlin-test` | kotlin.test (multiplatform) | `@Test`, `@BeforeTest`, `assertEquals`, `assertFailsWith` |\n| `junit-jupiter-*` | JUnit 5 | `@Test`, `@ParameterizedTest`, `@BeforeEach`, `@DisplayName` |\n| `junit:junit:4.x` | JUnit 4 | `@Test`, `@Before`, `@RunWith(JUnitPlatform::class)` rare |\n| `io.kotest:kotest-runner-junit5` | Kotest | `class FooSpec : FunSpec({ test(\"...\") { ... } })` (DSL — many styles: `StringSpec`, `BehaviorSpec`, etc.) |\n| `org.spekframework.spek2:spek-dsl-jvm` | Spek 2 | `object FooSpec : Spek({ describe(...) { it(...) {} } })` (legacy) |\n\nFor Kotest, **stick to the spec style the repo already uses** — mixing styles is confusing.\n\n## Test Templates\n\n### JUnit 5\n\n```kotlin\npackage com.example.foo\n\nimport org.junit.jupiter.api.DisplayName\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.assertThrows\nimport kotlin.test.assertEquals\n\nclass CalculatorTest {\n\n @Test\n @DisplayName(\"add returns sum of two positive numbers\")\n fun `add returns sum of two positives`() {\n val sut = Calculator()\n assertEquals(5, sut.add(2, 3))\n }\n\n @Test\n fun `divide by zero throws`() {\n val sut = Calculator()\n assertThrows\u003cArithmeticException> { sut.divide(1, 0) }\n }\n}\n```\n\nBackticked method names (`` `like this` ``) are idiomatic for Kotlin tests because they read better in failure messages.\n\n### Kotest (StringSpec)\n\n```kotlin\npackage com.example.foo\n\nimport io.kotest.core.spec.style.StringSpec\nimport io.kotest.matchers.shouldBe\nimport io.kotest.assertions.throwables.shouldThrow\n\nclass CalculatorSpec : StringSpec({\n \"add returns sum of two positive numbers\" {\n Calculator().add(2, 3) shouldBe 5\n }\n\n \"divide by zero throws\" {\n shouldThrow\u003cArithmeticException> { Calculator().divide(1, 0) }\n }\n})\n```\n\n## Coroutines\n\n- Use `kotlinx-coroutines-test` when it's already on the classpath; otherwise add it as a `testImplementation` only after confirming it is missing (see Dependency Installation)\n- Use `runTest { ... }` (replaces the older `runBlockingTest`) for `suspend` test bodies\n- For virtual time advance, use a `TestDispatcher` built from `testScheduler` — e.g. `StandardTestDispatcher(testScheduler)` or `UnconfinedTestDispatcher(testScheduler)` — rather than calling `delay` and waiting in real time\n- Inject a `CoroutineDispatcher` into production code instead of using `Dispatchers.Main/IO` directly — then swap it in tests via `Dispatchers.setMain(testDispatcher)`\n\n```kotlin\n@Test\nfun `loads data eventually`() = runTest {\n val repo = FakeRepo()\n val dispatcher = StandardTestDispatcher(testScheduler)\n val sut = Loader(repo, dispatcher)\n sut.start()\n advanceUntilIdle()\n assertEquals(LoadState.Done, sut.state.value)\n}\n```\n\n## Common Errors\n\n| Error | Fix |\n|-------|-----|\n| `Unresolved reference: X` | Add the import; verify the test source set sees the production source set; for KMP, the dep may be declared only in `jvmTest` |\n| `Cannot access 'X': it is internal in module Y` | `internal` is module-scoped, so a test in another Gradle module cannot see it. Move the test into the same module, expose a public seam (e.g. a `*-testing` artifact, or change visibility deliberately), or add the consuming module to the source module's `friend modules` via the Kotlin compiler `-Xfriend-paths` option. `@VisibleForTesting` does **not** widen Kotlin visibility |\n| `Class 'XTest' is not abstract and does not implement abstract member` (Kotest spec) | The spec class needs a no-arg constructor and a primary-constructor block — match the existing spec style |\n| `No tests found for given includes` (Gradle) | `--tests` pattern doesn't match; verify class name and that the framework's runner is registered on the test task (`useJUnitPlatform()`) |\n| `kotlin.UninitializedPropertyAccessException: lateinit property X has not been initialized` | The `@BeforeEach` (or `BeforeTest`) didn't run, or the field was reset; use `lateinit` only after confirming the lifecycle hook fires |\n| `IllegalStateException: Module with the Main dispatcher had failed to initialize` | Coroutines test needs `Dispatchers.setMain(...)` before launching anything that touches `Dispatchers.Main`; reset with `Dispatchers.resetMain()` in teardown |\n| `Mockito cannot mock final class` | Kotlin classes are `final` by default — either use **MockK** (works with final classes) or apply the `kotlin-allopen` plugin scoped to a marker annotation |\n| `MissingMockKException` | The mock wasn't initialized; call `MockKAnnotations.init(this)` or use `@MockK` with `@MockKExtension` (JUnit 5) |\n| KMP common test references a JVM-only API | Move the test to `jvmTest`, or use `expect/actual` declarations |\n| Android: `Method ... not mocked` | The unit test runs on the JVM and the SDK class is just a stub — either use Robolectric, move the test to instrumented (`androidTest`), or refactor to inject the dependency |\n\n## Mocking Rules\n\n- **MockK** is the de-facto standard for Kotlin (final classes, coroutine support): `every { mock.foo() } returns 1`, `coEvery { mock.suspendFn() } returns 1`, `verify { mock.foo() }`, `coVerify { ... }`\n- Mockito works on Kotlin too with `mockito-kotlin` extensions, but Kotlin classes are `final` by default — use Mockito's inline mock maker (default in Mockito 5+; the `mockito-inline` artifact for Mockito 3.x/4.x). `mockito-subclass` cannot mock final classes\n- Avoid `mockkStatic`/`mockkObject` for production code you control — refactor to a wrapper instead\n- Prefer constructor injection so you don't need framework annotations (`@InjectMocks`) at all\n- If a test needs more than 3 mocks, flag it as a design smell\n\n## Android Specifics\n\n- Robolectric tests live under `src/test/` and emulate the Android framework on the JVM — fast but imperfect\n- Instrumented tests live under `src/androidTest/`, require a connected device/emulator, and are slow — use sparingly\n- Compose UI tests use `createComposeRule()` and `composeTestRule.onNodeWithText(...).performClick()` — match the existing test setup if Compose is in the project\n- Hilt: use `@HiltAndroidTest` and `HiltAndroidRule` for instrumented tests; for unit tests pass fakes directly to ViewModels\n\n## Dependency Installation (Last Resort)\n\nOnly add dependencies after investigation confirms they are missing.\n\n`build.gradle.kts`:\n\n```kotlin\ndependencies {\n testImplementation(\"org.junit.jupiter:junit-jupiter:5.10.2\")\n testImplementation(\"io.mockk:mockk:1.13.10\")\n testImplementation(\"org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.0\")\n}\n\ntasks.test {\n useJUnitPlatform()\n}\n```\n\nIf the repo uses a version catalog, add to `gradle/libs.versions.toml` and reference via `libs.junit.jupiter` etc. Match the major versions already in use.\n\n## Skip Coverage Tools\n\nDo not configure or run coverage tools (JaCoCo, Kover). Coverage is measured separately by the evaluation harness.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":11473,"content_sha256":"7553070dbf0254c1bb91c293ae8ce4313a68450cf1a451975d9f92d5367eb906"},{"filename":"extensions/powershell-examples.md","content":"# PowerShell Pipeline Examples\n\nConcrete input→output examples for the test generation pipeline targeting a PowerShell module using Pester v5. These show what each pipeline phase produces for a small module.\n\n## Source Under Test\n\nA simple `InvoiceService` module:\n\n```text\nsrc/\n Contoso.Billing.psd1\n Contoso.Billing.psm1\nTests/ (empty)\n```\n\n```powershell\n# src/Contoso.Billing.psm1\nenum InvoiceStatus {\n Pending\n Paid\n}\n\nfunction Get-InvoiceTotal {\n [CmdletBinding()]\n param([Parameter(Mandatory)][pscustomobject]$Invoice)\n\n if (-not $Invoice.LineItems -or $Invoice.LineItems.Count -eq 0) {\n throw 'Invoice has no line items.'\n }\n\n $subtotal = 0\n foreach ($lineItem in $Invoice.LineItems) {\n $subtotal += $lineItem.Quantity * $lineItem.UnitPrice\n }\n\n [math]::Round($subtotal + ($subtotal * $Invoice.TaxRate), 2)\n}\n\nfunction Get-InvoiceById {\n [CmdletBinding()]\n param([Parameter(Mandatory)][int]$Id, [Parameter(Mandatory)][scriptblock]$FindInvoice)\n\n $invoice = & $FindInvoice $Id\n if ($null -eq $invoice) {\n throw \"Invoice $Id not found.\"\n }\n\n $invoice\n}\n\nfunction Set-InvoicePaid {\n [CmdletBinding()]\n param(\n [Parameter(Mandatory)][int]$Id,\n [Parameter(Mandatory)][scriptblock]$FindInvoice,\n [Parameter(Mandatory)][scriptblock]$UpdateInvoice,\n [scriptblock]$GetNow = { Get-Date }\n )\n\n $invoice = Get-InvoiceById -Id $Id -FindInvoice $FindInvoice\n if ($invoice.Status -eq [InvoiceStatus]::Paid) {\n throw 'Invoice is already paid.'\n }\n\n $invoice.Status = [InvoiceStatus]::Paid\n $invoice.PaidDate = & $GetNow\n & $UpdateInvoice $invoice\n}\n\nExport-ModuleMember -Function Get-InvoiceTotal, Get-InvoiceById, Set-InvoicePaid\n```\n\n## Sample Research Output\n\nWhat `code-testing-researcher` produces in `\u003cTESTAGENT_DIR>/research.md`:\n\n```markdown\n# Test Generation Research\n\n## Project Overview\n- **Path**: C:\\work\\contoso-billing\n- **Language**: PowerShell 7.4\n- **Module**: `src/Contoso.Billing.psd1` imports `Contoso.Billing.psm1`\n- **Test Framework**: Pester v5\n\n## Coverage Baseline\n- **Initial Line Coverage**: unknown\n- **Strategy**: broad\n- **Existing Test Count**: 0 tests across 0 files\n\n## Build & Test Commands\n- **Module load**: `Import-Module ./src/Contoso.Billing.psd1 -Force -ErrorAction Stop`\n- **Discovery**: `Invoke-Pester -Configuration @{ Run = @{ Path = './Tests'; PassThru = $true; SkipRun = $true } }`\n- **Test**: `Invoke-Pester -Path ./Tests -Output Detailed`\n\n## Files to Test\n\n### High Priority\n| File | Functions | Testability | Notes |\n|------|-----------|-------------|-------|\n| src/Contoso.Billing.psm1 | Get-InvoiceTotal, Get-InvoiceById, Set-InvoicePaid | High | Dependencies are scriptblocks, easy to fake; clock is injectable |\n\n## Testing Patterns\n- No existing patterns; recommend Pester v5 `Describe` / `Context` / `It`, top-level `using module ../src/Contoso.Billing.psd1` for the module-defined enum, `-TestCases` for total calculations, and scriptblock fakes for repository operations.\n```\n\n## Sample Plan Output\n\n```markdown\n# Test Implementation Plan\n\n## Overview\nGenerate Pester v5 tests for total calculation, repository lookup, and the\npaid-state transition. Single phase since there is one module file.\n\n## Commands\n- **Import**: `Import-Module ./src/Contoso.Billing.psd1 -Force -ErrorAction Stop`\n- **Test**: `Invoke-Pester -Path ./Tests/Contoso.Billing.Tests.ps1 -Output Detailed`\n\n## Phase 1: Contoso.Billing\n\n### Files to Test\n- **Source**: `src/Contoso.Billing.psm1`\n- **Test File**: `Tests/Contoso.Billing.Tests.ps1`\n\n**Functions to Test**:\n1. `Get-InvoiceTotal` — table-driven happy paths and empty-line-items error\n2. `Get-InvoiceById` — existing invoice and missing invoice\n3. `Set-InvoicePaid` — status/date update and persistence; already-paid error; missing invoice error\n```\n\n## Sample Generated Test File\n\n```powershell\n# Tests/Contoso.Billing.Tests.ps1\nusing module ../src/Contoso.Billing.psd1\n\nBeforeAll {\n function New-TestInvoice {\n param(\n [int]$Id = 1,\n [InvoiceStatus]$Status = [InvoiceStatus]::Pending,\n [double]$TaxRate = 0.10,\n [object[]]$LineItems = @(@{ Quantity = 1; UnitPrice = 100.00 })\n )\n\n [pscustomobject]@{\n Id = $Id\n Status = $Status\n TaxRate = $TaxRate\n LineItems = $LineItems\n PaidDate = $null\n }\n }\n}\n\nDescribe 'Contoso.Billing invoice functions' {\n Context 'Get-InvoiceTotal' {\n It 'returns \u003cExpected> for \u003cName>' -TestCases @(\n @{ Name = 'single item with tax'; LineItems = @(@{ Quantity = 1; UnitPrice = 100.00 }); TaxRate = 0.10; Expected = 110.00 }\n @{ Name = 'multi quantity zero tax'; LineItems = @(@{ Quantity = 3; UnitPrice = 25.00 }); TaxRate = 0.00; Expected = 75.00 }\n @{ Name = 'rounds to two decimals'; LineItems = @(@{ Quantity = 2; UnitPrice = 9.99 }); TaxRate = 0.07; Expected = 21.38 }\n ) {\n param($LineItems, $TaxRate, $Expected)\n\n $invoice = New-TestInvoice -LineItems $LineItems -TaxRate $TaxRate\n\n Get-InvoiceTotal -Invoice $invoice | Should -BeExactly $Expected\n }\n\n It 'throws when the invoice has no line items' {\n $invoice = New-TestInvoice -LineItems @()\n\n { Get-InvoiceTotal -Invoice $invoice } | Should -Throw '*no line items*'\n }\n }\n\n Context 'Get-InvoiceById' {\n It 'returns an existing invoice' {\n $expected = New-TestInvoice -Id 42\n $findInvoice = { param($Id) if ($Id -eq 42) { $expected } }\n\n $result = Get-InvoiceById -Id 42 -FindInvoice $findInvoice\n\n [object]::ReferenceEquals($result, $expected) | Should -BeTrue\n }\n\n It 'throws when the invoice is missing' {\n $findInvoice = { $null }\n\n { Get-InvoiceById -Id 999 -FindInvoice $findInvoice } | Should -Throw '*999*'\n }\n }\n\n Context 'Set-InvoicePaid' {\n It 'marks a pending invoice as paid and persists it' {\n $invoice = New-TestInvoice -Id 1\n $script:updatedInvoice = $null\n $fixedNow = [datetime]'2025-01-01T12:00:00Z'\n $findInvoice = { param($Id) if ($Id -eq 1) { $invoice } }\n $updateInvoice = { param($Invoice) $script:updatedInvoice = $Invoice }\n\n Set-InvoicePaid -Id 1 -FindInvoice $findInvoice -UpdateInvoice $updateInvoice -GetNow { $fixedNow }\n\n $invoice.Status | Should -Be ([InvoiceStatus]::Paid)\n $invoice.PaidDate | Should -Be $fixedNow\n [object]::ReferenceEquals($script:updatedInvoice, $invoice) | Should -BeTrue\n }\n\n It 'throws and does not update an already-paid invoice' {\n $invoice = New-TestInvoice -Status ([InvoiceStatus]::Paid)\n $script:updatedInvoice = $null\n $findInvoice = { $invoice }\n $updateInvoice = { param($Invoice) $script:updatedInvoice = $Invoice }\n\n { Set-InvoicePaid -Id 1 -FindInvoice $findInvoice -UpdateInvoice $updateInvoice } | Should -Throw '*already paid*'\n $script:updatedInvoice | Should -BeNullOrEmpty\n }\n\n It 'throws and does not update a missing invoice' {\n $findInvoice = { $null }\n $script:wasUpdated = $false\n $updateInvoice = { param($Invoice) $script:wasUpdated = $true }\n\n { Set-InvoicePaid -Id 999 -FindInvoice $findInvoice -UpdateInvoice $updateInvoice } | Should -Throw -ExpectedMessage 'Invoice 999 not found.'\n $script:wasUpdated | Should -BeFalse\n }\n }\n}\n```\n\n## Sample Fix Cycle\n\nWhen the implementer hits a Pester discovery or run issue, the fixer agent diagnoses and resolves it.\n\n**Test output:**\n\n```text\nUnable to find type [InvoiceStatus].\n```\n\n**Fixer diagnosis:** Runtime `Import-Module` does not make a module-defined enum available while the test script is parsed. `using module` must appear at the top of the file and use a literal path relative to the test script.\n\n**Fix applied:** Add top-level `using module ../src/Contoso.Billing.psd1` and remove the runtime import (as shown above). For identity assertions use `[object]::ReferenceEquals(...) | Should -BeTrue`; Pester v5 does not provide `Should -BeSame`.\n\n**Rerun:** `Invoke-Pester -Path ./Tests/Contoso.Billing.Tests.ps1 -Output Detailed` → SUCCESS\n\n## Sample Final Report\n\n```markdown\n## Test Generation Report\n\n**Project**: contoso-billing (PowerShell)\n**Strategy**: Direct (single module in scope)\n\n### Results\n| Metric | Value |\n|----------------|-------|\n| Tests created | 9 |\n| Tests passing | 9 |\n| Tests failing | 0 |\n| Files created | 1 |\n\n### Files Created\n- `Tests/Contoso.Billing.Tests.ps1` (9 Pester invocations: 6 ordinary examples and 3 data-driven total rows)\n\n### Coverage\n- Get-InvoiceTotal — 3 happy path, 1 error case\n- Get-InvoiceById — found and missing branches\n- Set-InvoicePaid — success, already-paid and missing-invoice branches; error cases do not update the repository\n\n### Build / Test Validation\n- Module load: ✅ `Import-Module ./src/Contoso.Billing.psd1 -Force -ErrorAction Stop`\n- Discovery: ✅ Pester found 9 tests\n- Test run: ✅ `Invoke-Pester -Path ./Tests -Output Detailed`\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":9347,"content_sha256":"664d878564d384d578e70a11faebff324d255fb28a6fd2508e72ca36a738c797"},{"filename":"extensions/powershell.md","content":"# PowerShell Extension\n\nLanguage-specific guidance for PowerShell test generation using Pester v5.\n\n## Rule #0: Confirm the Test Target\n\nIf the prompt does not name a specific file (e.g. \"test the repository\", \"cover one core module\", \"comprehensive suite\"), do **not** assume the largest or top-level upstream code is the intended target. In real workflows the user usually wants to test code they have just added, and large upstream repos contain hundreds of scripts already covered by existing `*.Tests.ps1` files.\n\nRun these **read-only** discovery commands first — they are the deliberate exception to Rule #1's \"before writing any test or running any command\" rule, and their output is the ground truth Rule #1's reading is meant to interpret. Do **not** write or execute any tests until Rule #0 and Rule #1 are both complete.\n\n| Goal | Command |\n|------|---------|\n| List uncommitted edits + untracked files | `git status -s` |\n| Untracked files only (typical for newly-added modules) | `git ls-files --others --exclude-standard` |\n| Recently added scripts/modules | `git log --diff-filter=A --name-only -5 -- '*.ps1' '*.psm1' '*.psd1'` |\n| Modules with no matching `*.Tests.ps1` | compare `Get-ChildItem -Recurse -Include *.psm1,*.ps1` against `*.Tests.ps1` files |\n\nPrefer targets that match **all** of:\n\n1. Untracked or recently added (`git status` / `git log --diff-filter=A`).\n2. Small and pure (a few hundred lines, no external state, no `Invoke-WebRequest`/registry/filesystem side effects).\n3. Located under a conventional source root (`tools/`, `src/`, `Public/`, `Private/`, or the module root next to a `.psd1`).\n4. Have **no** existing matching `*.Tests.ps1` file.\n\nIf a `.psd1` manifest's `RootModule` (or `ModuleToProcess`) points at a specific `.psm1`, that module is almost certainly the target — start there.\n\n### Test Placement Contract\n\nPester only discovers tests under the path passed to `Invoke-Pester -Path` (or the current directory when no path is given). Verification harnesses (CI, msbench, coverage tools) typically scope discovery to a single directory such as `tools/` or `tests/`. Place every test file there, matching the existing convention in the repo:\n\n| Layout used by the repo | Test placement |\n|-------------------------|----------------|\n| Co-located convention (`Module.psm1` + `Module.Tests.ps1` side-by-side) | Drop `\u003cModule>.Tests.ps1` next to the source file (`tools/StringUtils.psm1` → `tools/StringUtils.Tests.ps1`). |\n| Sibling `Tests/` directory | Mirror the source path (`src/Foo/Bar.psm1` → `Tests/Foo/Bar.Tests.ps1`). |\n| Mixed / unknown | Co-locate next to the source — this is what Pester discovers by default and what most harnesses scope to. |\n\nA `*.Tests.ps1` file placed outside the discovery root will be invisible to both `Invoke-Pester` and the harness.\n\n### First-Test Sanity Loop\n\nAfter writing the **first** `*.Tests.ps1` file — before writing any others:\n\n1. Run `Invoke-Pester -Path \u003cdir> -PassThru` and confirm the `TotalCount` is `> 0`. If it is `0`, Pester is not discovering your file; fix the location, filename, or `Describe`/`It` structure before continuing.\n2. Run the test (`Invoke-Pester -Path \u003cyour.Tests.ps1> -Output Detailed`); fix `Import-Module` / dot-source / `BeforeAll` errors before adding more tests.\n3. Only then expand to cover the remaining functions.\n\nThis catches placement and discovery mistakes on turn 1 instead of after dozens of failed-test iterations.\n\n### Harness Discovery Check\n\nBefore reporting success, run the **harness-equivalent** discovery command from the repo root and confirm the test count went up by at least the number of tests you generated. CI/msbench/coverage harnesses do not know which directory you targeted with `-Path`; they invoke Pester from the repo root with default discovery, so a test that passes via `Invoke-Pester -Path ./tools/Foo.Tests.ps1` is still worthless if `Invoke-Pester` from the repo root does not enumerate it.\n\n```powershell\n# From repo root — mirrors what a generic harness sees\n$result = Invoke-Pester -Configuration @{ Run = @{ PassThru = $true; SkipRun = $true } }\n\"$($result.TotalCount) tests discovered\"\n```\n\nIf the count did not increase, your `*.Tests.ps1` file is outside the harness discovery root. Move it to the convention the repo's existing tests use (or, if there are no existing tests, prefer the repo root's `tests/`, `Tests/`, `tst/`, `test/`, or co-locate next to the source). Do **not** report success until the harness-equivalent command sees your new tests.\n\n## Rule #1: Investigate the Repo First\n\nBefore writing any test or running any command, read:\n\n1. **Existing tests** — find `*.Tests.ps1` files and copy their style (structure, assertions, mock approach, import method)\n2. **Module structure** — look for `.psd1` (manifest), `.psm1` (root module), `Public/`/`Private/` organization\n3. **Build/test scripts** — check for `build.ps1`, `Invoke-Build` (`*.build.ps1`), `psake`, or CI scripts\n4. **Shell target** — check `.psd1` for `PowerShellVersion`/`CompatiblePSEditions`, CI matrix for `pwsh` vs `powershell.exe`\n\nUse the repo's existing test conventions. Only add Pester if the repo has no tests at all.\n\n## Build Commands\n\nPowerShell is interpreted — no build step. If the repo has a build script, use it. Otherwise validate with:\n\n- **Module loads**: `Import-Module ./MyModule.psd1 -Force -ErrorAction Stop`\n- **Script analyzer**: `Invoke-ScriptAnalyzer -Path ./src -Recurse` (if PSScriptAnalyzer is available)\n- **Lint**: `Invoke-ScriptAnalyzer -Path path/to/file.ps1 -Fix`\n\n## Test Commands\n\n| Scope | Command |\n|-------|---------|\n| All tests | `Invoke-Pester` |\n| Specific file | `Invoke-Pester -Path ./Tests/Get-Widget.Tests.ps1` |\n| Filter by name | `Invoke-Pester -FullNameFilter '*Get-Widget*'` |\n| Filter by tag | `Invoke-Pester -TagFilter 'Unit'` |\n| Non-interactive (CI) | `Invoke-Pester -CI` |\n| Detailed output | `Invoke-Pester -Output Detailed` |\n\n- Prefer the repo's build/test script over raw `Invoke-Pester`\n- Use `-Output Detailed` during fix cycles, `-Output Minimal` for final validation\n\n## Project Layout and Imports\n\n| Layout | Import in `BeforeAll` |\n|--------|-----------------------|\n| Module (`.psd1`) | `Import-Module \"$PSScriptRoot/../MyModule.psd1\" -Force` |\n| Library script (defines functions) | `. $PSScriptRoot/Get-Widget.ps1` |\n| Co-located test | `. $PSCommandPath.Replace('.Tests.ps1', '.ps1')` |\n| Executable script (has `param()`) | Do **not** dot-source — invoke with `& $PSScriptRoot/script.ps1 -Param value` and assert on output/errors |\n\n- **All imports go in `BeforeAll`** — never at script top level\n- **Use `$PSScriptRoot` or `$PSCommandPath`** — never `$MyInvocation.MyCommand.Path` (returns empty in `BeforeAll`)\n- Use `-Force` on `Import-Module` to pick up changes between runs\n\n## Test File Naming\n\n- Files: `*.Tests.ps1` — match existing convention (co-located vs `Tests/` directory)\n\n## Pester v5 Discovery vs Run (Critical)\n\nPester v5 runs in **two phases**: Discovery (collects test metadata) then Run (executes tests). This is the #1 source of agent errors.\n\n**Rules:**\n- All setup code goes in `BeforeAll` or `BeforeEach` — never at script top level or loose inside `Describe`/`Context`\n- Code directly inside `Describe`/`Context` (but outside `It`/`Before*`/`After*`) runs during **Discovery** — do not put setup, imports, or variable assignments there\n- Data for `-ForEach` / `-TestCases` must be set in `BeforeDiscovery`, not `BeforeAll` (BeforeAll runs after discovery)\n- `-Skip:$condition` evaluates at Discovery time — conditions from `BeforeAll` will be `$null`\n- Use `foreach` loops for dynamic test generation only with `BeforeDiscovery` data\n- Use `TestDrive:` for file-based tests instead of touching repo files — Pester cleans it up automatically\n\n## Parameterized Test Display Names\n\nApply [Report-safe test names and result validation](../../code-testing/unit-test-generation.prompt.md#report-safe-test-names-and-result-validation).\nUse an explicit safe `Name`/`Case` in `-ForEach` or `-TestCases` data and expand\nonly that field in the `It` title. Do not expand arbitrary `\u003cInput>` or\n`\u003cExpected>` values into discovery/report metadata.\n\nFor a function whose contract reverses UTF-16 code units (not Unicode scalars),\nthe reversed supplementary character is intentionally malformed UTF-16. Keep\nthat expected value in the assertion, not the title:\n\nUse `Text` for the data field, not `Input`: `$Input` is PowerShell's automatic\npipeline-input variable and can hide the intended case value inside `It`.\n\n```powershell\nBeforeDiscovery {\n $cases = @(\n @{\n Name = 'supplementary code-unit reversal'\n Text = [string]::Concat([char]0xD83D, [char]0xDE00)\n Expected = [string]::Concat([char]0xDE00, [char]0xD83D)\n }\n @{\n Name = 'isolated high surrogate'\n Text = [string][char]0xD800\n Expected = [string][char]0xD800\n }\n )\n}\n\nDescribe 'Get-Reversed' {\n BeforeAll {\n Import-Module (Join-Path $PSScriptRoot '../tools/StringUtils.psm1') -Force\n }\n\n It 'reverses \u003cName>' -ForEach $cases {\n Get-Reversed -Value $Text | Should -BeExactly $Expected\n }\n}\n```\n\nReuse the repository's configured Pester `TestResult` export path and format\nwhen present and parse the resulting artifact before reporting success. A\npassing `TotalCount`/`PassedCount` does not prove JUnit export succeeded.\n\n## Common Errors\n\n| Error | Fix |\n|-------|-----|\n| Variable is `$null` in `It` block | Move assignment into `BeforeAll` — variables set there are visible to child `It` blocks without `$script:` |\n| `-ForEach` data is empty | Move data setup from `BeforeAll` to `BeforeDiscovery` |\n| `CommandNotFoundException` for Mock target | The function must exist before mocking — import the module in `BeforeAll` first |\n| `$MyInvocation.MyCommand.Path` returns empty | Use `$PSCommandPath` or `$PSScriptRoot` instead |\n| `Should Be` (no dash) fails | Use v5 syntax: `Should -Be` (with dash prefix) |\n| `Assert-MockCalled` not recognized | Use v5 syntax: `Should -Invoke` |\n| Mock has no effect | Check scope — mocks in `It` only apply to that `It`; use `BeforeAll`/`BeforeEach` for broader scope |\n| `Should -Throw` doesn't catch cmdlet errors | Most cmdlet errors are non-terminating — wrap with `{ cmd -ErrorAction Stop }` or set `$ErrorActionPreference = 'Stop'` in `BeforeEach` |\n| Tests pass on Windows but fail on Linux | Use `Join-Path` not string concatenation; match exact file casing; avoid Windows-only cmdlets (Registry, EventLog) |\n\n## Mocking Rules\n\n- Place mocks in `BeforeAll` (shared) or `BeforeEach` (reset per test)\n- Mock where the command is **called from** — use `-ModuleName` to mock inside a module's scope\n- Use `-ParameterFilter` for selective mocking (no `param()` block needed in v5)\n- Verify calls with `Should -Invoke` — default scope inside `It` counts only that test's calls\n- Use `InModuleScope` sparingly and as narrowly as possible — prefer `Mock -ModuleName` for testing via public API\n- Inside mock bodies, use `$PesterBoundParameters` not `$PSBoundParameters`\n- If a test needs more than 3 mocks, flag it as a design smell\n\n## Non-Obvious Assertions\n\nMost `Should` operators are self-explanatory. These are the ones agents get wrong:\n\n- `Should -Throw` requires a **scriptblock**: `{ risky-op } | Should -Throw` — not a direct call\n- `Should -Contain` is for **collections** — use `Should -Be` for scalar equality\n- `Should -HaveParameter` validates cmdlet signatures: `Get-Command X | Should -HaveParameter 'Name' -Mandatory`\n- `Should -Invoke` verifies mock calls: `Should -Invoke Get-Item -Times 1 -Exactly`\n\n## Cross-Platform\n\n- Prefer `pwsh` (PowerShell 7+) unless the repo explicitly targets Windows PowerShell 5.1\n- Use `Join-Path` for paths — never string concatenation with `\\`\n- Linux/macOS file systems are **case-sensitive** — match exact casing in imports and paths\n- Windows ships Pester 3.4.0 — if v5 is needed: `Install-Module Pester -Force -SkipPublisherCheck`\n- Check `$PSVersionTable.PSEdition` to detect Core vs Desktop\n\n## Skip Coverage Tools\n\nDo not configure or run coverage tools (Pester CodeCoverage, JaCoCo export). Coverage is measured separately by the evaluation harness.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":12342,"content_sha256":"f34c954bf4b5816347767370011abd8c9b1dacf1c614f1d159437f02456d9dc9"},{"filename":"extensions/python-examples.md","content":"# Python Pipeline Examples\n\nConcrete input→output examples for the test generation pipeline targeting a Python codebase using pytest. These show what each pipeline phase produces for a small project.\n\n## Source Under Test\n\nA simple `InvoiceService` in a Python package using pytest:\n\n```text\nsrc/\n contoso_billing/\n __init__.py\n invoice_service.py\n invoice.py\n invoice_repository.py\ntests/\n __init__.py\n conftest.py (empty, just marks tests/ as a package root)\npyproject.toml\n```\n\n```python\n# src/contoso_billing/invoice_service.py\nfrom decimal import Decimal, ROUND_HALF_UP\nfrom .invoice import Invoice, InvoiceStatus\nfrom .invoice_repository import InvoiceRepository\n\n\nclass InvoiceService:\n def __init__(self, repository: InvoiceRepository) -> None:\n self._repository = repository\n\n def calculate_total(self, invoice: Invoice) -> Decimal:\n if invoice is None:\n raise ValueError(\"invoice must not be None\")\n if not invoice.line_items:\n raise ValueError(\"Invoice has no line items.\")\n\n subtotal = sum(\n (li.quantity * li.unit_price for li in invoice.line_items),\n start=Decimal(\"0\"),\n )\n tax = subtotal * invoice.tax_rate\n return (subtotal + tax).quantize(Decimal(\"0.01\"), rounding=ROUND_HALF_UP)\n\n def get_by_id(self, invoice_id: int) -> Invoice:\n invoice = self._repository.find(invoice_id)\n if invoice is None:\n raise KeyError(f\"Invoice {invoice_id} not found.\")\n return invoice\n\n def mark_as_paid(self, invoice_id: int) -> None:\n invoice = self._repository.find(invoice_id)\n if invoice is None:\n raise KeyError(f\"Invoice {invoice_id} not found.\")\n if invoice.status == InvoiceStatus.PAID:\n raise ValueError(\"Invoice is already paid.\")\n invoice.status = InvoiceStatus.PAID\n invoice.paid_date = _utcnow()\n self._repository.update(invoice)\n\n\ndef _utcnow():\n from datetime import datetime, timezone\n return datetime.now(timezone.utc)\n```\n\n## Sample Research Output\n\nWhat `code-testing-researcher` produces in `\u003cTESTAGENT_DIR>/research.md`:\n\n```markdown\n# Test Generation Research\n\n## Project Overview\n- **Path**: /work/contoso-billing\n- **Language**: Python 3.11\n- **Framework**: pure library (no Flask/Django)\n- **Test Framework**: pytest 8.x (declared in pyproject.toml [project.optional-dependencies].test)\n- **Package Layout**: `src/` layout — production package imports as `contoso_billing`\n\n## Coverage Baseline\n- **Initial Line Coverage**: unknown\n- **Strategy**: broad\n- **Existing Test Count**: 0 tests across 0 files\n\n## Build & Test Commands\n- **Install (editable)**: `python -m pip install -e \".[test]\"`\n- **Build/Type-check**: none configured\n- **Test**: `python -m pytest`\n- **Lint**: none configured\n\n## Project Structure\n- Source: `src/contoso_billing/`\n- Tests: `tests/` (exists, empty besides `conftest.py`)\n\n## Files to Test\n\n### High Priority\n| File | Classes/Functions | Testability | Notes |\n|------|-------------------|-------------|-------|\n| src/contoso_billing/invoice_service.py | InvoiceService: calculate_total, get_by_id, mark_as_paid | High | Core business logic, repository dependency needs mocking |\n\n### Low Priority / Skip\n| File | Reason |\n|------|--------|\n| src/contoso_billing/invoice.py | Dataclass, no logic |\n| src/contoso_billing/invoice_repository.py | Interface/protocol, no implementation |\n\n## Existing Tests\n- No existing tests found\n\n## Testing Patterns\n- No existing patterns; recommend pytest function-style tests in `tests/test_invoice_service.py`, `unittest.mock.Mock(spec=InvoiceRepository)` for repository fakes, and `@pytest.mark.parametrize` for table-driven cases.\n\n## Recommendations\n- Start with `calculate_total` (pure logic, easy to parametrize)\n- Then `get_by_id` and `mark_as_paid` (require mocking the repository)\n- Use `unittest.mock.patch(\"contoso_billing.invoice_service._utcnow\")` to control the timestamp in `mark_as_paid`\n```\n\n## Sample Plan Output\n\nWhat `code-testing-planner` produces in `\u003cTESTAGENT_DIR>/plan.md`:\n\n```markdown\n# Test Implementation Plan\n\n## Overview\nGenerate pytest tests for the Contoso Billing InvoiceService, covering all three\npublic methods across happy path, edge case, and error scenarios. Single phase\nsince there is only one source file.\n\n## Commands\n- **Install**: `python -m pip install -e \".[test]\"`\n- **Test**: `python -m pytest tests/test_invoice_service.py -q`\n- **Test (file-scoped during dev)**: `python -m pytest tests/test_invoice_service.py::test_calculate_total_valid_line_items_returns_expected_total -q`\n\n## Phase Summary\n| Phase | Focus | Files | Est. Tests |\n|-------|-------|-------|------------|\n| 1 | InvoiceService | 1 | 9-12 |\n\n---\n\n## Phase 1: InvoiceService\n\n### Overview\nCover all public methods of InvoiceService. `calculate_total` is pure logic tested\nwith `@pytest.mark.parametrize`. The async-looking methods are synchronous but\nrequire a mocked InvoiceRepository.\n\n### Files to Test\n\n#### 1. invoice_service.py\n- **Source**: `src/contoso_billing/invoice_service.py`\n- **Test File**: `tests/test_invoice_service.py`\n\n**Methods to Test**:\n1. `calculate_total` — Pure calculation logic\n - Happy path: single line item returns quantity × price + tax\n - Happy path: multiple line items summed correctly\n - Edge case: zero tax rate returns subtotal only\n - Error case: None invoice raises ValueError\n - Error case: empty line items raises ValueError\n\n2. `get_by_id` — Repository lookup\n - Happy path: existing ID returns invoice\n - Error case: missing ID raises KeyError\n\n3. `mark_as_paid` — State transition\n - Happy path: pending invoice transitions to PAID with `paid_date` set\n - Error case: already-paid raises ValueError\n - Error case: missing ID raises KeyError\n\n### Success Criteria\n- [ ] Test file created at `tests/test_invoice_service.py`\n- [ ] `python -m pytest` reports all tests passed\n- [ ] No real network/IO; repository is mocked with `Mock(spec=InvoiceRepository)`\n```\n\n## Sample Generated Test File\n\nWhat `code-testing-implementer` produces:\n\n```python\n# tests/test_invoice_service.py\nfrom datetime import datetime, timezone\nfrom decimal import Decimal\nfrom unittest.mock import Mock, patch\n\nimport pytest\n\nfrom contoso_billing.invoice import Invoice, InvoiceStatus, LineItem\nfrom contoso_billing.invoice_repository import InvoiceRepository\nfrom contoso_billing.invoice_service import InvoiceService\n\n\[email protected]\ndef repository() -> Mock:\n return Mock(spec=InvoiceRepository)\n\n\[email protected]\ndef sut(repository: Mock) -> InvoiceService:\n return InvoiceService(repository)\n\n\n# --- calculate_total ---\n\[email protected](\n \"quantity, unit_price, tax_rate, expected\",\n [\n (1, \"100.00\", \"0.10\", \"110.00\"),\n (3, \"25.00\", \"0.00\", \"75.00\"),\n (2, \"9.99\", \"0.07\", \"21.38\"),\n ],\n ids=[\"single-item-10pct-tax\", \"multi-quantity-zero-tax\", \"rounds-half-up\"],\n)\ndef test_calculate_total_valid_line_items_returns_expected_total(\n sut: InvoiceService, quantity: int, unit_price: str, tax_rate: str, expected: str\n) -> None:\n invoice = Invoice(\n tax_rate=Decimal(tax_rate),\n line_items=[LineItem(quantity=quantity, unit_price=Decimal(unit_price))],\n )\n\n total = sut.calculate_total(invoice)\n\n assert total == Decimal(expected)\n\n\ndef test_calculate_total_none_invoice_raises_value_error(sut: InvoiceService) -> None:\n with pytest.raises(ValueError, match=\"invoice must not be None\"):\n sut.calculate_total(None)\n\n\ndef test_calculate_total_empty_line_items_raises_value_error(sut: InvoiceService) -> None:\n invoice = Invoice(tax_rate=Decimal(\"0\"), line_items=[])\n\n with pytest.raises(ValueError, match=\"no line items\"):\n sut.calculate_total(invoice)\n\n\n# --- get_by_id ---\n\ndef test_get_by_id_existing_id_returns_invoice(\n sut: InvoiceService, repository: Mock\n) -> None:\n expected = Invoice(id=42, tax_rate=Decimal(\"0\"), line_items=[])\n repository.find.return_value = expected\n\n result = sut.get_by_id(42)\n\n assert result is expected\n repository.find.assert_called_once_with(42)\n\n\ndef test_get_by_id_missing_id_raises_key_error(\n sut: InvoiceService, repository: Mock\n) -> None:\n repository.find.return_value = None\n\n with pytest.raises(KeyError, match=\"999\"):\n sut.get_by_id(999)\n\n\n# --- mark_as_paid ---\n\ndef test_mark_as_paid_pending_invoice_sets_status_and_date(\n sut: InvoiceService, repository: Mock\n) -> None:\n invoice = Invoice(id=1, status=InvoiceStatus.PENDING, tax_rate=Decimal(\"0\"), line_items=[])\n repository.find.return_value = invoice\n fixed_now = datetime(2025, 1, 1, 12, 0, tzinfo=timezone.utc)\n\n with patch(\"contoso_billing.invoice_service._utcnow\", return_value=fixed_now):\n sut.mark_as_paid(1)\n\n assert invoice.status == InvoiceStatus.PAID\n assert invoice.paid_date == fixed_now\n repository.update.assert_called_once_with(invoice)\n\n\ndef test_mark_as_paid_already_paid_raises_value_error(\n sut: InvoiceService, repository: Mock\n) -> None:\n invoice = Invoice(id=1, status=InvoiceStatus.PAID, tax_rate=Decimal(\"0\"), line_items=[])\n repository.find.return_value = invoice\n\n with pytest.raises(ValueError, match=\"already paid\"):\n sut.mark_as_paid(1)\n\n repository.update.assert_not_called()\n\n\ndef test_mark_as_paid_missing_id_raises_key_error(\n sut: InvoiceService, repository: Mock\n) -> None:\n repository.find.return_value = None\n\n with pytest.raises(KeyError, match=\"999\"):\n sut.mark_as_paid(999)\n```\n\n## Sample Fix Cycle\n\nWhen the implementer encounters an import or attribute error, the fixer agent diagnoses and resolves it.\n\n**Test output:**\n\n```text\nModuleNotFoundError: No module named 'contoso_billing'\n```\n\n**Fixer diagnosis:** The package is not installed in editable mode, so the `src/` layout's package is not on `sys.path`.\n\n**Fix applied:**\n\n```bash\npython -m pip install -e \".[test]\"\n```\n\n**Rerun:** `python -m pytest tests/test_invoice_service.py -q` → SUCCESS\n\n---\n\n**Another common cycle — patch target wrong:**\n\n**Test output:**\n\n```text\nAttributeError: \u003cmodule 'datetime'> does not have the attribute '_utcnow'\n```\n\n**Fixer diagnosis:** The test patched `datetime._utcnow` but the production code defines its own `_utcnow` helper inside `contoso_billing.invoice_service`. Patches must target the lookup site, not the definition site.\n\n**Fix applied:**\n\n```python\n# Before (wrong)\nwith patch(\"datetime._utcnow\", return_value=fixed_now):\n\n# After (fixed) — patch where the name is looked up\nwith patch(\"contoso_billing.invoice_service._utcnow\", return_value=fixed_now):\n```\n\n**Rerun:** SUCCESS\n\n---\n\n**Another common cycle — Mock without spec:**\n\n**Test output:**\n\n```text\nAttributeError: Mock object has no attribute 'find_by_id'\n```\n\n(but the actual repository method is `find`, not `find_by_id`)\n\n**Fixer diagnosis:** `Mock()` happily creates any attribute on access, so a typo in the test went undetected until the production code called `repository.find(...)`. Using `Mock(spec=InvoiceRepository)` would have failed at setup time.\n\n**Fix applied:**\n\n```python\n# Before\nrepository = Mock()\nrepository.find_by_id.return_value = expected # typo, silently accepted\n\n# After\nrepository = Mock(spec=InvoiceRepository)\nrepository.find.return_value = expected # typos now raise AttributeError\n```\n\n**Rerun:** SUCCESS\n\n## Sample Final Report\n\nWhat `test-engineer` produces at Step 9:\n\n```markdown\n## Test Generation Report\n\n**Project**: contoso-billing\n**Strategy**: Direct (single source file in scope)\n\n### Results\n| Metric | Value |\n|----------------|-------|\n| Tests created | 9 |\n| Tests passing | 9 |\n| Tests failing | 0 |\n| Files created | 1 |\n\n### Files Created\n- `tests/test_invoice_service.py` (9 tests, 3 parametrized)\n\n### Coverage\n- InvoiceService.calculate_total — 3 happy path, 2 error cases\n- InvoiceService.get_by_id — 1 happy path, 1 error case\n- InvoiceService.mark_as_paid — 1 happy path, 2 error cases\n\n### Build / Install Validation\n- Editable install: ✅ `python -m pip install -e \".[test]\"`\n- Test run: ✅ `python -m pytest` — 9 passed in 0.12s\n\n### Next Steps\n- Add tests for repository implementations if any exist\n- Consider snapshot/property-based testing (`hypothesis`) for `calculate_total` rounding behaviour\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":12425,"content_sha256":"d142728fb8fcd5e0977e317cf52b14bbaa2efb403d8a3dd341470b1fa2f5f238"},{"filename":"extensions/python.md","content":"# Python Extension\n\nLanguage-specific guidance for Python test generation.\n\n## Rule #1: Investigate the Repo First\n\nBefore writing any test or running any command, discover what the repo already does:\n\n1. **Find ALL existing test files** — search broadly: `test_*.py`, `*_test.py`, `*.uts`, `test/*.sh`, or any other test format. Do not assume pytest.\n2. **Identify the test framework and runner** — check, in order:\n - `pyproject.toml` `[tool.pytest.ini_options]` `testpaths`\n - `pytest.ini`\n - `setup.cfg` `[tool:pytest]`\n - `tox.ini` `[testenv]` `commands`\n - `Makefile`, `noxfile.py`, `conftest.py` locations\n - Project-specific runners such as Django `runtests.py`, `manage.py test`, or `DJANGO_SETTINGS_MODULE`\n3. **Find the active test layout** — note the directory, working directory, and fixture scope used by existing tests. Some repos use non-standard layouts such as Ansible-style `test/units/`.\n4. **Read existing tests thoroughly** — copy their exact style: file format, imports, fixtures, assertion patterns, helper utilities, setup/teardown conventions\n5. **Package layout** — determine import paths from existing code, not guesswork\n\n**Use whatever framework and conventions the repo already uses.** If the repo uses a custom test framework (custom file formats, custom runners, domain-specific test utilities), adopt it fully — do not layer pytest on top. Only introduce pytest if the repo has no tests at all.\n\n**Preserve requested regression coverage.** Run the full new-test suite before finishing. Fix test defects without weakening required assertions. If a requested regression exposes a production defect or cannot run because of a tooling/environment blocker, retain the test and report the failing evidence and blocker; do not delete, skip, or xfail it merely to report green. Remove only speculative cases outside the requested scope.\n\n**Start simple to bank coverage.** Write high-certainty tests for pure functions, validation branches, serializers, small helpers, and deterministic error paths before attempting async views, templates, sessions, network paths, or integration-heavy code.\n\n## Environment Detection\n\nDetect the runner from lockfiles/config and prefix all commands accordingly:\n\n| Indicator | Prefix |\n|-----------|--------|\n| `poetry.lock` / `[tool.poetry]` in `pyproject.toml` | `poetry run` |\n| `pdm.lock` / `[tool.pdm]` in `pyproject.toml` | `pdm run` |\n| `uv.lock` / `[tool.uv]` in `pyproject.toml` | `uv run` |\n| `Pipfile.lock` | `pipenv run` |\n| `hatch.toml` / `[tool.hatch]` in `pyproject.toml` | `hatch run` |\n| None of the above | `python -m` |\n\n`\u003cprefix>` applies to **module execution** only. With the default `python -m` prefix, `\u003cprefix> pytest` expands to `python -m pytest`, but a script entry point or inline probe must not be double-prefixed — `python -m python manage.py …` / `python -m python -c …` is invalid. Run script entry points (`manage.py`, `runtests.py`) and `python -c` probes with `python` directly, wrapping with the env tool when one is detected (e.g. `poetry run python manage.py test …`, `uv run python -c \"…\"`) instead of `python -m`.\n\nIf `Makefile`, `tox.ini`, or `nox` config exists, prefer those scripts over raw commands.\n\n## Build Commands\n\nPython has no separate build step. Validate with the type checker if one is configured:\n\n| Scope | Command |\n|-------|---------|\n| Syntax check | `\u003cprefix> py_compile path/to/file.py` |\n| Type check | `\u003cprefix> mypy path/to/file.py` or `\u003cprefix> pyright path/to/file.py` |\n\n## Test Commands\n\nRun new tests the same way the repo runs existing tests: same working directory, same command wrapper, same `conftest.py` scope, and same settings environment variables.\n\nBefore choosing a command, inspect runner configuration with copy-pasteable probes:\n\n```powershell\nGet-ChildItem -Recurse -File -Include pyproject.toml,pytest.ini,setup.cfg,tox.ini,Makefile,noxfile.py,conftest.py,runtests.py,manage.py\nSelect-String -Path pyproject.toml,pytest.ini,setup.cfg,tox.ini -Pattern 'testpaths|\\[tool.pytest|\\[tool:pytest|commands|DJANGO_SETTINGS_MODULE' -ErrorAction SilentlyContinue\n```\n\nIf the repo uses a **custom test framework** (custom file formats, custom runner), use its native commands — do not wrap them in pytest. Examples:\n\n| Framework | Command |\n|-----------|---------|\n| UTscapy (`.uts` files) | `\u003cprefix> scapy.tools.UTscapy -f test/test_file.uts` |\n| Django runner script | `python runtests.py app_label.tests.test_module` |\n| Django project | `python manage.py test app_label.tests.test_module` |\n| Custom runner script | `make test`, `./run_tests.sh`, `tox` |\n| Repo-defined script | Whatever `scripts.test` in Makefile/tox/nox specifies |\n\nFor **pytest** projects (the most common case), use the detected `\u003cprefix>`:\n\n| Scope | Command |\n|-------|---------|\n| All tests | `\u003cprefix> pytest` |\n| Specific file | `\u003cprefix> pytest tests/test_module.py` |\n| Specific test | `\u003cprefix> pytest tests/test_module.py::TestClass::test_method` |\n| Keyword filter | `\u003cprefix> pytest -k \"keyword\"` |\n| Stop on first failure | `\u003cprefix> pytest -x --tb=short` |\n\n- Prefer `python -m pytest` over bare `pytest` to ensure the correct interpreter\n- If the project uses `unittest` only (no pytest in deps), use `python -m unittest discover`\n- If tests must run from a subdirectory, `Set-Location` there first and keep that working directory for verification\n\n## Frameworks Beyond Plain Pytest\n\nMirror the existing tests' import style and invocation exactly.\n\n- **Django**: Prefer the repo's runner (`runtests.py`, `manage.py test`, or tox/make target). If the repo uses `pytest-django`, ensure `DJANGO_SETTINGS_MODULE` is set exactly as existing tests/config require.\n - `python manage.py test app_label.tests.test_module`\n - `$env:DJANGO_SETTINGS_MODULE='project.settings'; python -m pytest tests/app/test_module.py`\n- **unittest-style suites**: Use `python -m unittest path.to.test_module` or the repo's discover command; do not force pytest unless existing tests already do.\n- **Subdir runners**: Some repos expect commands from `tests/`, `test/units/`, or another subdir so relative imports and fixtures work.\n\n## Lint Command\n\nUse the repo's existing lint script first (`make lint`, `tox -e lint`). Otherwise detect tools from config:\n\n- `ruff.toml` or `[tool.ruff]` → `\u003cprefix> ruff check --fix && \u003cprefix> ruff format`\n- `[tool.black]` → `\u003cprefix> black`\n- `.flake8` → `\u003cprefix> flake8`\n\n## Project Layout and Imports\n\n| Layout | Import Style |\n|--------|-------------|\n| `src/package/module.py` | `from package.module import X` |\n| `package/module.py` at root | `from package.module import X` |\n| `module.py` at root | `from module import X` |\n\n- **Match existing test imports exactly** — do not invent `src.` prefixes unless existing tests use them\n- Place new tests where the existing suite lives so the same `conftest.py`, fixtures, helpers, and settings apply\n- Check `pyproject.toml` `[tool.setuptools.package-dir]` for layout hints\n- Default test placement: `tests/` mirroring source structure (`src/billing/service.py` → `tests/billing/test_service.py`)\n\n## Heavy or Native Dependencies\n\nBefore writing tests for a target module, verify it imports cleanly in the same environment and working directory as tests. Run the probe under the **same env wrapper as the test command** (`poetry run`, `pdm run`, `uv run`, `pipenv run`, `hatch run`) so the check reflects the real test interpreter/venv — a bare `python` may resolve to a different environment and report a misleading `ok`:\n\n```powershell\n# Wrap with the detected env tool, e.g. `poetry run python -c \"...\"`\npython -c \"import package.module; print('ok')\"\npython -c \"from package import module; print('ok')\"\n```\n\nIf a heavy/native dependency such as NumPy, pandas, PyTorch, TensorFlow, cryptography, or a compiled extension cannot be imported or built in the environment:\n\n- For an explicitly requested target, preserve its regression cases and report the import/build failure as an environment blocker; do not replace the requested target with unrelated tests\n- Do not spend the budget fighting native build/import failures or installing unrelated packages\n- For an open-ended request only, choose a pure-Python submodule that imports cleanly and disclose the omitted scope (see *Finalization: Validate and Report Blockers*)\n\n## Test File Naming\n\nMatch the repo's existing conventions. Common patterns:\n\n- **pytest**: Files `test_*.py` or `*_test.py`, functions `test_` prefix, classes `Test` prefix\n- **Custom frameworks**: Use whatever format existing tests use (e.g. `.uts` for UTscapy, custom extensions)\n\nIf writing new tests in a repo with no tests, default to pytest conventions.\n\n## Common Errors\n\n| Error | Fix |\n|-------|-----|\n| `ModuleNotFoundError: No module named 'src'` | Import from the package name used by the repo, not from `src` |\n| `ModuleNotFoundError: No module named 'X'` | Check existing imports for the correct package name; if editable install needed: `\u003cprefix> pip install -e .` |\n| `ImportError: attempted relative import` | Convert to absolute imports matching existing test patterns |\n| `fixture 'X' not found` | Check `conftest.py` for existing fixtures; reuse them instead of creating new ones |\n| `TypeError: missing required argument` | Read the full `__init__`/function signature; pass all required parameters |\n| `async def functions are not natively supported` | Use `@pytest.mark.asyncio` only if `pytest-asyncio` is already in deps; check for `asyncio_mode = \"auto\"` in config |\n| `DJANGO_SETTINGS_MODULE is undefined` | Use the repo's Django runner or set the same settings module used by existing tests |\n| `ImportError` from `torch`, `numpy`, or compiled extension | Report the environment blocker for a requested target; choose a pure-Python alternative only for open-ended scope |\n| `SyntaxError` | Fix syntax at the indicated line |\n\n## Mocking Rules\n\n- Use `unittest.mock` (stdlib) — no extra dependency needed\n- **Patch where the name is looked up**, not where it is defined: `@patch(\"mypackage.module.datetime\")` not `@patch(\"datetime.datetime\")`\n- Use `Mock(spec=RealClass)` to catch attribute errors\n- Use `AsyncMock` for async functions\n- Prefer dependency injection over `@patch`\n- If a test needs more than 3 mocks, flag it as a design smell\n\n## Dependency Installation (Last Resort)\n\nOnly install packages after investigation confirms they are missing. Use the detected prefix:\n\n| Manager | Install command |\n|---------|----------------|\n| Poetry | `poetry add --group dev pytest` |\n| PDM | `pdm add -dG test pytest` |\n| uv | `uv add --dev pytest` |\n| pip | `python -m pip install -e \".[dev]\"` |\n\nNever run bare `pip install` in a Poetry/PDM/uv project — it bypasses the lockfile.\n\n## Finalization: Validate and Report Blockers\n\nBefore finishing, run the complete set of tests you added with the repo's native invocation, under the **same env wrapper as the repo's tests** (`poetry run`, `pdm run`, `uv run`, `pipenv run`, `hatch run`). Running the green-suite check in a different interpreter/venv can pass locally yet still fail under the repo's actual runner.\n\n```powershell\n# Examples; choose the repo-native command/wrapper discovered above\npoetry run python -m pytest tests/path/to/new_tests.py\nuv run python -m unittest path.to.new_test_module\npoetry run python manage.py test app_label.tests.test_module\n```\n\nFix test defects and rerun without weakening required assertions. Retain explicitly requested regression cases when they expose a production defect or an environment/collection blocker, and report the exact command, failure, and remaining work rather than claiming success. Do not skip, xfail, or delete required cases to manufacture a green suite. Remove only speculative out-of-scope cases, and disclose any scope reduction.\n\n## Skip Coverage Tools\n\nDo not configure or run coverage tools (coverage.py, pytest-cov). Coverage is measured separately by the evaluation harness.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":11991,"content_sha256":"613cd80d37dadd5237e39eb860aacb0617ad68a75f28a802b835cae63c64c381"},{"filename":"extensions/ruby-examples.md","content":"# Ruby Pipeline Examples\n\nConcrete input→output examples for the test generation pipeline targeting a Ruby codebase using RSpec. These show what each pipeline phase produces for a small gem-style project.\n\n> Minitest follows the same coverage shape. Replace `RSpec.describe` / `it` / `expect` with `Minitest::Test` methods and assertions, and run through `bundle exec rake test` or the repo's established Minitest command.\n\n## Source Under Test\n\nA simple `InvoiceService` in a Ruby gem:\n\n```text\nGemfile\nlib/\n contoso_billing.rb\n contoso_billing/invoice.rb\n contoso_billing/invoice_repository.rb\n contoso_billing/invoice_service.rb\nspec/spec_helper.rb\n```\n\n```ruby\n# lib/contoso_billing/invoice_service.rb\nrequire 'bigdecimal'\nrequire 'time'\n\nmodule ContosoBilling\n class InvoiceService\n def initialize(repository:, clock: -> { Time.now.utc })\n @repository = repository\n @clock = clock\n end\n\n def calculate_total(invoice)\n raise ArgumentError, 'invoice must not be nil' if invoice.nil?\n raise ArgumentError, 'Invoice has no line items.' if invoice.line_items.empty?\n\n subtotal = invoice.line_items.sum { |item| item.quantity * item.unit_price }\n tax = subtotal * invoice.tax_rate\n (subtotal + tax).round(2)\n end\n\n def get_by_id(id)\n invoice = @repository.find(id)\n raise KeyError, \"Invoice #{id} not found.\" if invoice.nil?\n\n invoice\n end\n\n def mark_as_paid(id)\n invoice = get_by_id(id)\n raise StandardError, 'Invoice is already paid.' if invoice.status == :paid\n\n invoice.status = :paid\n invoice.paid_at = @clock.call\n @repository.update(invoice)\n end\n end\nend\n```\n\n## Sample Research Output\n\nWhat `code-testing-researcher` produces in `\u003cTESTAGENT_DIR>/research.md`:\n\n```markdown\n# Test Generation Research\n\n## Project Overview\n- **Path**: /work/contoso-billing\n- **Language**: Ruby 3.3 (from `.ruby-version`)\n- **Project Type**: Plain gem\n- **Test Framework**: RSpec 3.x (detected in Gemfile.lock and `spec/spec_helper.rb`)\n- **Run Prefix**: `bundle exec` required because Gemfile.lock is present\n\n## Coverage Baseline\n- **Initial Line Coverage**: unknown\n- **Strategy**: broad\n- **Existing Test Count**: 0 examples across 0 files\n\n## Build & Test Commands\n- **Syntax**: `ruby -c lib/contoso_billing/invoice_service.rb`\n- **Discovery**: `bundle exec rspec --dry-run`\n- **Single file**: `bundle exec rspec spec/contoso_billing/invoice_service_spec.rb`\n- **All specs**: `bundle exec rspec`\n\n## Files to Test\n\n### High Priority\n| File | Classes/Methods | Testability | Notes |\n|------|-----------------|-------------|-------|\n| lib/contoso_billing/invoice_service.rb | InvoiceService: calculate_total, get_by_id, mark_as_paid | High | Repository dependency and clock are injected |\n\n## Testing Patterns\n- Existing specs use `RSpec.describe`, `subject`, `let`, and `instance_double`.\n- Recommend `instance_double('InvoiceRepository')` for the repository and a fixed clock lambda for time-dependent behavior.\n```\n\n## Sample Plan Output\n\n```markdown\n# Test Implementation Plan\n\n## Overview\nGenerate RSpec tests for ContosoBilling::InvoiceService, covering calculation,\nlookup, and paid-state transition behavior.\n\n## Commands\n- **Syntax**: `ruby -c spec/contoso_billing/invoice_service_spec.rb`\n- **Discovery**: `bundle exec rspec --dry-run`\n- **Test**: `bundle exec rspec spec/contoso_billing/invoice_service_spec.rb`\n\n## Phase 1: InvoiceService\n\n### Files to Test\n- **Source**: `lib/contoso_billing/invoice_service.rb`\n- **Test File**: `spec/contoso_billing/invoice_service_spec.rb`\n\n**Methods to Test**:\n1. `calculate_total` — tax, zero tax, rounding, nil, and empty line items\n2. `get_by_id` — existing invoice and missing invoice\n3. `mark_as_paid` — success with fixed clock, already-paid, missing\n```\n\n## Sample Generated Test File\n\n```ruby\n# spec/contoso_billing/invoice_service_spec.rb\nrequire 'spec_helper'\nrequire 'bigdecimal'\nrequire 'ostruct'\nrequire 'time'\nrequire 'contoso_billing/invoice_service'\n\nRSpec.describe ContosoBilling::InvoiceService do\n subject(:service) { described_class.new(repository: repository, clock: clock) }\n\n let(:repository) { instance_double('InvoiceRepository') }\n let(:fixed_time) { Time.utc(2025, 1, 1, 12, 0, 0) }\n let(:clock) { -> { fixed_time } }\n\n def build_invoice(id: 1, status: :pending, tax_rate: BigDecimal('0.10'), line_items: [OpenStruct.new(quantity: 1, unit_price: BigDecimal('100.00'))])\n OpenStruct.new(id: id, status: status, tax_rate: tax_rate, line_items: line_items, paid_at: nil)\n end\n\n describe '#calculate_total' do\n it 'returns the total for a single item with tax' do\n expect(service.calculate_total(build_invoice)).to eq(BigDecimal('110.00'))\n end\n\n it 'returns the subtotal when tax is zero' do\n invoice = build_invoice(\n tax_rate: BigDecimal('0'),\n line_items: [OpenStruct.new(quantity: 3, unit_price: BigDecimal('25.00'))]\n )\n\n expect(service.calculate_total(invoice)).to eq(BigDecimal('75.00'))\n end\n\n it 'rounds to two decimals' do\n invoice = build_invoice(\n tax_rate: BigDecimal('0.07'),\n line_items: [OpenStruct.new(quantity: 2, unit_price: BigDecimal('9.99'))]\n )\n\n expect(service.calculate_total(invoice)).to eq(BigDecimal('21.38'))\n end\n\n it 'raises for a nil invoice' do\n expect { service.calculate_total(nil) }.to raise_error(ArgumentError, /must not be nil/)\n end\n\n it 'raises when there are no line items' do\n expect { service.calculate_total(build_invoice(line_items: [])) }.to raise_error(ArgumentError, /no line items/)\n end\n end\n\n describe '#get_by_id' do\n it 'returns an existing invoice' do\n invoice = build_invoice(id: 42)\n allow(repository).to receive(:find).with(42).and_return(invoice)\n\n expect(service.get_by_id(42)).to be(invoice)\n end\n\n it 'raises KeyError for a missing invoice' do\n allow(repository).to receive(:find).with(999).and_return(nil)\n\n expect { service.get_by_id(999) }.to raise_error(KeyError, /999/)\n end\n end\n\n describe '#mark_as_paid' do\n it 'marks a pending invoice as paid and persists it' do\n invoice = build_invoice(id: 1)\n allow(repository).to receive(:find).with(1).and_return(invoice)\n allow(repository).to receive(:update)\n\n service.mark_as_paid(1)\n\n expect(invoice.status).to eq(:paid)\n expect(invoice.paid_at).to eq(fixed_time)\n expect(repository).to have_received(:update).with(invoice)\n end\n\n it 'raises and does not update an already-paid invoice' do\n invoice = build_invoice(id: 1, status: :paid)\n allow(repository).to receive(:find).with(1).and_return(invoice)\n allow(repository).to receive(:update)\n\n expect { service.mark_as_paid(1) }.to raise_error(StandardError, /already paid/)\n expect(repository).not_to have_received(:update)\n end\n\n it 'raises KeyError and does not update a missing invoice' do\n allow(repository).to receive(:find).with(999).and_return(nil)\n allow(repository).to receive(:update)\n\n expect { service.mark_as_paid(999) }.to raise_error(KeyError, 'Invoice 999 not found.')\n expect(repository).not_to have_received(:update)\n end\n end\nend\n```\n\n## Sample Fix Cycle\n\nWhen the implementer encounters a load or mock issue, the fixer agent diagnoses and resolves it.\n\n**Test output:**\n\n```text\nLoadError: cannot load such file -- contoso_billing/invoice_service\n```\n\n**Fixer diagnosis:** The generated spec omitted `require 'spec_helper'`, so the gem's load-path setup did not run.\n\n**Fix applied:** Add `require 'spec_helper'` as the first require and keep source requires consistent with existing specs.\n\n**Rerun:** `bundle exec rspec spec/contoso_billing/invoice_service_spec.rb` → SUCCESS\n\n---\n\n**Another common cycle — verifying double mismatch:**\n\n**Test output:**\n\n```text\nThe InvoiceRepository class does not implement the instance method: find_by_id\n```\n\n**Fixer diagnosis:** `instance_double` caught a typo in the test setup. The production code calls `repository.find`, not `find_by_id`.\n\n**Fix applied:** Stub `find` with the expected id instead of `find_by_id`.\n\n**Rerun:** SUCCESS\n\n## Sample Final Report\n\n```markdown\n## Test Generation Report\n\n**Project**: contoso-billing (Ruby)\n**Strategy**: Direct (single source file in scope)\n\n### Results\n| Metric | Value |\n|----------------|-------|\n| Tests created | 10 |\n| Tests passing | 10 |\n| Tests failing | 0 |\n| Files created | 1 |\n\n### Files Created\n- `spec/contoso_billing/invoice_service_spec.rb` (10 RSpec examples)\n\n### Coverage\n- InvoiceService#calculate_total — 3 happy path, 2 error cases\n- InvoiceService#get_by_id — found and missing branches\n- InvoiceService#mark_as_paid — success, already-paid and missing-invoice branches; error cases do not update the repository\n\n### Build / Test Validation\n- Syntax: ✅ `ruby -c spec/contoso_billing/invoice_service_spec.rb`\n- Discovery: ✅ `bundle exec rspec --dry-run` found the new examples\n- Test run: ✅ `bundle exec rspec spec/contoso_billing/invoice_service_spec.rb`\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":9130,"content_sha256":"0320df5702892f3057d33f56e384a9b992f104438a32c7141ef61b5a5db544cf"},{"filename":"extensions/ruby.md","content":"# Ruby Extension\n\nLanguage-specific guidance for Ruby test generation.\n\n## Rule #0: Confirm the Test Target\n\nIf the prompt does not name a specific file (e.g. \"test the repository\", \"cover one core module\", \"comprehensive suite\"), do **not** assume the largest or top-level upstream code is the intended target. In real workflows the user usually wants to test code they have just added, and large upstream repos contain hundreds of modules already covered by existing specs.\n\nRun these **read-only** discovery commands first — they are the deliberate exception to Rule #1's \"before writing any test or running any command\" rule, and their output is the ground truth Rule #1's reading is meant to interpret. Do **not** write or execute any tests until Rule #0 and Rule #1 are both complete.\n\n| Goal | Command |\n|------|---------|\n| List uncommitted edits + untracked files | `git status -s` |\n| Untracked files only (typical for newly-added modules) | `git ls-files --others --exclude-standard` |\n| Recently added files under `lib/` or `app/` | `git log --diff-filter=A --name-only -5 -- 'lib/**' 'app/**'` |\n| Files referenced by `spec_helper.rb` / `rails_helper.rb` | `grep -nE \"^\\s*require(_relative)?\\s\" spec/spec_helper.rb spec/rails_helper.rb 2>/dev/null` |\n| Modules with no matching spec | compare `lib/**/*.rb` against `spec/**/*_spec.rb` paths |\n\nPrefer targets that match **all** of:\n\n1. Untracked or recently added (`git status` / `git log --diff-filter=A`).\n2. Small and pure (a few hundred lines, no I/O, no global state).\n3. Located under a conventional source root (`lib/`, `app/models/`, `app/services/`).\n4. Have **no** existing matching `*_spec.rb` / `*_test.rb`.\n\nIf `spec/spec_helper.rb` already `require`s one specific file (e.g. `require \"string_utils\"`), that file is almost certainly the target — start there.\n\n### Test Placement Contract\n\nRSpec only discovers specs under `spec/` by default, and verification harnesses (CI, msbench, coverage tools) typically scope discovery to `spec/` alone. Place every spec there, mirroring the source layout:\n\n| Source | Spec |\n|--------|------|\n| `lib/string_utils.rb` | `spec/string_utils_spec.rb` |\n| `lib/foo/bar.rb` | `spec/foo/bar_spec.rb` |\n| `app/models/user.rb` (Rails) | `spec/models/user_spec.rb` |\n\nA spec placed anywhere outside `spec/` (e.g. next to the source under `lib/`) will be invisible to `bundle exec rspec` and to the harness. The same applies to Minitest: place tests under `test/` and use `*_test.rb` naming.\n\n**Gem-monorepo trap (fastlane, ruby/ruby, large gems with sub-gems):** if the repo contains multiple `*/spec/` directories (each sub-gem with its own specs), `bundle exec rspec` from the repo root only loads `./spec/` by default — sub-gem specs are invisible to the harness. Either:\n\n- place the new spec inside the **root** `./spec/` (with a `require_relative` to the sub-gem's `lib/`), or\n- run the sub-gem's `bundle exec rspec` from the sub-gem dir AND verify in the Harness Discovery Check below that the root command also enumerates it (often it won't — you'll need to extend `.rspec` with `--default-path` or the root `Rakefile`'s test task).\n\nFor interpreter-build repos (ruby/ruby itself) the test runner requires `make test-all` after `make miniruby` — `ruby test/foo_test.rb` alone is not what the harness runs.\n\n### First-Test Sanity Loop\n\nAfter writing the **first** spec — before writing any others:\n\n1. Run `bundle exec rspec --dry-run` and confirm the example count is `> 0`. If it is `0`, RSpec is not seeing your file; fix the location, filename, or `$LOAD_PATH` before continuing.\n2. Run the spec (`bundle exec rspec spec/\u003cyour_spec>.rb`); fix `LoadError`, missing `require`, or constant errors before adding more tests.\n3. Only then expand to cover the remaining methods.\n\nThis catches placement and load-path mistakes on turn 1 instead of after dozens of failed-test iterations.\n\n### Harness Discovery Check\n\nBefore reporting success, run the **harness-equivalent** discovery command from the repo root and confirm the example count went up by at least the number of tests you generated. CI/msbench/coverage harnesses do not know which file or sub-gem dir you targeted; they run the framework's default discovery from the repo root, so a spec that passes via `bundle exec rspec fastlane_core/spec/foo_spec.rb` is still worthless if `bundle exec rspec --dry-run` from the repo root doesn't enumerate it.\n\nChoose the repository's configured runner **before** execution. Run the selected\ncommands from the repo root in the **same Bash process** as `set -euo pipefail`,\nand require exit code zero before using any count as evidence. Do not suppress\nstderr, pipe the runner into a counting command, or switch runners after failure.\n\nFor RSpec, use its dry-run example summary:\n\n```bash\nset -euo pipefail\nbundle exec rspec --dry-run --format progress\n```\n\nFor Minitest without a repository-supported discovery mode, run the configured\nreal test task and use its actual `\u003cN> runs, \u003cN> assertions` summary, not the number\nof Rake tasks. For a repository whose harness uses `rake test`:\n\n```bash\nset -euo pipefail\nbundle exec rake test\n```\n\nIf the harness instead uses `bin/rails test` or another task, substitute that\ncommand before execution; it is not a fallback for a failed selected task.\nFor custom runners (Homebrew, ruby/ruby, etc.), use the repository's documented\ndiscovery mode or real harness-equivalent test task, with the same exit-status\nrequirements.\n\nRequire a successful runner exit and a nonzero test/example count, then compare\nthe baseline and updated summaries for the same harness scope. If the count did\nnot increase, your spec is invisible to the harness. Move it into `./spec/`,\nextend `.rspec`/`Rakefile` so the harness picks up the sub-gem dir, or switch to a\n`require_relative` strategy from a root-level spec. A failed runner or missing\nsummary is a blocker, not evidence of empty discovery. Do **not** report success\nuntil the harness-equivalent command sees your new tests.\n\n## Rule #1: Investigate the Repo First\n\nBefore writing any test or running any command, read:\n\n1. **Existing tests** — find `spec/**/*_spec.rb` (RSpec) or `test/**/*_test.rb` (Minitest) and copy their style (matchers, helpers, factories, contexts)\n2. **`Gemfile` / `Gemfile.lock`** — Ruby version, test framework, supporting gems (`rspec`, `minitest`, `factory_bot`, `webmock`, `vcr`, `rails`)\n3. **`.ruby-version`** / `.tool-versions` — pinned Ruby version\n4. **Test helpers** — `spec/spec_helper.rb`, `spec/rails_helper.rb`, `test/test_helper.rb` — these dictate the load path, requires, and global config\n5. **Rake tasks** — `Rakefile` may define a `default` task that runs the full test suite\n\nUse the framework the repo already uses. Do not introduce RSpec into a Minitest project (or vice versa).\n\n## Toolchain Detection\n\n| Indicator | Manager | Run prefix |\n|-----------|---------|------------|\n| `Gemfile.lock` | Bundler | `bundle exec \u003ccmd>` |\n| `.ruby-version` + `rbenv` | rbenv | combine with `bundle exec` |\n| `mise.toml` / `asdf` `.tool-versions` | mise/asdf | the wrapper handles version selection; still use `bundle exec` |\n| Plain Ruby, no Bundler | system Ruby | `ruby \u003cfile>` (rare in real projects) |\n\nAlways run inside `bundle exec` if a `Gemfile.lock` is present — otherwise you may pick up a system gem version that disagrees with the lockfile.\n\n## Build Commands\n\nRuby is interpreted — there is no compile step. The closest validations:\n\n| Scope | Command |\n|-------|---------|\n| Syntax check | `ruby -c path/to/file.rb` |\n| Lint (RuboCop) | `bundle exec rubocop path/to/file.rb` |\n| Type check (Sorbet) | `bundle exec srb tc` (only if `sorbet/` dir exists) |\n| Type check (RBS/Steep) | `bundle exec steep check` |\n\nFor Rails: load all classes once with `bundle exec rails zeitwerk:check` to catch missing constants before running tests.\n\n## Test Commands\n\n### RSpec\n\n| Scope | Command |\n|-------|---------|\n| All specs | `bundle exec rspec` |\n| Single file | `bundle exec rspec spec/models/widget_spec.rb` |\n| Single line | `bundle exec rspec spec/models/widget_spec.rb:42` |\n| By name | `bundle exec rspec -e \"creates a widget\"` |\n| Tagged | `bundle exec rspec --tag focus` |\n| Fail fast | `bundle exec rspec --fail-fast` |\n| Documentation format | `bundle exec rspec --format documentation` |\n\n### Minitest\n\n| Scope | Command |\n|-------|---------|\n| All tests | `bundle exec rake test` (Rails) or `bundle exec ruby -Ilib -Itest -e 'Dir.glob(\"./test/**/*_test.rb\").each { |f| require f }'` |\n| Single file | `bundle exec ruby -Itest test/models/widget_test.rb` |\n| Single test | `bundle exec ruby -Itest test/models/widget_test.rb -n test_creates_widget` |\n| By name pattern | `... -n /pattern/` |\n\n### Rails (any framework)\n\n| Scope | Command |\n|-------|---------|\n| Default suite | `bin/rails test` (Minitest) or `bundle exec rspec` |\n| Single Rails test file | `bin/rails test test/models/widget_test.rb:42` |\n| System tests | `bin/rails test:system` |\n\nAlways prefer the wrapper script (`bin/rails`, `bin/rspec`) when present — they enforce the project's loader/setup.\n\n## Lint Command\n\n- `bundle exec rubocop` — autocorrect with `bundle exec rubocop -A` (only if existing tests already conform; do not autocorrect unrelated files)\n- `bundle exec standardrb --fix` if `standard` is in the Gemfile\n- Some Rails projects add `rubocop-rails`, `rubocop-rspec`, `rubocop-performance` — they enforce extra rules\n\n## Project Layout and Loading\n\n| Layout | Test placement |\n|--------|----------------|\n| Plain gem (RSpec) | `spec/` mirrors `lib/` (e.g. `lib/foo/bar.rb` → `spec/foo/bar_spec.rb`) |\n| Plain gem (Minitest) | `test/` mirrors `lib/` (e.g. `test/foo/bar_test.rb`) |\n| Rails (RSpec) | `spec/models`, `spec/controllers`, `spec/requests`, `spec/system`, etc. |\n| Rails (Minitest) | `test/models`, `test/controllers`, `test/integration`, `test/system` |\n\n**Loading source code:**\n\n- RSpec: `spec/spec_helper.rb` typically does `require 'my_gem'` or sets `$LOAD_PATH`. Match its pattern in new specs by `require 'spec_helper'` (or `require 'rails_helper'` in Rails)\n- Minitest: each `_test.rb` typically `require 'test_helper'`\n- Rails uses Zeitwerk autoloading — do **not** add `require_relative '../../app/models/widget'`; just `require 'rails_helper'` and reference the constant\n\n## Test File Naming\n\n| Framework | File suffix | Class/example |\n|-----------|-------------|---------------|\n| RSpec | `_spec.rb` | `RSpec.describe Widget do ... end`, `it \"...\" do ... end` |\n| Minitest (classic) | `_test.rb` | `class WidgetTest \u003c Minitest::Test`, methods `def test_...` |\n| Minitest (spec) | `_test.rb` | `describe Widget do ... it \"...\" do ... end end` |\n| Rails Minitest | `_test.rb` | `class WidgetTest \u003c ActiveSupport::TestCase` |\n\n## RSpec Template\n\n```ruby\nrequire 'spec_helper'\nrequire 'calculator'\n\nRSpec.describe Calculator do\n subject(:calculator) { described_class.new }\n\n describe '#add' do\n it 'returns the sum of two positive numbers' do\n expect(calculator.add(2, 3)).to eq(5)\n end\n\n context 'with negative numbers' do\n it 'returns the correct sum' do\n expect(calculator.add(-1, 1)).to eq(0)\n end\n end\n\n it 'raises when given non-numeric input' do\n expect { calculator.add('a', 1) }.to raise_error(TypeError)\n end\n end\nend\n```\n\n## Common Errors\n\n| Error | Fix |\n|-------|-----|\n| `LoadError: cannot load such file -- foo` | Missing `require` or load path; check `spec_helper.rb` for the established pattern instead of patching `$LOAD_PATH` ad hoc |\n| `NameError: uninitialized constant X` | Constant isn't loaded — in Rails, ensure you require `rails_helper`; in plain Ruby, add the appropriate `require` |\n| `ArgumentError: wrong number of arguments (given X, expected Y)` | Read the method signature; pass keyword vs positional args correctly |\n| `NoMethodError: undefined method 'foo' for nil:NilClass` | Test setup left a value `nil`; check `let`/`before` ordering and factory data |\n| `Failure/Error: ... received :foo with unexpected arguments` (RSpec) | Tighten the matcher: `with(hash_including(...))` or relax to `with(any_args)` deliberately |\n| `expected #\u003c...> to receive :foo (1 time) but received it 0 times` | Either the code path didn't call the stub, or you stubbed the wrong receiver |\n| `DEPRECATION WARNING` (Rails) | Address the deprecation rather than silencing it; tests that warn today break tomorrow |\n| `ActiveRecord::PendingMigrationError` | Run `bin/rails db:migrate RAILS_ENV=test` before tests |\n| `Mysql2::Error / PG::ConnectionBad` in CI | Tests need a database — check `config/database.yml` and CI service containers |\n| `Capybara::ElementNotFound` (system tests) | Use `find` with explicit waits; do not add `sleep` |\n\n## Mocking Rules (RSpec)\n\n- Use `instance_double(Klass)` and `class_double(Klass)` — they verify that the method actually exists, unlike `double`\n- `allow(obj).to receive(:method).and_return(value)` for stubs; `expect(obj).to receive(:method)` for interaction expectations\n- Prefer `instance_double` over plain `double`; prefer dependency injection over `allow_any_instance_of`\n- Use `let` for memoized helpers; use `let!` only when the side effect must run before each example\n- Avoid global state mutation in tests — wrap in `around` blocks or use `ClimateControl` for env vars\n- For HTTP, use `webmock` (`stub_request(:get, ...)`) or `vcr` cassettes if the project already uses them\n- If a test needs more than 3 mocks, flag it as a design smell\n\n## Mocking Rules (Minitest)\n\n- Use `Minitest::Mock` for simple cases: `mock = Minitest::Mock.new; mock.expect(:method, return_value, [arg])`\n- For richer mocking, projects commonly add `mocha`: `obj.expects(:method).returns(value)` (in `test_helper.rb`: `require 'mocha/minitest'`)\n- Always verify mocks at end of test (`mock.verify` for `Minitest::Mock`); Mocha verifies automatically\n\n## Rails Specifics\n\n- Use the **smallest** spec type that covers the behavior: model spec for pure logic, request spec for HTTP, system spec only when JS/UI matters\n- `rails-controller-testing` gem must be present for `assigns(:foo)` and `assert_template`\n- `ActiveJob::TestHelper` and `ActiveSupport::Testing::TimeHelpers` (`travel_to`) come with Rails — use them instead of `Timecop` if Rails ≥ 5\n- Use fixtures only if the project already uses them; `factory_bot` is more common in modern Rails apps\n- Database transactions wrap each test by default — for system tests with browser drivers, use `DatabaseCleaner` strategies the project already configures\n\n## Dependency Installation (Last Resort)\n\nOnly add gems after investigation confirms they are missing. Edit `Gemfile`:\n\n```ruby\ngroup :test do\n gem 'rspec'\n gem 'webmock'\nend\n```\n\nThen run:\n\n```\nbundle install\n```\n\nNever `gem install` outside Bundler — it bypasses the lockfile and changes the global Ruby environment.\n\n## Skip Coverage Tools\n\nDo not configure or run coverage tools (SimpleCov). Coverage is measured separately by the evaluation harness.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":15067,"content_sha256":"a74f82cef7a4fb9cbc147c79ace44f61fd7fc0f4325ad9ebd1f4db421015b82d"},{"filename":"extensions/rust-examples.md","content":"# Rust Pipeline Examples\n\nConcrete input→output examples for the test generation pipeline targeting a Rust crate using the built-in test harness. These show what each pipeline phase produces for a small library crate.\n\n## Source Under Test\n\nA simple `InvoiceService` in a Rust crate:\n\n```text\nCargo.toml\nsrc/\n lib.rs\n invoice.rs\n invoice_repository.rs\n invoice_service.rs\n```\n\n```rust\n// src/invoice_service.rs\nuse crate::invoice::{Invoice, InvoiceStatus};\nuse crate::invoice_repository::InvoiceRepository;\nuse std::time::SystemTime;\n\n#[derive(Debug, PartialEq, Eq)]\npub enum InvoiceError {\n EmptyLineItems,\n NotFound(i32),\n AlreadyPaid,\n Repository(String),\n}\n\npub struct InvoiceService\u003cR, C>\nwhere\n R: InvoiceRepository,\n C: Fn() -> SystemTime,\n{\n repository: R,\n clock: C,\n}\n\nimpl\u003cR, C> InvoiceService\u003cR, C>\nwhere\n R: InvoiceRepository,\n C: Fn() -> SystemTime,\n{\n pub fn new(repository: R, clock: C) -> Self {\n Self { repository, clock }\n }\n\n pub fn calculate_total(&self, invoice: &Invoice) -> Result\u003cf64, InvoiceError> {\n if invoice.line_items.is_empty() {\n return Err(InvoiceError::EmptyLineItems);\n }\n\n let subtotal: f64 = invoice.line_items.iter().map(|item| item.quantity as f64 * item.unit_price).sum();\n Ok(((subtotal + subtotal * invoice.tax_rate) * 100.0).round() / 100.0)\n }\n\n pub fn get_by_id(&self, id: i32) -> Result\u003cInvoice, InvoiceError> {\n self.repository.find(id).map_err(InvoiceError::Repository)?.ok_or(InvoiceError::NotFound(id))\n }\n\n pub fn mark_as_paid(&mut self, id: i32) -> Result\u003c(), InvoiceError> {\n let mut invoice = self.get_by_id(id)?;\n if invoice.status == InvoiceStatus::Paid {\n return Err(InvoiceError::AlreadyPaid);\n }\n\n invoice.status = InvoiceStatus::Paid;\n invoice.paid_at = Some((self.clock)());\n self.repository.update(invoice).map_err(InvoiceError::Repository)\n }\n}\n```\n\n## Sample Research Output\n\nWhat `code-testing-researcher` produces in `\u003cTESTAGENT_DIR>/research.md`:\n\n```markdown\n# Test Generation Research\n\n## Project Overview\n- **Path**: /work/contoso-billing\n- **Language**: Rust 1.78 (edition 2021, from Cargo.toml)\n- **Crate Type**: library\n- **Test Framework**: built-in Rust test harness (`#[test]`), no `mockall`/`rstest` dev-dependencies detected\n\n## Coverage Baseline\n- **Initial Line Coverage**: unknown\n- **Strategy**: broad\n- **Existing Test Count**: 0 tests across 0 files\n\n## Build & Test Commands\n- **Check**: `cargo check --all-targets`\n- **Compile tests**: `cargo test --no-run`\n- **Test**: `cargo test`\n- **Single module**: `cargo test invoice_service::tests`\n\n## Files to Test\n\n### High Priority\n| File | Types/Methods | Testability | Notes |\n|------|---------------|-------------|-------|\n| src/invoice_service.rs | InvoiceService: calculate_total, get_by_id, mark_as_paid | High | Generic repository trait is easy to fake; clock closure is injectable |\n\n## Testing Patterns\n- No existing patterns; recommend unit tests in `#[cfg(test)] mod tests` at the bottom of `invoice_service.rs` and a hand-written fake repository.\n```\n\n## Sample Plan Output\n\n```markdown\n# Test Implementation Plan\n\n## Overview\nGenerate built-in Rust unit tests for InvoiceService covering calculation,\nlookup, and paid-state transition behavior.\n\n## Commands\n- **Check**: `cargo check --all-targets`\n- **Compile tests**: `cargo test --no-run`\n- **Test**: `cargo test invoice_service::tests`\n\n## Phase 1: InvoiceService\n\n### Files to Test\n- **Source**: `src/invoice_service.rs`\n- **Test Location**: `#[cfg(test)] mod tests` appended to `src/invoice_service.rs`\n\n**Methods to Test**:\n1. `calculate_total` — tax, zero tax, rounding, empty-line-items error\n2. `get_by_id` — existing invoice, missing invoice, repository error\n3. `mark_as_paid` — success, already-paid, missing invoice\n```\n\n## Sample Generated Test File\n\n```rust\n// Appended to src/invoice_service.rs\n#[cfg(test)]\nmod tests {\n use super::*;\n use crate::invoice::{Invoice, InvoiceStatus, LineItem};\n use std::collections::HashMap;\n use std::time::{Duration, UNIX_EPOCH};\n\n #[derive(Default)]\n struct FakeRepository {\n invoices: HashMap\u003ci32, Invoice>,\n updated: Option\u003cInvoice>,\n find_error: Option\u003cString>,\n }\n\n impl InvoiceRepository for FakeRepository {\n fn find(&self, id: i32) -> Result\u003cOption\u003cInvoice>, String> {\n if let Some(error) = &self.find_error {\n return Err(error.clone());\n }\n\n Ok(self.invoices.get(&id).cloned())\n }\n\n fn update(&mut self, invoice: Invoice) -> Result\u003c(), String> {\n self.updated = Some(invoice.clone());\n self.invoices.insert(invoice.id, invoice);\n Ok(())\n }\n }\n\n fn make_invoice(id: i32) -> Invoice {\n Invoice {\n id,\n status: InvoiceStatus::Pending,\n tax_rate: 0.10,\n line_items: vec![LineItem { quantity: 1, unit_price: 100.0 }],\n paid_at: None,\n }\n }\n\n fn service_with(repository: FakeRepository) -> InvoiceService\u003cFakeRepository, fn() -> std::time::SystemTime> {\n InvoiceService::new(repository, || UNIX_EPOCH + Duration::from_secs(123))\n }\n\n #[test]\n fn calculate_total_valid_line_items_returns_expected_total() {\n let cases = [\n (\"single item with tax\", vec![LineItem { quantity: 1, unit_price: 100.0 }], 0.10, 110.0),\n (\"multi quantity zero tax\", vec![LineItem { quantity: 3, unit_price: 25.0 }], 0.0, 75.0),\n (\"rounds to two decimals\", vec![LineItem { quantity: 2, unit_price: 9.99 }], 0.07, 21.38),\n ];\n let service = service_with(FakeRepository::default());\n\n for (name, line_items, tax_rate, expected) in cases {\n let mut invoice = make_invoice(1);\n invoice.line_items = line_items;\n invoice.tax_rate = tax_rate;\n\n let total = service.calculate_total(&invoice).unwrap_or_else(|err| panic!(\"{name}: unexpected error: {err:?}\"));\n\n assert!((total - expected).abs() \u003c 0.001, \"{name}: got {total}, expected {expected}\");\n }\n }\n\n #[test]\n fn calculate_total_empty_line_items_returns_error() {\n let service = service_with(FakeRepository::default());\n let mut invoice = make_invoice(1);\n invoice.line_items.clear();\n\n assert_eq!(Err(InvoiceError::EmptyLineItems), service.calculate_total(&invoice));\n }\n\n #[test]\n fn get_by_id_existing_invoice_returns_invoice() {\n let mut repository = FakeRepository::default();\n repository.invoices.insert(42, make_invoice(42));\n let service = service_with(repository);\n\n let invoice = service.get_by_id(42).expect(\"invoice should exist\");\n\n assert_eq!(42, invoice.id);\n }\n\n #[test]\n fn get_by_id_missing_invoice_returns_not_found() {\n let service = service_with(FakeRepository::default());\n\n assert_eq!(Err(InvoiceError::NotFound(999)), service.get_by_id(999));\n }\n\n #[test]\n fn get_by_id_repository_error_is_preserved() {\n let repository = FakeRepository { find_error: Some(\"boom\".to_owned()), ..FakeRepository::default() };\n let service = service_with(repository);\n\n assert_eq!(Err(InvoiceError::Repository(\"boom\".to_owned())), service.get_by_id(1));\n }\n\n #[test]\n fn mark_as_paid_pending_invoice_updates_status_date_and_repository() {\n let mut repository = FakeRepository::default();\n repository.invoices.insert(1, make_invoice(1));\n let mut service = service_with(repository);\n\n service.mark_as_paid(1).expect(\"mark_as_paid should succeed\");\n\n let updated = service.repository.updated.as_ref().expect(\"repository should be updated\");\n assert_eq!(InvoiceStatus::Paid, updated.status);\n assert_eq!(Some(UNIX_EPOCH + Duration::from_secs(123)), updated.paid_at);\n }\n\n #[test]\n fn mark_as_paid_already_paid_returns_error_without_update() {\n let mut invoice = make_invoice(1);\n invoice.status = InvoiceStatus::Paid;\n let mut repository = FakeRepository::default();\n repository.invoices.insert(1, invoice);\n let mut service = service_with(repository);\n\n assert_eq!(Err(InvoiceError::AlreadyPaid), service.mark_as_paid(1));\n assert!(service.repository.updated.is_none());\n }\n\n #[test]\n fn mark_as_paid_missing_invoice_returns_not_found_without_update() {\n let mut service = service_with(FakeRepository::default());\n\n assert_eq!(Err(InvoiceError::NotFound(999)), service.mark_as_paid(999));\n assert!(service.repository.updated.is_none());\n }\n}\n```\n\n## Sample Fix Cycle\n\nWhen the implementer hits a compiler or test-runner issue, the fixer agent diagnoses and resolves it.\n\n**Build output:**\n\n```text\nerror[E0596]: cannot borrow `self.repository` as mutable, as it is behind a `&` reference\n```\n\n**Fixer diagnosis:** `mark_as_paid` calls `repository.update(...)`, which requires mutable repository access. The production method must take `&mut self`, and tests must bind the service as `let mut service`.\n\n**Fix applied:** Change the method receiver to `&mut self` and update tests to use mutable bindings for `mark_as_paid` cases.\n\n**Rebuild + rerun:** `cargo test --no-run && cargo test invoice_service::tests` → SUCCESS\n\n---\n\n**Another common cycle — integration test imports:**\n\n**Build output:**\n\n```text\nerror[E0432]: unresolved import `crate::invoice_service`\n```\n\n**Fixer diagnosis:** The test was created under `tests/invoice_service.rs`, which is an integration test crate. Integration tests import the library by crate name, not `crate::`.\n\n**Fix applied:** Move the tests into `#[cfg(test)] mod tests` in `src/invoice_service.rs` and use `use super::*;`.\n\n**Rerun:** SUCCESS\n\n## Sample Final Report\n\n```markdown\n## Test Generation Report\n\n**Project**: contoso-billing (Rust)\n**Strategy**: Direct (single module in scope)\n\n### Results\n| Metric | Value |\n|----------------|-------|\n| Tests created | 8 |\n| Tests passing | 8 |\n| Tests failing | 0 |\n| Files created | 0 (tests appended to source module) |\n\n### Files Modified\n- `src/invoice_service.rs` (8 unit tests in `#[cfg(test)] mod tests`; 3 calculation rows run within one test)\n\n### Coverage\n- InvoiceService::calculate_total — 3 happy path, 1 error case\n- InvoiceService::get_by_id — found, missing, repository error\n- InvoiceService::mark_as_paid — success, already-paid and missing-invoice branches; error cases do not update the repository\n\n### Build / Test Validation\n- Check: ✅ `cargo check --all-targets`\n- Compile tests: ✅ `cargo test --no-run`\n- Test run: ✅ `cargo test invoice_service::tests`\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":10812,"content_sha256":"b4933f329331113cdf0903f9599dd0f281caa20f9a3bd6b762b43f16cf35cd76"},{"filename":"extensions/rust.md","content":"# Rust Extension\n\nLanguage-specific guidance for Rust test generation.\n\n## Rule #1: Investigate the Repo First\n\nBefore writing any test or running any command, read:\n\n1. **Existing tests** — look at `#[cfg(test)] mod tests` blocks inside `src/`, integration tests in `tests/`, doc tests in source comments, and any `examples/` that double as smoke tests\n2. **`Cargo.toml`** — workspace layout (`[workspace]`), edition, `dev-dependencies`, feature flags, `[[bench]]` / `[[test]]` declarations\n3. **`Cargo.lock`** — if checked in, you must not break it without intent\n4. **Toolchain** — `rust-toolchain.toml` pins the channel (stable / nightly / specific version)\n5. **`build.rs`** — custom build scripts may set `cfg` flags or generate code that tests rely on\n\nMatch the repo's existing conventions — assertion macros, mock approach, feature-gating — exactly. Do not introduce `tokio::test` if the repo uses `async-std`, etc.\n\n## Toolchain Detection\n\n| Indicator | Meaning |\n|-----------|---------|\n| `rust-toolchain.toml` with `channel = \"...\"` | Use rustup to install/select that channel — `rustup show active-toolchain` |\n| `rust-version = \"1.x\"` in `Cargo.toml` | Minimum supported Rust version (MSRV); do not use newer language features |\n| `[workspace]` in root `Cargo.toml` | Multi-crate workspace; commands accept `-p \u003ccrate>` to target one member |\n| `nightly` channel | Tests may use `#![feature(...)]` flags; do not remove them |\n\n## Build Commands\n\n| Scope | Command |\n|-------|---------|\n| Type-check fast | `cargo check` |\n| Type-check whole workspace | `cargo check --workspace --all-targets` |\n| Build (debug) | `cargo build` |\n| Build with all features | `cargo build --all-features` |\n| Build a single crate | `cargo build -p crate-name` |\n| Build tests without running | `cargo test --no-run` |\n\n`cargo check` is far faster than `cargo build` and catches almost the same errors. Prefer it during the fix loop; use `cargo build --tests` (or `cargo test --no-run`) before declaring tests compilable.\n\n## Test Commands\n\n| Scope | Command |\n|-------|---------|\n| All tests | `cargo test` |\n| Workspace | `cargo test --workspace` |\n| Single crate | `cargo test -p crate-name` |\n| Filter by name | `cargo test substring_of_test_name` |\n| Exact name | `cargo test -- --exact path::to::test_fn` |\n| Single integration file | `cargo test --test file_stem` (no `.rs`) |\n| Doc tests only | `cargo test --doc` |\n| Show stdout | `cargo test -- --nocapture` |\n| Single-threaded | `cargo test -- --test-threads=1` |\n| Ignored tests | `cargo test -- --ignored` |\n| With features | `cargo test --features \"feat1 feat2\"` |\n| All features | `cargo test --all-features` |\n\n- Arguments before `--` are for cargo; arguments after `--` go to the test binary\n- `cargo test foo` runs every test with `foo` in its full path (`module::tests::foo_does_a_thing`) — to avoid surprise matches use `--exact`\n- `cargo nextest run` is significantly faster if the repo already uses it (`Cargo.toml` `[profile.nextest...]` or `.config/nextest.toml`) — match the repo's choice\n\n## Lint Command\n\nUse the repo's lint script first. Otherwise:\n\n- `cargo fmt --all -- --check` (CI), `cargo fmt` (apply)\n- `cargo clippy --all-targets --all-features -- -D warnings`\n- If `clippy.toml` / `rustfmt.toml` exists, the project has opinions — never override them in your tests\n\n## Project Layout\n\n```\nmy_crate/\n├── Cargo.toml\n├── src/\n│ ├── lib.rs # library crate root\n│ ├── main.rs # binary crate root (mutually OK with lib.rs)\n│ └── module.rs # private/public module\n├── tests/ # integration tests — each .rs is a separate crate\n│ └── widget.rs\n├── benches/ # cargo bench targets\n└── examples/ # cargo run --example name\n```\n\n| Test type | Where | Sees |\n|-----------|-------|------|\n| Unit test | `#[cfg(test)] mod tests` inside the source file | Private items in the surrounding module |\n| Integration test | `tests/\u003cname>.rs` | Only the public API of the crate |\n| Doc test | `///` doctests in source comments | Only the public API; runs via `cargo test --doc` |\n\n- **Unit tests** at the bottom of `module.rs`:\n\n ```rust\n #[cfg(test)]\n mod tests {\n use super::*;\n\n #[test]\n fn name_scenario_expected() {\n // ...\n }\n }\n ```\n\n- **Integration tests** import the crate by name: `use my_crate::PublicType;`\n- Helpers shared between integration tests must live in `tests/common/mod.rs` (the `mod.rs` form prevents cargo from treating them as a top-level test crate)\n\n## Test Function Patterns\n\n| Kind | Attribute |\n|------|-----------|\n| Sync test | `#[test]` |\n| Should panic | `#[test] #[should_panic(expected = \"message substring\")]` |\n| Ignored (long/manual) | `#[test] #[ignore = \"reason\"]` |\n| Async test (Tokio) | `#[tokio::test]` (or `#[tokio::test(flavor = \"multi_thread\")]`) |\n| Async test (async-std) | `#[async_std::test]` |\n| Returning `Result` | `fn name() -> Result\u003c(), Box\u003cdyn Error>>` — use `?` instead of `.unwrap()` |\n\nPick the async harness the repo already uses. Do not mix `tokio` and `async-std` in tests.\n\n## Common Errors\n\n| Error | Fix |\n|-------|-----|\n| `cannot find type X in this scope` | Add `use crate::module::X;` or `use super::*;` inside the test module |\n| `function or associated item not found in 'X'` | Verify the method exists on the exact type; check trait imports (e.g. `use std::io::Read`) |\n| `the trait bound 'X: Y' is not satisfied` | Either implement the trait, add a `where` bound, or change the test to use a type that already implements it |\n| `borrow of moved value` | Add `.clone()`, borrow with `&`, or restructure ownership — do not use `mem::transmute` to dodge it |\n| `cannot borrow as mutable` | Make the binding `let mut x` or restructure to avoid simultaneous mutable + immutable borrows |\n| `lifetime may not live long enough` | Add explicit lifetime annotations or use owned types (`String` instead of `&str`) in the test |\n| `mismatched types` between `i32` and `usize` | Use `as` casts deliberately or change the literal type with a suffix (`5usize`, `5u32`) |\n| `unresolved import 'crate::...'` in `tests/foo.rs` | Integration tests must import via the **crate name** (as listed in `Cargo.toml`), not `crate::` |\n| `error: no test target found` for `cargo test --test foo` | The file must live directly in `tests/`, not `tests/subdir/foo.rs` (subdirs are treated as helpers) |\n| `attempt to subtract with overflow` (debug) | Underflow on unsigned types; use `checked_sub`/`saturating_sub` or compare before subtracting |\n| Doctest fails to compile | Use a leading \"# \" on hidden setup lines; mark code blocks `ignore`/`no_run`/`should_panic` if needed |\n| `the following imports are unused` (warning treated as error) | Remove unused `use` statements; do not silence with `#[allow(unused_imports)]` |\n\n## Mocking Rules\n\nRust has no single dominant mocking framework. Match the repo:\n\n- **Trait + struct fakes** (most idiomatic): define a trait, pass `Arc\u003cdyn Trait>` or generic `T: Trait`, implement a fake struct in tests\n- **`mockall`** crate: `#[automock]` on a trait generates `MockTrait` for use in tests\n- **`mockito`** / **`wiremock`**: HTTP server mocks for client tests\n- **`tempfile`**: scoped temp directories that auto-clean (`tempfile::tempdir()`)\n\nAvoid `unsafe` patches to \"mock\" free functions. Refactor to inject a trait instead. If a test needs more than 3 mocks, flag it as a design smell.\n\n## Features and `cfg`\n\n- Tests behind a feature flag run only when that feature is enabled — use `#[cfg(feature = \"foo\")]` on the `mod tests` or individual `#[test]` functions\n- `--all-features` exercises everything but may pull conflicting features in some workspaces; check `cargo test --all-features` is part of CI before relying on it\n- Use `#[cfg(test)]` to gate test-only helpers in production source files — not `#[cfg(feature = \"test\")]`\n\n## Concurrency, IO, and `unsafe`\n\n- Tests run in parallel by default. If your tests share global state (env vars, current dir, statics), serialize them with the `serial_test` crate (if present) or move state into the test\n- Never write to `/tmp` or the repo dir directly — use `tempfile::tempdir()` so cleanup is automatic\n- Tests in `unsafe` code should also run under Miri (`cargo +nightly miri test`) if the repo's CI does\n\n## Dependency Installation (Last Resort)\n\nOnly add dependencies after investigation confirms they are missing:\n\n```toml\n[dev-dependencies]\nmockall = \"0.12\"\ntokio = { version = \"1\", features = [\"macros\", \"rt-multi-thread\"] }\n```\n\nOr via cargo:\n\n```\ncargo add --dev mockall\ncargo add --dev tokio --features macros,rt-multi-thread\n```\n\nMatch the major version of any tokio/serde/etc. already pinned by the workspace.\n\n## Skip Coverage Tools\n\nDo not configure or run coverage tools (`cargo tarpaulin`, `cargo llvm-cov`, `grcov`). Coverage is measured separately by the evaluation harness.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":8983,"content_sha256":"6228a43826eb47112bee333ba34ebbb692770907da235af20296d8938e96ceea"},{"filename":"extensions/swift.md","content":"# Swift Extension\n\nLanguage-specific guidance for Swift test generation.\n\n## Rule #1: Investigate the Repo First\n\nBefore writing any test or running any command, read:\n\n1. **Existing tests** — find files in `Tests/` (SPM) or `*Tests/` groups (Xcode) and copy their style. Distinguish **XCTest** (`import XCTest`, classes inheriting `XCTestCase`) from **Swift Testing** (`import Testing`, free functions tagged `@Test`)\n2. **Project file** — `Package.swift` (SPM), `*.xcodeproj`, `*.xcworkspace`, or `Project.swift` (Tuist)\n3. **Swift toolchain** — `.swift-version`, `swift-tools-version` line in `Package.swift`, `IPHONEOS_DEPLOYMENT_TARGET` and `SWIFT_VERSION` build settings in Xcode\n4. **CI scripts** — `.github/workflows/*.yml`, `Fastfile`, `Makefile` — these reveal the canonical build/test invocation\n\nUse the testing framework the repo already uses. Both XCTest and Swift Testing can coexist in one target — match what the file you're adding tests next to uses.\n\n## Project Type Detection\n\n| Indicator | Project type | Build tool |\n|-----------|--------------|------------|\n| `Package.swift` only | Swift Package Manager | `swift build` / `swift test` |\n| `*.xcodeproj` or `*.xcworkspace` | Xcode project (often app/iOS) | `xcodebuild` |\n| Both | SPM library + Xcode app shell | Use SPM for library targets, Xcode for app targets |\n| `Project.swift` (Tuist) | Tuist-generated Xcode project | Run `tuist generate` first, then xcodebuild |\n| `project.yml` (XcodeGen) | XcodeGen-generated project | Run `xcodegen generate` first |\n\nIf both an `.xcodeproj` and `.xcworkspace` exist (e.g. CocoaPods), **always pass `-workspace` not `-project`** to xcodebuild.\n\n## Build Commands\n\n### Swift Package Manager\n\n| Scope | Command |\n|-------|---------|\n| Build all | `swift build` |\n| Build a target | `swift build --target MyLibrary` |\n| Build for release | `swift build -c release` |\n\n### Xcode (`xcodebuild`)\n\n```\nxcodebuild build \\\n -workspace MyApp.xcworkspace \\\n -scheme MyAppScheme \\\n -destination 'platform=iOS Simulator,name=iPhone 15' \\\n -configuration Debug\n```\n\n- Always specify `-destination` for iOS/tvOS/watchOS — the default may not exist on the build machine\n- Use `-quiet` to suppress xcodebuild's chatty output, and pipe to `xcbeautify`/`xcpretty` if installed\n- For deterministic CI builds add `-derivedDataPath ./DerivedData`\n\n## Test Commands\n\n### Swift Package Manager\n\n| Scope | Command |\n|-------|---------|\n| All tests | `swift test` |\n| Filter by test name (XCTest) | `swift test --filter MyClassTests/testFooBar` |\n| Filter by test name (Swift Testing) | `swift test --filter MyTestSuite.fooBar` |\n| Parallel | `swift test --parallel` |\n| Single platform | `swift test --triple x86_64-apple-macosx` (rare; usually skip) |\n\n### Xcode\n\n```\nxcodebuild test \\\n -workspace MyApp.xcworkspace \\\n -scheme MyAppScheme \\\n -destination 'platform=iOS Simulator,name=iPhone 15' \\\n -only-testing:MyAppTests/MyClassTests/testFooBar\n```\n\n- `-only-testing:` and `-skip-testing:` accept `Bundle/Class/Method` paths and may be repeated\n- `xcodebuild test-without-building` skips compilation if you've already built\n- For Swift Testing in Xcode 16+, use the same `-only-testing:` syntax — the runner handles both frameworks\n\n## Lint Command\n\nUse the repo's lint tooling first:\n\n- `swiftlint lint --quiet` (autocorrect: `swiftlint --fix`) when `.swiftlint.yml` is present\n- `swiftformat .` when `.swiftformat` is present\n- Some projects gate format on a build phase — running `xcodebuild` may already invoke it\n\n## Project Layout\n\n### SPM\n\n```\nPackage.swift\nSources/\n└── MyLibrary/\n ├── Foo.swift\n └── Bar.swift\nTests/\n└── MyLibraryTests/\n └── FooTests.swift\n```\n\n- Test target name conventionally is `\u003cTargetName>Tests` and lives in `Tests/\u003cTargetName>Tests/`\n- Test target must list its production target as a dependency in `Package.swift`:\n\n ```swift\n .testTarget(\n name: \"MyLibraryTests\",\n dependencies: [\"MyLibrary\"]),\n ```\n\n### Xcode\n\n- Tests live in a separate target (e.g. `MyAppTests`) added to the scheme's \"Test\" action\n- The test target's \"Host Application\" determines whether tests run on the simulator with the app loaded (unit tests) or as a UI test runner\n\n## Imports\n\n- XCTest: `import XCTest` plus `@testable import MyLibrary` to access `internal` symbols\n- Swift Testing: `import Testing` plus `@testable import MyLibrary`\n- `@testable` works only when the production target is built with `-enable-testing` (the SPM test target and Xcode \"Debug\" config do this by default)\n- Never mark production code `public` solely to make it visible to tests — use `@testable import` instead\n\n## Test File Templates\n\n### Swift Testing (Xcode 16 / Swift 6)\n\n```swift\nimport Testing\n@testable import MyLibrary\n\n@Suite(\"Calculator\")\nstruct CalculatorTests {\n @Test(\"add returns the sum of two integers\")\n func addReturnsSum() {\n let calc = Calculator()\n #expect(calc.add(2, 3) == 5)\n }\n\n @Test(\"add throws on overflow\", arguments: [\n (Int.max, 1),\n (Int.min, -1),\n ])\n func addThrowsOnOverflow(a: Int, b: Int) {\n #expect(throws: ArithmeticError.self) {\n try Calculator().add(a, b)\n }\n }\n}\n```\n\n### XCTest\n\n```swift\nimport XCTest\n@testable import MyLibrary\n\nfinal class CalculatorTests: XCTestCase {\n func testAddReturnsSum() {\n let calc = Calculator()\n XCTAssertEqual(calc.add(2, 3), 5)\n }\n\n func testAddThrowsOnOverflow() {\n XCTAssertThrowsError(try Calculator().add(.max, 1)) { error in\n XCTAssertEqual(error as? ArithmeticError, .overflow)\n }\n }\n}\n```\n\n- XCTest requires test methods to start with `test` and take no arguments\n- Mark XCTest classes `final` to silence warnings and prevent unintended subclassing\n- Use `XCTUnwrap` instead of force-unwrapping (`!`) inside tests so the failure is reported rather than crashing the runner\n\n## Async, Throws, and Concurrency\n\n- Test methods may be `async` and/or `throws` in both frameworks\n- For asynchronous expectations under XCTest, use `XCTestExpectation` + `wait(for:timeout:)` only when you cannot refactor to `async`\n- For Swift Testing, use `await confirmation { ... }` to assert that a callback fires\n- Cancel tasks deliberately with `Task.cancel()` instead of relying on test timeout\n\n## Common Errors\n\n| Error | Fix |\n|-------|-----|\n| `cannot find 'X' in scope` from a test | Add `@testable import MyLibrary` (and ensure the test target depends on it) |\n| `module 'MyLibrary' was not compiled for testing` | Build the production target with `-enable-testing`; SPM test targets do this automatically — Xcode Debug configs need \"Enable Testability\" = YES |\n| `failed to launch test runner` (Xcode) | Simulator destination may be invalid; list with `xcrun simctl list devices` and pick an existing one |\n| `No such module 'XCTest'` outside a test target | XCTest is only available in test targets — do not import it from production code |\n| `Static method 'expect(_:_:sourceLocation:)' is unavailable` / `No such module 'Testing'` | Swift Testing requires Swift 6 / Xcode 16+. On older toolchains, fall back to XCTest |\n| `Symbol not found: _OBJC_CLASS_$_...` | Linker missing a framework; add it to the test target's \"Link Binary With Libraries\" |\n| `signal SIGABRT` in tests | Often a force-unwrap on `nil`; replace `!` with `XCTUnwrap` to localize the failure |\n| `MainActor-isolated property cannot be referenced from a non-isolated context` | Mark the test method `@MainActor` or move setup into a `MainActor` task |\n| `Sandbox: ... deny file-write-create` | Use `FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)` instead of writing to fixed paths |\n| Test discovery shows zero tests on Linux | XCTest on Linux needs `XCTMain([testCase(MyTests.allTests), ...])` in `Tests/LinuxMain.swift` (legacy SwiftPM only); for Swift 5.4+ this is auto-generated |\n\n## Mocking Rules\n\nSwift has no Mockito-equivalent — favor protocol-oriented design:\n\n- Define a **protocol** for the dependency, pass it via initializer, and implement a fake/stub struct in the test target\n- For URL/HTTP, use `URLProtocol` subclasses to intercept `URLSession` requests, or use `MockingbirdSwift` / `Cuckoo` if the repo already adopts them\n- For dates/clocks, inject a `Clock` (`ContinuousClock`, `SuspendingClock`, or a custom `Clock`-conforming type) — do not call `Date()` directly in business logic\n- Avoid `swizzling` and runtime hacks — they break under Swift's optimizer\n\nIf a test needs more than 3 mocks, flag it as a design smell.\n\n## Cross-Platform Considerations\n\n- Swift on Linux supports XCTest but **not** all of Foundation — guard with `#if canImport(Darwin)` or `#if os(macOS)` only when necessary\n- Use `String(decoding:as:)` rather than `String(contentsOf:encoding:)` for cross-platform reads\n- Be careful with `Bundle.main` in tests — on macOS unit tests it points to `xctest`, not your bundle; use `Bundle(for: type(of: self))` (XCTest) or a resource-bundle helper\n\n## Dependency Installation (Last Resort)\n\nOnly add dependencies after investigation confirms they are missing.\n\n`Package.swift`:\n\n```swift\n.package(url: \"https://github.com/apple/swift-collections.git\", from: \"1.1.0\"),\n```\n\nThen add to the test target's `dependencies:`. For CocoaPods/Carthage, edit `Podfile`/`Cartfile` and run `pod install` / `carthage update --use-xcframeworks`.\n\n## Skip Coverage Tools\n\nDo not configure or run coverage tools (`-enableCodeCoverage YES`, `xccov`, `slather`). Coverage is measured separately by the evaluation harness.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":9632,"content_sha256":"6765fb1f54ffbe34ecd25099ea5c735bae1c2f8f93bb7ac21d3b1fa0821d2adb"},{"filename":"extensions/typescript-examples.md","content":"# TypeScript Pipeline Examples\n\nConcrete input→output examples for the test generation pipeline targeting a TypeScript codebase using Vitest. These show what each pipeline phase produces for a small project.\n\n> Jest, Mocha, and node:test follow the same shape. Replace `vi.fn()` / `vi.mock()` with `jest.fn()` / `jest.mock()` (Jest) or hand-written stubs (node:test/Mocha) and adjust the runner command accordingly.\n\n## Source Under Test\n\nA simple `InvoiceService` in a TypeScript library using Vitest:\n\n```text\nsrc/\n invoiceService.ts\n invoice.ts\n invoiceRepository.ts\n index.ts (re-exports public API)\npackage.json\ntsconfig.json\nvitest.config.ts\npackage-lock.json (committed for reproducible installs)\n```\n\n```typescript\n// src/invoiceService.ts\nimport { Invoice, InvoiceStatus } from \"./invoice\";\nimport { InvoiceRepository } from \"./invoiceRepository\";\n\nexport class InvoiceService {\n constructor(private readonly repository: InvoiceRepository) {}\n\n calculateTotal(invoice: Invoice): number {\n if (invoice == null) throw new TypeError(\"invoice must not be null\");\n if (invoice.lineItems.length === 0) {\n throw new Error(\"Invoice has no line items.\");\n }\n\n const subtotal = invoice.lineItems.reduce(\n (acc, li) => acc + li.quantity * li.unitPrice,\n 0,\n );\n const tax = subtotal * invoice.taxRate;\n return roundTo2(subtotal + tax);\n }\n\n async getById(id: number): Promise\u003cInvoice> {\n const invoice = await this.repository.find(id);\n if (invoice == null) {\n throw new Error(`Invoice ${id} not found.`);\n }\n return invoice;\n }\n\n async markAsPaid(id: number): Promise\u003cvoid> {\n const invoice = await this.repository.find(id);\n if (invoice == null) {\n throw new Error(`Invoice ${id} not found.`);\n }\n if (invoice.status === InvoiceStatus.Paid) {\n throw new Error(\"Invoice is already paid.\");\n }\n invoice.status = InvoiceStatus.Paid;\n invoice.paidDate = new Date();\n await this.repository.update(invoice);\n }\n}\n\nfunction roundTo2(n: number): number {\n return Math.round((n + Number.EPSILON) * 100) / 100;\n}\n```\n\n## Sample Research Output\n\nWhat `code-testing-researcher` produces in `\u003cTESTAGENT_DIR>/research.md`:\n\n```markdown\n# Test Generation Research\n\n## Project Overview\n- **Path**: /work/contoso-billing\n- **Language**: TypeScript 5.4\n- **Module system**: ESM (`\"type\": \"module\"` in package.json)\n- **Test Framework**: Vitest 1.x (detected via `vitest.config.ts` and `devDependencies.vitest`)\n- **Package Manager**: npm (lockfile = `package-lock.json`)\n\n## Coverage Baseline\n- **Initial Line Coverage**: unknown\n- **Strategy**: broad\n- **Existing Test Count**: 0 tests across 0 files\n\n## Build & Test Commands\n- **Install**: `npm ci`\n- **Type-check**: `npx tsc --noEmit`\n- **Test**: `npx vitest run` (NEVER bare `vitest` — that starts watch mode)\n- **Lint**: none configured\n\n## Project Structure\n- Source: `src/`\n- Tests: none (will colocate as `src/invoiceService.test.ts` to match Vitest defaults)\n\n## Files to Test\n\n### High Priority\n| File | Classes/Functions | Testability | Notes |\n|------|-------------------|-------------|-------|\n| src/invoiceService.ts | InvoiceService: calculateTotal, getById, markAsPaid | High | Core business logic, repository dependency needs mocking |\n\n### Low Priority / Skip\n| File | Reason |\n|------|--------|\n| src/invoice.ts | Type definitions and enum |\n| src/invoiceRepository.ts | Interface only |\n| src/index.ts | Re-export barrel |\n\n## Existing Tests\n- No existing tests found\n\n## Testing Patterns\n- No existing patterns; recommend `describe`/`it` blocks, `vi.fn()` stubs for the repository interface, and `it.each` for table-driven cases.\n\n## Recommendations\n- Co-locate test next to source (`src/invoiceService.test.ts`) — matches Vitest defaults and avoids reaching into `../src/`\n- Use a fake-timers helper (`vi.useFakeTimers()`) to control `new Date()` in `markAsPaid`\n- Use a type-narrowed mock object (`{ find: vi.fn(), update: vi.fn() } satisfies InvoiceRepository`) rather than full module mocking\n```\n\n## Sample Plan Output\n\nWhat `code-testing-planner` produces in `\u003cTESTAGENT_DIR>/plan.md`:\n\n```markdown\n# Test Implementation Plan\n\n## Overview\nGenerate Vitest tests for InvoiceService, covering all three public methods\nacross happy path, edge case, and error scenarios. Single phase since there is\nonly one source file.\n\n## Commands\n- **Install**: `npm ci`\n- **Type-check**: `npx tsc --noEmit`\n- **Test (file-scoped during dev)**: `npx vitest run src/invoiceService.test.ts`\n- **Test (full)**: `npx vitest run`\n\n## Phase Summary\n| Phase | Focus | Files | Est. Tests |\n|-------|-------|-------|------------|\n| 1 | InvoiceService | 1 | 9-12 |\n\n---\n\n## Phase 1: InvoiceService\n\n### Overview\nCover all public methods of InvoiceService. `calculateTotal` is pure logic tested\nwith `it.each`. Async methods require a fake repository.\n\n### Files to Test\n\n#### 1. invoiceService.ts\n- **Source**: `src/invoiceService.ts`\n- **Test File**: `src/invoiceService.test.ts`\n\n**Methods to Test**:\n1. `calculateTotal` — Pure calculation logic\n - Happy path: single line item returns quantity × price + tax\n - Happy path: multiple line items summed correctly\n - Edge case: zero tax rate returns subtotal only\n - Error case: null invoice throws TypeError\n - Error case: empty line items throws Error\n\n2. `getById` — Repository lookup\n - Happy path: existing ID returns invoice\n - Error case: missing ID rejects with Error\n\n3. `markAsPaid` — State transition\n - Happy path: pending invoice transitions to Paid with `paidDate` set\n - Error case: already-paid rejects with Error\n - Error case: missing ID rejects with Error\n\n### Success Criteria\n- [ ] Test file created at `src/invoiceService.test.ts`\n- [ ] `npx tsc --noEmit` succeeds\n- [ ] `npx vitest run` reports all tests passed\n- [ ] No real network/timers — repository is a `vi.fn()` fake, `new Date()` is controlled via fake timers\n```\n\n## Sample Generated Test File\n\nWhat `code-testing-implementer` produces:\n\n```typescript\n// src/invoiceService.test.ts\nimport { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport { Invoice, InvoiceStatus } from \"./invoice\";\nimport type { InvoiceRepository } from \"./invoiceRepository\";\nimport { InvoiceService } from \"./invoiceService\";\n\nfunction makeRepository(): InvoiceRepository & { find: ReturnType\u003ctypeof vi.fn>; update: ReturnType\u003ctypeof vi.fn> } {\n return {\n find: vi.fn(),\n update: vi.fn(),\n };\n}\n\ndescribe(\"InvoiceService\", () => {\n let repository: ReturnType\u003ctypeof makeRepository>;\n let sut: InvoiceService;\n\n beforeEach(() => {\n repository = makeRepository();\n sut = new InvoiceService(repository);\n });\n\n // --- calculateTotal ---\n\n describe(\"calculateTotal\", () => {\n it.each([\n { quantity: 1, unitPrice: 100, taxRate: 0.1, expected: 110 },\n { quantity: 3, unitPrice: 25, taxRate: 0, expected: 75 },\n { quantity: 2, unitPrice: 9.99, taxRate: 0.07, expected: 21.38 },\n ])(\n \"returns $expected for $quantity × $unitPrice with tax $taxRate\",\n ({ quantity, unitPrice, taxRate, expected }) => {\n const invoice: Invoice = {\n id: 1,\n status: InvoiceStatus.Pending,\n taxRate,\n lineItems: [{ quantity, unitPrice }],\n };\n\n expect(sut.calculateTotal(invoice)).toBe(expected);\n },\n );\n\n it(\"throws TypeError when invoice is null\", () => {\n expect(() => sut.calculateTotal(null as unknown as Invoice)).toThrow(TypeError);\n });\n\n it(\"throws when line items are empty\", () => {\n const invoice: Invoice = {\n id: 1,\n status: InvoiceStatus.Pending,\n taxRate: 0,\n lineItems: [],\n };\n\n expect(() => sut.calculateTotal(invoice)).toThrow(\"no line items\");\n });\n });\n\n // --- getById ---\n\n describe(\"getById\", () => {\n it(\"returns the invoice for an existing id\", async () => {\n const expected: Invoice = { id: 42, status: InvoiceStatus.Pending, taxRate: 0, lineItems: [] };\n repository.find.mockResolvedValue(expected);\n\n await expect(sut.getById(42)).resolves.toBe(expected);\n expect(repository.find).toHaveBeenCalledWith(42);\n });\n\n it(\"rejects with Error when the id is missing\", async () => {\n repository.find.mockResolvedValue(null);\n\n await expect(sut.getById(999)).rejects.toThrow(/999/);\n });\n });\n\n // --- markAsPaid ---\n\n describe(\"markAsPaid\", () => {\n beforeEach(() => {\n vi.useFakeTimers();\n vi.setSystemTime(new Date(\"2025-01-01T12:00:00.000Z\"));\n });\n afterEach(() => {\n vi.useRealTimers();\n });\n\n it(\"transitions a pending invoice to Paid with paidDate set\", async () => {\n const invoice: Invoice = {\n id: 1,\n status: InvoiceStatus.Pending,\n taxRate: 0,\n lineItems: [],\n };\n repository.find.mockResolvedValue(invoice);\n repository.update.mockResolvedValue(undefined);\n\n await sut.markAsPaid(1);\n\n expect(invoice.status).toBe(InvoiceStatus.Paid);\n expect(invoice.paidDate).toEqual(new Date(\"2025-01-01T12:00:00.000Z\"));\n expect(repository.update).toHaveBeenCalledWith(invoice);\n });\n\n it(\"rejects when the invoice is already paid\", async () => {\n const invoice: Invoice = {\n id: 1,\n status: InvoiceStatus.Paid,\n taxRate: 0,\n lineItems: [],\n };\n repository.find.mockResolvedValue(invoice);\n\n await expect(sut.markAsPaid(1)).rejects.toThrow(\"already paid\");\n expect(repository.update).not.toHaveBeenCalled();\n });\n\n it(\"rejects when the id is missing\", async () => {\n repository.find.mockResolvedValue(null);\n\n await expect(sut.markAsPaid(999)).rejects.toThrow(/999/);\n });\n });\n});\n```\n\n## Sample Fix Cycle\n\nWhen the implementer encounters a runner or type error, the fixer agent diagnoses and resolves it.\n\n**Test output:**\n\n```text\nError: Vitest failed to access its internal state.\nOne of the following is possible:\n- \"vitest\" is imported directly without running \"vitest\" command\n```\n\n**Fixer diagnosis:** The agent ran `node src/invoiceService.test.ts` (or bare `vitest`, which is watch-mode). The runner must be invoked via `npx vitest run`.\n\n**Fix applied:**\n\n```bash\n# Wrong — bare vitest starts an interactive watcher in CI\nnpx vitest\n\n# Right — `run` is the one-shot command\nnpx vitest run\n```\n\n**Rerun:** SUCCESS\n\n---\n\n**Another common cycle — ESM/CJS mismatch:**\n\n**Test output:**\n\n```text\nSyntaxError: Cannot use import statement outside a module\n```\n\n**Fixer diagnosis:** The project's `tsconfig.json` emits ESM (`\"module\": \"NodeNext\"`) but `package.json` has no `\"type\": \"module\"`. Vitest happens to handle this natively; switching to Jest would require additional configuration. The fix here is to ensure Vitest is the runner being used (as already configured in `vitest.config.ts`) and avoid recompiling test files through a separate non-ESM-aware tool.\n\n**Fix applied:** Use `npx vitest run` (which uses esbuild internally and handles both ESM and CJS) instead of compiling with `tsc` and running the emitted `.js` directly.\n\n**Rerun:** SUCCESS\n\n---\n\n**Another common cycle — wrong mock typing:**\n\n**Build output:**\n\n```text\nsrc/invoiceService.test.ts:14:5 - error TS2322: Type '{ find: Mock\u003cany, any>; }' is not assignable to type 'InvoiceRepository'.\n Property 'update' is missing in type '{ find: Mock\u003cany, any>; }' but required in type 'InvoiceRepository'.\n```\n\n**Fixer diagnosis:** The fake repository only stubbed `find`, not `update`. The `InvoiceRepository` interface requires both. TypeScript caught this at compile time.\n\n**Fix applied:**\n\n```typescript\n// Before\nconst repository = { find: vi.fn() } as InvoiceRepository;\n\n// After — provide both methods, narrow the return type so the test code keeps autocomplete\nfunction makeRepository(): InvoiceRepository & { find: ReturnType\u003ctypeof vi.fn>; update: ReturnType\u003ctypeof vi.fn> } {\n return { find: vi.fn(), update: vi.fn() };\n}\n```\n\n**Rebuild + rerun:** SUCCESS\n\n## Sample Final Report\n\nWhat `test-engineer` produces at Step 9:\n\n```markdown\n## Test Generation Report\n\n**Project**: contoso-billing (TypeScript)\n**Strategy**: Direct (single source file in scope)\n\n### Results\n| Metric | Value |\n|----------------|-------|\n| Tests created | 9 |\n| Tests passing | 9 |\n| Tests failing | 0 |\n| Files created | 1 |\n\n### Files Created\n- `src/invoiceService.test.ts` (9 tests, 3 parameterized via `it.each`)\n\n### Coverage\n- InvoiceService.calculateTotal — 3 happy path, 2 error cases\n- InvoiceService.getById — 1 happy path, 1 error case\n- InvoiceService.markAsPaid — 1 happy path, 2 error cases\n\n### Build / Test Validation\n- Install: ✅ `npm ci`\n- Type-check: ✅ `npx tsc --noEmit`\n- Test run: ✅ `npx vitest run`\n\n### Next Steps\n- Add tests for any HTTP/Express adapters once they exist\n- Consider property-based testing (`fast-check`) for `calculateTotal` rounding\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":13006,"content_sha256":"0f95defcae997f2e15d71f379e08e043987f5fb9892a82669505bfdfd3767b16"},{"filename":"extensions/typescript.md","content":"# TypeScript Extension\n\nLanguage-specific guidance for TypeScript (and JavaScript) test generation.\n\n## Rule #1: Investigate the Repo First\n\nBefore writing any test or running any command, read:\n\n1. **Existing tests** — find `*.test.ts` / `*.spec.ts` files and copy their style (imports, describe/it vs test, assertion patterns, mock approach)\n2. **`package.json`** — `scripts.test`, `devDependencies`, `type` field\n3. **Config files** — `tsconfig.json`, `jest.config.*`, `vitest.config.*`, `eslint.config.*`\n\nUse the repo's existing test runner and conventions — do not switch frameworks. If multiple runners are configured, follow whichever `scripts.test` invokes. Only introduce a framework if the repo has no tests at all.\n\n## Package Manager Detection\n\nDetect the package manager from lockfiles and use it consistently for **all** commands:\n\n| Indicator | Manager | Run script | Execute binary |\n|-----------|---------|------------|----------------|\n| `pnpm-lock.yaml` | pnpm | `pnpm test` | `pnpm exec \u003ctool>` |\n| `yarn.lock` | Yarn | `yarn test` | `yarn \u003ctool>` |\n| `bun.lockb` / `bun.lock` | Bun | `bun test` | `bunx \u003ctool>` |\n| `package-lock.json` or none | npm | `npm test` | `npx \u003ctool>` |\n\nUse `\u003cexec>` below as shorthand for the detected exec command.\n\n## Build Commands\n\n| Scope | Command |\n|-------|---------|\n| Type check | `\u003cexec> tsc --noEmit` or the repo's `typecheck` script |\n| Build (if configured) | The repo's `build` script |\n\nMany projects don't need an explicit build step — the test runner handles transpilation.\n\n## Test Commands\n\nDetect the runner from `devDependencies` and `scripts.test`. Always prefer the repo's test script first.\n\n| Runner | Run once | Filter by file | Filter by name |\n|--------|----------|----------------|----------------|\n| **Jest** | `\u003cexec> jest` | `\u003cexec> jest path/to/file` | `\u003cexec> jest -t \"name\"` |\n| **Vitest** | `\u003cexec> vitest run` | `\u003cexec> vitest run path/to/file` | `\u003cexec> vitest run -t \"name\"` |\n| **Mocha** | `\u003cexec> mocha` | (use config or positional args) | `\u003cexec> mocha --grep \"name\"` |\n\n- **Always use `vitest run`** (not bare `vitest`) — bare `vitest` starts watch mode\n- **Never use `--watch`** — the agent must not start interactive/watch mode\n- For Jest: `--bail` to stop on first failure, `--verbose` for detail\n- Mocha `--grep` filters by **test name**, not file path\n\n## Lint Command\n\nUse the repo's lint script first. Otherwise detect from `devDependencies` and config:\n\n- `eslint.config.*` or `.eslintrc.*` → `\u003cexec> eslint --fix path/to/file.ts`\n- `prettier` → `\u003cexec> prettier --write path/to/file.ts`\n- `biome.json` → `\u003cexec> biome check --write path/to/file.ts`\n\n## Project Layout and Imports\n\n| Layout | Import Style |\n|--------|-------------|\n| Colocated (`src/module.test.ts`) | `import { X } from './module'` |\n| `__tests__/` dir | `import { X } from '../module'` |\n| Top-level `tests/` | `import { X } from '../src/module'` |\n\n- **Match existing test imports** — copy path style from neighboring tests\n- If `tsconfig.json` has `paths` aliases (e.g., `@/`), use them in tests too\n- For monorepos: import from the package name, not relative cross-package paths\n- For monorepo workspaces (Nx, Turborepo, Lerna): run tests via the workspace tool (`nx test \u003cproject>`, `turbo test`), not from a random package directory\n\n## Test File Naming\n\n- Match existing convention — check for `.test.ts` vs `.spec.ts`\n- Jest/Vitest default: `*.test.ts`, `*.spec.ts`, or files inside `__tests__/`\n- Place test files to mirror the existing project pattern\n\n## Parameterized Test Display Names\n\nApply [Report-safe test names and result validation](../../code-testing/unit-test-generation.prompt.md#report-safe-test-names-and-result-validation).\nFor Jest `it.each`/`test.each`, interpolate only a safe label (`$Name` for object\nrows), or use a short behavior label with `%#` for the case index. Do not use\n`%p`, `%s`, or `$Input`/`$Expected` to render arbitrary data in the title.\nHarmless numeric placeholders such as `%i` remain fine.\n\nFor a word counter whose contract treats form-feed as whitespace, keep the real\nU+000C input but give the case a safe name:\n\n```typescript\nit.each([\n { Name: \"form-feed separator\", Input: \"one\\ftwo\", Expected: 2 },\n { Name: \"space separator\", Input: \"one two\", Expected: 2 },\n])(\"countWords: $Name\", ({ Input, Expected }) => {\n expect(countWords(Input)).toBe(Expected);\n});\n```\n\nThe escape in `Input` becomes an actual control character at runtime; the title\ndoes not contain it. Reuse the configured reporter and validate its exported\nartifact when required/configured; do not install `jest-junit` just for this check.\n\n## Common Errors\n\n| Error | Fix |\n|-------|-----|\n| `Cannot find module 'X'` | Check existing imports for correct paths; verify `tsconfig.json` `paths`; check `moduleNameMapper` (Jest) or `resolve.alias` (Vitest) |\n| `TS2305: has no exported member` | Verify the exact export name from the source file |\n| `TS2345: type not assignable` | Match the expected type; use type assertion only for mock objects |\n| `SyntaxError: Unexpected token` / `Jest encountered an unexpected token` | Verify TS transform config (`ts-jest`, `@swc/jest`, or Vitest handles natively) |\n| `ReferenceError: describe is not defined` | Vitest: import from `vitest` or set `globals: true` in config; Jest: ensure tests run under Jest not bare `node` |\n| `Cannot use import statement outside a module` / `ERR_REQUIRE_ESM` | ESM/CJS mismatch — align runner config with the project's module system (see ESM section); do **not** blindly set `\"type\": \"module\"` |\n| `ReferenceError: document is not defined` | Set test environment: `testEnvironment: 'jsdom'` (Jest) or `environment: 'jsdom'` (Vitest) |\n| `jest.mock() ... out-of-scope variables` | Keep `jest.mock()` at top level; don't reference variables declared after the mock call (Jest hoists mocks) |\n| `Cannot find module '@/...'` | Mirror the project's alias config in the test runner's module resolution |\n| `Warning: not wrapped in act(...)` | Await async UI updates using the repo's existing pattern (`waitFor`, `act`) |\n\n## ESM vs CommonJS\n\nCheck these signals to determine the project's module system:\n\n- `\"type\": \"module\"` in `package.json` → ESM\n- `\"module\": \"ESNext\"` or `\"NodeNext\"` in `tsconfig.json` → ESM output (but not sufficient alone)\n- `.mjs`/`.mts` extensions → ESM files\n\nIf the test runner fails with ESM errors, align the runner's config with the project's module system. **Do not change `package.json` `type` field** — align the test runner to match whatever the project uses:\n\n- **Jest**: `--experimental-vm-modules` + `ts-jest` with `useESM: true`, or `@swc/jest`\n- **Vitest**: handles ESM natively\n- **Mocha**: `--loader ts-node/esm`\n\n## Mocking Rules\n\n- Prefer dependency injection over module mocking\n- Use typed mocks: `jest.Mocked\u003cT>`, `vi.mocked(obj)`, or `Partial\u003cT>` with `as T`\n- Jest: `jest.mock()` is hoisted — keep at top level, don't close over local variables\n- Vitest: `vi.mock()` follows the same hoisting rules\n- If a test needs more than 3–4 mocks, flag it as a design smell\n- Mock reset: rely on `clearMocks`/`restoreMocks` config if present; otherwise reset in `beforeEach`\n\n## Framework-Specific Notes\n\n- **React/Preact**: use `@testing-library/react`, wrap with necessary providers (router, query client, theme) matching existing test setup\n- **Express/Koa**: use `supertest` for HTTP testing if the repo already uses it\n- **NestJS**: build testing module with `Test.createTestingModule` — don't instantiate controllers directly\n\n## Dependency Installation (Last Resort)\n\nOnly install packages after investigation confirms they are missing. Use the detected package manager:\n\n```\n\u003cmanager> add --save-dev jest ts-jest @types/jest\n\u003cmanager> add --save-dev vitest\n```\n\nNever install test infrastructure that conflicts with what the repo already uses.\n\n## Skip Coverage Tools\n\nDo not configure or run coverage tools (istanbul, c8, `vitest --coverage`). Coverage is measured separately by the evaluation harness.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":8058,"content_sha256":"c6662430a1e0f04658d210f0031bd7fcc4de362bb64660a924d4fa5842012dbb"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Code Testing Extensions","type":"text"}]},{"type":"paragraph","content":[{"text":"This reference-only catalog lists bundled guidance for the code-testing pipeline. Read the matching file directly; do not invoke this catalog as a skill. Resource paths are relative to this catalog, not the project workspace.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Available Extension Files","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"File","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Language","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Contents","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"extensions/dotnet.md","type":"text","marks":[{"type":"link","attrs":{"href":"extensions/dotnet.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":".NET (C#/F#/VB)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Build commands, test commands, project reference validation, common CS error codes, MSTest template","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"extensions/python.md","type":"text","marks":[{"type":"link","attrs":{"href":"extensions/python.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Python","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Framework-adaptive test commands (pytest, custom runners), project layout detection, mocking guidelines, common errors","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"extensions/typescript.md","type":"text","marks":[{"type":"link","attrs":{"href":"extensions/typescript.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"TypeScript/JavaScript","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Build/test commands (Jest/Vitest/Mocha), framework detection, mocking, TS-specific considerations","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"extensions/powershell.md","type":"text","marks":[{"type":"link","attrs":{"href":"extensions/powershell.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"PowerShell","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Test commands (Pester v5), module import patterns, discovery/run pitfalls, mocking, common errors","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"extensions/cpp.md","type":"text","marks":[{"type":"link","attrs":{"href":"extensions/cpp.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"C++","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Testing internals with friend declarations","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"extensions/go.md","type":"text","marks":[{"type":"link","attrs":{"href":"extensions/go.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Go","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"go test","type":"text","marks":[{"type":"code_inline"}]},{"text":" commands, table-driven tests, integration vs unit layout, mocking via interfaces, common errors","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"extensions/java.md","type":"text","marks":[{"type":"link","attrs":{"href":"extensions/java.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Java","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Maven/Gradle commands, JUnit 4/5 and TestNG detection, Mockito, Spring Boot slices, common errors","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"extensions/rust.md","type":"text","marks":[{"type":"link","attrs":{"href":"extensions/rust.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Rust","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"cargo test","type":"text","marks":[{"type":"code_inline"}]},{"text":" commands, unit vs integration vs doc tests, features, async test harnesses, common errors","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"extensions/ruby.md","type":"text","marks":[{"type":"link","attrs":{"href":"extensions/ruby.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Ruby","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"RSpec and Minitest commands, Bundler usage, Rails specifics, mocking patterns, common errors","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"extensions/swift.md","type":"text","marks":[{"type":"link","attrs":{"href":"extensions/swift.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Swift","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"SPM and Xcode test commands, XCTest vs Swift Testing, ","type":"text"},{"text":"@testable import","type":"text","marks":[{"type":"code_inline"}]},{"text":", async/throws tests, common errors","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"extensions/kotlin.md","type":"text","marks":[{"type":"link","attrs":{"href":"extensions/kotlin.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Kotlin","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Gradle commands, JUnit/Kotest detection, MockK, coroutines test, KMP and Android specifics, common errors","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"extensions/dotnet-examples.md","type":"text","marks":[{"type":"link","attrs":{"href":"extensions/dotnet-examples.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":".NET (C#/F#/VB)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Concrete pipeline examples: sample research output, plan, generated tests, fix cycles, final report","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"extensions/python-examples.md","type":"text","marks":[{"type":"link","attrs":{"href":"extensions/python-examples.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Python","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Concrete pipeline examples (pytest): research, plan, generated test file, fix cycles, final report","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"extensions/typescript-examples.md","type":"text","marks":[{"type":"link","attrs":{"href":"extensions/typescript-examples.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"TypeScript/JavaScript","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Concrete pipeline examples (Vitest, applicable to Jest): research, plan, generated test file, fix cycles, final report","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"extensions/go-examples.md","type":"text","marks":[{"type":"link","attrs":{"href":"extensions/go-examples.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Go","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Concrete pipeline examples (standard ","type":"text"},{"text":"testing","type":"text","marks":[{"type":"code_inline"}]},{"text":"): research, plan, table-driven test file, fix cycles, final report","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"extensions/java-examples.md","type":"text","marks":[{"type":"link","attrs":{"href":"extensions/java-examples.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Java","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Concrete pipeline examples (JUnit 5 + Mockito on Maven): research, plan, generated test file, fix cycles, final report","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Usage","type":"text"}]},{"type":"paragraph","content":[{"text":"Read the appropriate base extension before writing test code. Load an ","type":"text"},{"text":"\u003clanguage>-examples.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" file only when the repository has no representative tests and the base extension does not resolve the needed pattern. Do not load end-to-end examples for a focused request with established local conventions.","type":"text"}]}]},"metadata":{"date":"2026-11-02","name":"code-testing-extensions","author":"@skillopedia","source":{"stars":5797,"repo_name":"skills","origin_url":"https://github.com/dotnet/skills/blob/HEAD/plugins/dotnet-test/skills/code-testing-extensions/SKILL.md","repo_owner":"dotnet","body_sha256":"81149d3622f5e358e45d17e2e566077b12c92db311f696d130ea6fc32d95cfc0","cluster_key":"cd5b724eb4a1d35cb47d0faf474662f3495431c7c6d0d3e69b553c2f6cd6641c","clean_bundle":{"format":"clean-skill-bundle-v1","source":"dotnet/skills/plugins/dotnet-test/skills/code-testing-extensions/SKILL.md","text_bytes":237185,"source_commit":"95e7d0cb7994d47b5789baa68e13f7a2f348b956","skills_sh_rank":5328,"attachment_count":21,"text_attachments":21,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"plugins/dotnet-test/skills/code-testing-extensions/SKILL.md","import_metadata":{"date":"2026-11-02","author":"@skillopedia","version":"v1","category":"testing-qa","category_label":"Testing"},"exact_dupes_collapsed_into_this":0},"license":"MIT","version":"v1","category":"testing-qa","import_tag":"skills-sh-top20k-2026-11","description":"Provides file paths to language-specific extension files for the code-testing pipeline. Read this reference-only catalog for extension guidance files (e.g., dotnet.md for .NET, cpp.md for C++). Agents and skills read its bundled files directly; do not invoke this catalog through the model's skill tool.","user-invocable":false,"disable-model-invocation":true}},"renderedAt":1793670936005}

Code Testing Extensions This reference-only catalog lists bundled guidance for the code-testing pipeline. Read the matching file directly; do not invoke this catalog as a skill. Resource paths are relative to this catalog, not the project workspace. Available Extension Files | File | Language | Contents | |------|----------|----------| | extensions/dotnet.md | .NET (C#/F#/VB) | Build commands, test commands, project reference validation, common CS error codes, MSTest template | | extensions/python.md | Python | Framework-adaptive test commands (pytest, custom runners), project layout detectio…