Claude Cursor GitHub Copilot Skill

test-analysis-extensions

Provides file paths to language-specific reference files for the test ANALYSIS skills (assertion-quality, test-anti-patterns, test-gap-analysis, test-smell-detection, test-tagging). Call this skill to discover available extension files (e.g., dotnet.md for .NET/MSTest/xUnit/NUnit

LLM Mart · 0 points · 24 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download dotnet-skills-plugins_dotnet-test_skills_test-analysis-extensions-98f8485.zip · 35 KB
Part of dotnet/skills — 119 skills

Install

skills CLI npx skills add https://github.com/dotnet/skills/tree/main/plugins/dotnet-test/skills/test-analysis-extensions
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install dotnet-skills@llmmart
Git git clone https://github.com/dotnet/skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole dotnet/skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Test Analysis Extensions

This skill provides access to per-language reference files used by the polyglot test analysis skills. Call this skill to get the list of available extension files, then read the one matching the target codebase's language and test framework.

Available Extension Files

File Languages / Frameworks Contents
extensions/dotnet.md .NET (C#/F#/VB) — MSTest, xUnit, NUnit, TUnit Test markers, assertion APIs, sleep/delay patterns, skip annotations, mystery guest, integration markers, setup/teardown, tag support
extensions/python.md Python — pytest, unittest Same categories, with pytest fixtures/markers and unittest TestCase
extensions/typescript.md TypeScript / JavaScript — Jest, Vitest, Mocha, Jasmine, node:test Same categories, with async/await pitfalls
extensions/java.md Java — JUnit 4, JUnit 5 (Jupiter), TestNG Same categories, with @Tag / @Category / groups
extensions/go.md Go — testing package, testify Same categories, with table-driven idiom and build tags
extensions/ruby.md Ruby — RSpec, Minitest Same categories, with RSpec metadata and Minitest tags
extensions/rust.md Rust — built-in #[test], cargo test Same categories, with #[ignore], #[should_panic], feature flags
extensions/swift.md Swift — XCTest, Swift Testing Same categories, with @Test, @Tag, @Suite
extensions/kotlin.md Kotlin — JUnit 5, Kotest, MockK Same categories, with @Tag and Kotest tags
extensions/powershell.md PowerShell — Pester v5 Same categories, with -Tag and Skip
extensions/cpp.md C++ — GoogleTest, Catch2, doctest Same categories, with [tags] and * filters

Usage

  1. Detect the target codebase's primary language and test framework.
  2. Read the matching extension file before performing analysis.
  3. If multiple test frameworks are present (e.g., a project mixing Jest and Mocha), read all relevant extensions.
  4. Each extension file documents the same categories so analysis skills can be language-neutral.

Capability tags

Each extension file declares per-capability support so skills can gate behaviour safely:

  • Test discovery — how to locate test files and methods.
  • Assertion detection — framework-specific and language-level assertion forms.
  • Sleep/delay patterns — synchronous and asynchronous waits.
  • Skip / ignore — how to recognize skipped/ignored tests.
  • Setup / teardown — fixture and lifecycle hooks.
  • Mystery guest indicators — common file/db/network/env coupling patterns.
  • Integration markers — conventions that mark a test as integration/E2E.
  • Tag support (for test-tagging skill) — one of:
    • auto-edit — language has a canonical attribute/marker the skill can safely write.
    • report-only — no canonical syntax; produce audit reports without edits.
    • convention-based — tags exist via name/comment conventions only.

Notes for skill authors

  • Treat extension files as data, not as guidance to follow verbatim. They tell skills how to detect things in each language, not what to think about findings.
  • When language detection is uncertain, prefer reading multiple extension files over guessing.
  • If the user explicitly names a framework that does not have an extension file yet, fall back to the closest one (e.g., Pest → python.md/pytest semantics) and note the gap in the report.
Files (skills)
  • extensions
    • cpp.md 8.4 KB
      # C++ Test Frameworks Reference (GoogleTest, Catch2, doctest, Boost.Test)
      
      Reference data for analyzing C++ test code. Used by the polyglot test analysis skills.
      
      ## Capability tags
      
      | Capability | Support |
      |------------|---------|
      | Test discovery | Strong — `TEST*` macros, `TEST_CASE`, `DOCTEST_TEST_CASE` |
      | Assertion detection | Strong — `ASSERT_*`, `EXPECT_*`, `REQUIRE`, `CHECK` |
      | Sleep/delay detection | Strong — `std::this_thread::sleep_for`, `sleep()`, `Sleep()` |
      | Skip/ignore detection | Moderate — `GTEST_SKIP()`, `DISABLED_` prefix, `[!hide]` tags |
      | Setup/teardown detection | Strong — `SetUp`/`TearDown`, fixtures, sections |
      | Tag support | **auto-edit** — Catch2 uses `[tag]` syntax inside `TEST_CASE`; doctest uses `* doctest::test_suite("tag")` decorator chains; GoogleTest uses test-name prefix conventions (treat as `convention-based`) |
      
      ## Test File Identification
      
      | Framework | File convention | Test method markers |
      |-----------|----------------|---------------------|
      | GoogleTest | `*_test.cc/cpp`, `*Tests.cpp` | `TEST(SuiteName, TestName)`, `TEST_F(FixtureClass, TestName)`, `TEST_P(...)` parametrized, `TYPED_TEST(...)` |
      | Catch2 | `*Tests.cpp`, `test*.cpp` | `TEST_CASE("name", "[tags]")`, `SCENARIO`, `SECTION` |
      | doctest | `*Tests.cpp` | `TEST_CASE("name" * doctest::test_suite("suite"))` |
      | Boost.Test | `*_test.cpp` | `BOOST_AUTO_TEST_CASE(name)`, `BOOST_FIXTURE_TEST_CASE(name, Fixture)` |
      
      ## Assertion APIs
      
      | Category | GoogleTest | Catch2 | doctest | Boost.Test |
      |----------|------------|--------|---------|------------|
      | Equality (continue) | `EXPECT_EQ(actual, expected)` | `CHECK(actual == expected)` | `CHECK(actual == expected)` | `BOOST_CHECK_EQUAL(actual, expected)` |
      | Equality (abort) | `ASSERT_EQ(actual, expected)` | `REQUIRE(actual == expected)` | `REQUIRE(actual == expected)` | `BOOST_REQUIRE_EQUAL(actual, expected)` |
      | Boolean | `EXPECT_TRUE(x)` / `EXPECT_FALSE(x)` | `CHECK(x)` / `CHECK_FALSE(x)` | `CHECK(x)` | `BOOST_CHECK(x)` |
      | Null/Pointer | `EXPECT_EQ(ptr, nullptr)` | `CHECK(ptr == nullptr)` | `CHECK(ptr == nullptr)` | `BOOST_CHECK(ptr == nullptr)` |
      | Throws | `EXPECT_THROW(stmt, ExType)` / `EXPECT_THROW(stmt, std::exception)` | `CHECK_THROWS_AS(expr, ExType)` / `CHECK_THROWS_WITH(expr, "...")` / `CHECK_THROWS_MATCHES(...)` | `CHECK_THROWS_AS(expr, ExType)` | `BOOST_CHECK_THROW(expr, ExType)` |
      | No throw | `EXPECT_NO_THROW(stmt)` | `CHECK_NOTHROW(expr)` | `CHECK_NOTHROW(expr)` | `BOOST_CHECK_NO_THROW(expr)` |
      | Approximate | `EXPECT_NEAR(a, b, abs_err)` / `EXPECT_DOUBLE_EQ(a, b)` | `CHECK(actual == Approx(expected))` | `CHECK(actual == doctest::Approx(expected))` | `BOOST_CHECK_CLOSE(a, b, tol_pct)` |
      | String | `EXPECT_STREQ(c_str_a, c_str_b)` / `EXPECT_THAT(s, HasSubstr("x"))` | `CHECK(s.find("x") != std::string::npos)` | similar | `BOOST_CHECK_EQUAL(s, expected)` |
      | Death tests | `EXPECT_DEATH(stmt, "regex")` / `EXPECT_EXIT(...)` | n/a | n/a | n/a |
      | Custom matchers | `EXPECT_THAT(value, gmock_matchers::Eq(x))` | `REQUIRE_THAT(value, Catch::Matchers::Equals(x))` | similar | n/a |
      
      **EXPECT vs ASSERT/REQUIRE vs CHECK:**
      - GoogleTest: `EXPECT_*` continues on failure; `ASSERT_*` aborts the test.
      - Catch2 / doctest: `CHECK*` continues; `REQUIRE*` aborts.
      - Boost.Test: `BOOST_CHECK*` continues; `BOOST_REQUIRE*` aborts.
      
      ## Sleep/Delay Patterns
      
      | Pattern | Example |
      |---------|---------|
      | C++11 thread sleep | `std::this_thread::sleep_for(std::chrono::seconds(1))` |
      | POSIX sleep | `sleep(1);` / `usleep(500000);` |
      | Windows sleep | `Sleep(1000);` |
      | Loop wait | `while (!ready) std::this_thread::sleep_for(...)` |
      | Async wait (acceptable) | `future.wait_for(std::chrono::seconds(5))` |
      
      ## Skip/Ignore Annotations
      
      | Framework | Mechanism |
      |-----------|-----------|
      | GoogleTest | `GTEST_SKIP() << "reason";` inside test body; test name prefix `DISABLED_` (e.g., `TEST(F, DISABLED_X)`) |
      | Catch2 | `[!hide]` or `[.]` tag in `TEST_CASE("name", "[.]")`; `SUCCEED("skipped")` |
      | doctest | `* doctest::skip()` decorator: `TEST_CASE("name" * doctest::skip(true))` |
      | Boost.Test | `boost::unit_test::disabled()` decorator, or `BOOST_AUTO_TEST_CASE(name, *boost::unit_test::disabled())` |
      
      `DISABLED_` prefix without a tracking comment is a smell — flag as Ignored Test.
      
      ## Exception Handling — Idiomatic Alternatives
      
      ```cpp
      // GoogleTest:
      EXPECT_THROW({
          service.placeOrder(empty);
      }, InvalidOrderException);
      
      // Or capture and inspect:
      try {
          service.placeOrder(empty);
          FAIL() << "Expected InvalidOrderException";
      } catch (const InvalidOrderException& e) {
          EXPECT_STREQ("at least one item", e.what());
      }
      
      // Catch2:
      REQUIRE_THROWS_AS(service.placeOrder(empty), InvalidOrderException);
      REQUIRE_THROWS_WITH(service.placeOrder(empty), Catch::Contains("at least one item"));
      ```
      
      The manual try/catch/FAIL pattern is acceptable when message inspection is needed; flag bare `try { ... } catch (...) {}` (swallowed).
      
      ## Mystery Guest — Common C++ Patterns
      
      | Indicator | What to look for |
      |-----------|------------------|
      | File system | `std::ifstream`, `std::ofstream`, `fopen`, hard-coded paths |
      | Network | raw `socket()` / `connect()`, `curl_easy_perform` to real URL |
      | Environment | `std::getenv("X")`, Windows registry calls |
      | Database | direct `sqlite3_open(path)`, ODBC connections |
      | Acceptable | `std::stringstream`, `std::tmpfile`, GoogleMock for collaborators, `boost::iostreams`, in-memory streams |
      
      ## Integration Test Markers
      
      - File suffix: `*_integration_test.cc`, `*_e2e_test.cpp`
      - GoogleTest suite names containing `Integration` / `EndToEnd`
      - Catch2 tags: `[integration]`, `[e2e]`, `[slow]`
      - CMake target names ending in `_integration_tests`
      - Conditional compilation: `#ifdef BUILD_INTEGRATION_TESTS`
      
      ## Setup/Teardown
      
      | Framework | Per-test | Per-suite |
      |-----------|----------|-----------|
      | GoogleTest fixture | `void SetUp() override` | `static void SetUpTestSuite()` |
      | GoogleTest fixture | `void TearDown() override` | `static void TearDownTestSuite()` |
      | Catch2 | `TEST_CASE` body + `SECTION` re-runs setup per section | fixture class via `TEST_CASE_METHOD(Fixture, "name")` |
      | doctest | similar to Catch2 | `doctest::TestCase` fixture |
      | Boost.Test | `BOOST_FIXTURE_TEST_CASE(name, Fixture)` | `BOOST_GLOBAL_FIXTURE(Fixture)` |
      
      Catch2 `SECTION`s are re-entered for each combination, so the `TEST_CASE` body acts as fresh per-section setup — a powerful idiom.
      
      ## Tag/Trait Attributes (for `test-tagging`)
      
      | Framework | Tag mechanism | Example |
      |-----------|---------------|---------|
      | Catch2 | `[tag]` syntax in `TEST_CASE` second arg | `TEST_CASE("creates order", "[positive][critical-path]")` |
      | doctest | `* doctest::test_suite("tag")` decorator chain | `TEST_CASE("name" * doctest::test_suite("positive"))` |
      | GoogleTest | test name prefix convention (e.g., `Positive_*`, `Boundary_*`) or `--gtest_filter` patterns | suite naming or `TEST(PositiveCases, ...)`; **report-only** for auto-edit |
      | Boost.Test | label decorator: `* boost::unit_test::label("positive")` | `BOOST_AUTO_TEST_CASE(name, *boost::unit_test::label("positive"))` |
      
      Filter syntax:
      - Catch2: `./tests "[positive]" ~"[slow]"`
      - doctest: `./tests -ts="positive"`
      - GoogleTest: `./tests --gtest_filter='Positive*'`
      - Boost.Test: `./tests --run_test=@positive`
      
      ## Language-specific calibration notes
      
      - **`EXPECT_*` continues on failure** in GoogleTest — many `EXPECT_EQ` calls in one test may produce cascading messages from one root cause.
      - **`REQUIRE_*` / `ASSERT_*` aborts** — use for preconditions in long tests.
      - **Death tests** (`EXPECT_DEATH`) fork the process and check stderr — slow; acknowledge as integration-style.
      - **`DISABLED_` prefix** disables tests silently — `--gtest_also_run_disabled_tests` is required to opt back in. Flag committed `DISABLED_` tests as Ignored Test.
      - **Catch2 `SECTION`s** are NOT duplicate tests — each section is a permutation of the parent `TEST_CASE`.
      - **GoogleMock `EXPECT_CALL(mock, Method(...))`** counts as a state/side-effect assertion.
      - **Template / typed tests** (`TYPED_TEST`, `TEMPLATE_TEST_CASE`) are parametrized, not duplicates.
      - **Hidden tests** (Catch2 `[.]` or `[!hide]`) are excluded by default but runnable on demand — note in audit.
      - **Sanitizer-only tests** (`#ifdef __SANITIZE_THREAD__`, etc.) are conditional smoke checks — note but don't flag.
      - **Test binaries that don't link `gtest_main`** require a custom `main()` — verify it calls `RUN_ALL_TESTS()`.
      - **`SUCCEED()` / `INFO(...)`** are not assertions; tests with only `SUCCEED()` are assertion-free.
      
    • dotnet.md 6.7 KB
      # .NET Test Frameworks Reference (MSTest, xUnit, NUnit, TUnit)
      
      Reference data for analyzing .NET test code. Used by the polyglot test analysis skills (`assertion-quality`, `test-anti-patterns`, `test-gap-analysis`, `test-smell-detection`, `test-tagging`).
      
      ## Capability tags
      
      | Capability | Support |
      |------------|---------|
      | Test discovery | Strong — markers and conventions are well-defined |
      | Assertion detection | Strong — framework-specific APIs plus FluentAssertions/Shouldly/Verify |
      | Sleep/delay detection | Strong |
      | Skip/ignore detection | Strong |
      | Setup/teardown detection | Strong |
      | Tag support | **auto-edit** — `[TestCategory]`, `[Trait]`, `[Category]`, `[Property]` |
      
      ## Test File Identification
      
      | Framework | Test class markers | Test method markers |
      |-----------|-------------------|---------------------|
      | MSTest | `[TestClass]` | `[TestMethod]`, `[DataTestMethod]` |
      | xUnit | *(none — convention-based)* | `[Fact]`, `[Theory]` |
      | NUnit | `[TestFixture]` | `[Test]`, `[TestCase]`, `[TestCaseSource]` |
      | TUnit | *(none — convention-based)* | `[Test]` |
      
      ## Assertion APIs by Framework
      
      | Category | MSTest | xUnit | NUnit | TUnit |
      |----------|--------|-------|-------|-------|
      | Equality | `Assert.AreEqual` | `Assert.Equal` | `Assert.That(x, Is.EqualTo(y))` | `await Assert.That(x).IsEqualTo(y)` |
      | Boolean | `Assert.IsTrue` / `Assert.IsFalse` | `Assert.True` / `Assert.False` | `Assert.That(x, Is.True)` | `await Assert.That(x).IsTrue()` |
      | Null | `Assert.IsNull` / `Assert.IsNotNull` | `Assert.Null` / `Assert.NotNull` | `Assert.That(x, Is.Null)` | `await Assert.That(x).IsNull()` |
      | Exception | `Assert.Throws<T>()` / `Assert.ThrowsExactly<T>()` | `Assert.Throws<T>()` | `Assert.That(() => ..., Throws.TypeOf<T>())` | `await Assert.That(() => ...).Throws<T>()` |
      | Collection | `CollectionAssert.Contains` | `Assert.Contains` | `Assert.That(col, Has.Member(x))` | `await Assert.That(col).Contains(x)` |
      | String | `StringAssert.Contains` | `Assert.Contains(str, sub)` | `Assert.That(str, Does.Contain(sub))` | `await Assert.That(str).Contains(sub)` |
      | Type | `Assert.IsInstanceOfType` | `Assert.IsAssignableFrom` | `Assert.That(x, Is.InstanceOf<T>())` | `await Assert.That(x).IsAssignableTo<T>()` |
      | Inconclusive | `Assert.Inconclusive()` | `[Fact(Skip)]` | `Assert.Inconclusive()` | `Skip.Test("reason")` |
      | Fail | `Assert.Fail()` | `Assert.Fail()` (.NET 10+) | `Assert.Fail()` | `Assert.Fail()` |
      
      **TUnit-specific:** assertions are async and must be awaited — a forgotten `await` causes the assertion to never run and the test to pass silently. Multiple assertions chainable via `.And` / `.Or` or grouped via `Assert.Multiple()`.
      
      Third-party assertion libraries: `Should*` (Shouldly), `.Should()` (FluentAssertions / AwesomeAssertions), `Verify()` (Verify). TUnit also ships `TUnit.Assertions.Should`.
      
      ## Sleep/Delay Patterns
      
      | Pattern | Example |
      |---------|---------|
      | Thread sleep | `Thread.Sleep(2000)` |
      | Task delay | `await Task.Delay(1000)` |
      | SpinWait | `SpinWait.SpinUntil(() => condition, timeout)` |
      
      ## Skip/Ignore Annotations
      
      | Framework | Annotation | With reason |
      |-----------|------------|-------------|
      | MSTest | `[Ignore]` | `[Ignore("reason")]` |
      | xUnit | `[Fact(Skip = "reason")]` | *(reason required)* |
      | NUnit | `[Ignore("reason")]` | *(reason required)* |
      | TUnit | `[Skip("reason")]` | *(reason required; valid at class/assembly scope; dynamic via `Skip.Test("reason")`)* |
      | Conditional | `#if false` / `#if NEVER` | *(no reason)* |
      
      ## Exception Handling — Idiomatic Alternatives
      
      When a test uses `try`/`catch` to verify exceptions, prefer the framework-native form:
      
      ```csharp
      // MSTest (exact type):
      var ex = Assert.ThrowsExactly<InvalidOperationException>(() => sut.Do());
      Assert.AreEqual("expected message", ex.Message);
      
      // xUnit:
      var ex = Assert.Throws<InvalidOperationException>(() => sut.Do());
      
      // NUnit:
      var ex = Assert.Throws<InvalidOperationException>(() => sut.Do());
      
      // TUnit:
      await Assert.That(() => sut.Do()).Throws<InvalidOperationException>();
      ```
      
      ## Mystery Guest — Common .NET Patterns
      
      | Indicator | What to look for |
      |-----------|------------------|
      | File system | `File.ReadAllText`, `File.Exists`, `Directory.GetFiles`, `Path.Combine` with hard-coded paths |
      | Database | `SqlConnection`, `DbContext` (without in-memory provider), `SqlCommand` |
      | Network | `HttpClient` without `HttpMessageHandler` override, `WebRequest`, `TcpClient` |
      | Environment | `Environment.GetEnvironmentVariable`, `Environment.CurrentDirectory` |
      | Acceptable | `MemoryStream`, `StringReader`, in-memory database providers, custom `DelegatingHandler` |
      
      ## Integration Test Markers
      
      Recognize these as integration tests (adjust smell severity accordingly):
      
      - Class name contains `Integration`, `E2E`, `EndToEnd`, or `Acceptance`
      - `[TestCategory("Integration")]` (MSTest)
      - `[Trait("Category", "Integration")]` (xUnit)
      - `[Category("Integration")]` (NUnit, TUnit)
      - Project name ending in `.IntegrationTests` or `.E2ETests`
      
      ## Setup/Teardown Methods
      
      | Framework | Setup | Teardown |
      |-----------|-------|----------|
      | MSTest | `[TestInitialize]` or constructor | `[TestCleanup]` or `IDisposable.Dispose` |
      | xUnit | constructor | `IDisposable.Dispose` / `IAsyncDisposable.DisposeAsync` |
      | NUnit | `[SetUp]` | `[TearDown]` |
      | TUnit | `[Before(Test)]` or constructor | `[After(Test)]` or `IDisposable.Dispose` |
      | MSTest (class) | `[ClassInitialize]` | `[ClassCleanup]` |
      | NUnit (class) | `[OneTimeSetUp]` | `[OneTimeTearDown]` |
      | xUnit (class) | `IClassFixture<T>` | fixture's `Dispose` |
      | TUnit (class) | `[Before(Class)]` | `[After(Class)]` |
      
      ## Tag/Trait Attributes (for `test-tagging`)
      
      | Framework | Existing Attribute | Example |
      |-----------|--------------------|---------|
      | MSTest | `[TestCategory("...")]` | `[TestCategory("positive")]` |
      | xUnit | `[Trait("Category", "...")]` | `[Trait("Category", "positive")]` |
      | NUnit | `[Category("...")]` | `[Category("positive")]` |
      | TUnit | `[Category("...")]` or `[Property("Category", "...")]` | `[Category("positive")]` |
      
      Place trait attributes on the line directly above or below the existing test attribute. Multiple traits on the same test are allowed.
      
      ## Language-specific calibration notes
      
      - **Sealed test classes (MSTest 4)** that lock down class layout are intentional, not a smell.
      - **xUnit per-test instances** mean fields initialized in the constructor are reset between tests — General Fixture (over-broad setup) detection should still flag fields used by < 50% of tests.
      - **TUnit's `await` requirement** is itself a fertile source of assertion-free smells; flag any TUnit assertion line that lacks `await` as a critical anti-pattern.
      - **Data-driven tests** (`[DataRow]`, `[Theory]/[InlineData]`, `[TestCase]`, `[Arguments]`) are *not* duplicate tests; treat them as the consolidated form.
      
    • go.md 7.4 KB
      # Go Test Framework Reference (`testing` package, testify)
      
      Reference data for analyzing Go test code. Used by the polyglot test analysis skills.
      
      ## Capability tags
      
      | Capability | Support |
      |------------|---------|
      | Test discovery | Strong — `*_test.go`, `func TestXxx(t *testing.T)` |
      | Assertion detection | Moderate — bare `if … { t.Errorf(...) }` patterns; stronger with testify |
      | Sleep/delay detection | Strong — `time.Sleep`, `<-time.After` |
      | Skip/ignore detection | Strong — `t.Skip`, `t.SkipNow`, build tags |
      | Setup/teardown detection | Strong — `TestMain`, `t.Cleanup`, subtests |
      | Tag support | **report-only** by default — no canonical attribute; build tags can scope tests but are coarse |
      
      ## Test File Identification
      
      | Convention | Description |
      |------------|-------------|
      | `*_test.go` | Test files (must end with `_test.go`) |
      | `func TestXxx(t *testing.T)` | Standard tests |
      | `func BenchmarkXxx(b *testing.B)` | Benchmarks |
      | `func ExampleXxx()` | Documentation examples (act as tests when they have `// Output:` blocks) |
      | `func FuzzXxx(f *testing.F)` | Fuzz tests (Go 1.18+) |
      | `t.Run("subtest", func(t *testing.T) {...})` | Subtests / table-driven cases |
      
      Test packages may be `foo` (white-box) or `foo_test` (black-box). The latter only sees exported names.
      
      ## Assertion APIs
      
      Go's `testing` package has no built-in assertion library. Tests fail by calling `t.Error*` / `t.Fatal*`.
      
      | Category | Standard `testing` | testify (`require` / `assert`) |
      |----------|-------------------|--------------------------------|
      | Equality | `if got != want { t.Errorf("got %v, want %v", got, want) }` | `assert.Equal(t, want, got)` |
      | Boolean | `if !ok { t.Error("expected ok") }` | `assert.True(t, ok)` |
      | Nil | `if v != nil { t.Error(...) }` | `assert.Nil(t, v)` / `assert.NotNil(t, v)` |
      | Error | `if err != nil { t.Fatal(err) }` | `require.NoError(t, err)` / `assert.Error(t, err)` / `assert.ErrorIs(t, err, target)` |
      | Panic | `defer func() { if r := recover(); r == nil { t.Error("expected panic") } }()` | `assert.Panics(t, func() {...})` |
      | Type | `if _, ok := v.(T); !ok { t.Error(...) }` | `assert.IsType(t, T{}, v)` |
      | Membership | manual loop or `slices.Contains` | `assert.Contains(t, slice, item)` |
      | String | `if !strings.Contains(...) { t.Error(...) }` | `assert.Contains(t, s, sub)` |
      | Fail | `t.Fail()` / `t.FailNow()` / `t.Fatal(...)` / `t.Fatalf(...)` | `t.FailNow()` / `require.Fail(t, "...")` |
      
      **`require` vs `assert` (testify):** `require.*` calls `t.FailNow()` and stops the test; `assert.*` records the failure and continues. Tests that need preconditions before further work should use `require.NoError(t, err)`.
      
      **Bare `if ... { t.Error... }` is the canonical Go assertion form.** Do NOT flag these as missing-framework-API smells.
      
      Other libraries: `gotest.tools/v3` (`assert.Check`, `assert.Equal`), `go-cmp` (`cmp.Diff`).
      
      ## Sleep/Delay Patterns
      
      | Pattern | Example |
      |---------|---------|
      | Hard sleep | `time.Sleep(time.Second)` |
      | Timer wait | `<-time.After(time.Second)` |
      | Loop wait | `for !ready() { time.Sleep(10*time.Millisecond) }` |
      | Acceptable wait | `<-ctx.Done()` or `<-done` channels driven by the SUT |
      | Deadline | `ctx, cancel := context.WithTimeout(...)` |
      
      ## Skip/Ignore Annotations
      
      | Mechanism | Example |
      |-----------|---------|
      | `t.Skip("reason")` | Inline skip at any point in the test body |
      | `t.SkipNow()` | Skip without a message |
      | Build tag at top of file | `//go:build integration` (excludes file unless `-tags=integration`) |
      | `testing.Short()` guard | `if testing.Short() { t.Skip("skipping in short mode") }` |
      | `t.Skipf` | Formatted skip messages |
      
      There is no `@Disabled`-style permanent disable. Build tags and skip guards are the idiomatic way to gate tests.
      
      ## Exception Handling — Idiomatic Alternatives
      
      Go uses error returns and panics; there is no `try/catch`. Testing patterns:
      
      ```go
      // Error return:
      if _, err := svc.PlaceOrder(empty); err == nil {
          t.Error("expected error, got nil")
      }
      // Better with testify:
      _, err := svc.PlaceOrder(empty)
      require.Error(t, err)
      assert.Contains(t, err.Error(), "at least one item")
      
      // Error-target match (Go 1.13+):
      assert.ErrorIs(t, err, ErrEmptyOrder)
      assert.ErrorAs(t, err, &validationErr)
      
      // Panic:
      assert.PanicsWithValue(t, "bad input", func() { mustParse("xxx") })
      ```
      
      Flag tests that ignore returned errors (`_, _ = svc.Foo()`) without subsequent assertion.
      
      ## Mystery Guest — Common Go Patterns
      
      | Indicator | What to look for |
      |-----------|------------------|
      | File system | `os.ReadFile`, `os.Open`, hard-coded absolute paths |
      | Database | `sql.Open` against a real DB connection string, raw `pgx.Connect` |
      | Network | `http.Get`, `http.Post` to real URLs, raw `net.Dial` |
      | Environment | `os.Getenv("X")` (especially in test body without `t.Setenv`) |
      | Acceptable | `t.TempDir()`, `t.Setenv()`, `httptest.NewServer`, `sqlmock`, `dockertest` / `testcontainers-go` (integration-acknowledged), in-memory `bytes.Buffer` |
      
      ## Integration Test Markers
      
      - Build tag at file top: `//go:build integration` / `//go:build e2e` (run via `go test -tags=integration`)
      - File name suffix: `*_integration_test.go`, `*_e2e_test.go`
      - Package directory: `tests/integration/`, `internal/integrationtests/`
      - `testing.Short()` guard pattern: `if testing.Short() { t.Skip("integration test") }`
      
      ## Setup/Teardown
      
      | Mechanism | Description |
      |-----------|-------------|
      | `TestMain(m *testing.M)` | Package-level setup/teardown — runs `m.Run()` between setup and teardown |
      | `t.Cleanup(fn)` | Per-test cleanup that runs after the test (even on failure) |
      | Helper functions | `func setupFoo(t *testing.T) (*Foo, func())` returning a teardown closure |
      | Subtests with shared setup | `func TestX(t *testing.T) { foo := setup(t); t.Run("a", ...); t.Run("b", ...) }` |
      | testify suites | `type FooSuite struct{ suite.Suite }` with `SetupTest`, `TearDownTest`, `SetupSuite`, `TearDownSuite` |
      
      ## Tag/Trait Attributes (for `test-tagging`)
      
      **Default mode: report-only.** Go has no per-test tag attribute. Strategies:
      
      - **Build tags** scope an entire file (coarse): `//go:build integration`
      - **Subtest names** can encode tags: `t.Run("[positive] valid input returns ok", ...)`
      - **Test name prefixes**: `func TestNegative_InvalidInput_Returns400`
      - **testify suites** with grouping methods
      
      When the project already follows one of these conventions, switch to `auto-edit` mode and apply it consistently.
      
      ## Language-specific calibration notes
      
      - **Table-driven tests** with `for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ... }) }` are idiomatic — **do NOT flag the `for` loop as Conditional Test Logic.**
      - **Bare `if … t.Errorf` patterns** are the canonical assertion form. Do NOT flag as "no framework API used."
      - **Goroutine leaks in tests** are a real smell — recommend `goleak.VerifyNone(t)` or `t.Cleanup`.
      - **`t.Parallel()`** in tests: races on shared fixture data are a smell; tests calling `t.Setenv` then `t.Parallel` will fail in newer Go versions.
      - **`require` vs `assert` mixing**: subsequent code after a failed `assert.*` may panic on `nil`. Prefer `require.*` for preconditions.
      - **Examples with `// Output:`** are tests; treat the `// Output:` block as the assertion.
      - **Fuzz tests** without `f.Add(...)` seed inputs may only run with `-fuzz`; flag as a coverage gap.
      - **Generated mocks** (mockery, mockgen) — verify call expectations count as assertions.
      - **Missing `t.Helper()`** in helper functions is not a smell per se but degrades failure location reporting.
      
    • java.md 7.5 KB
      # Java Test Frameworks Reference (JUnit 4, JUnit 5 / Jupiter, TestNG)
      
      Reference data for analyzing Java test code. Used by the polyglot test analysis skills.
      
      ## Capability tags
      
      | Capability | Support |
      |------------|---------|
      | Test discovery | Strong — annotations + Maven Surefire / Gradle conventions |
      | Assertion detection | Strong — `Assertions.*`, `assertThat` (AssertJ/Hamcrest) |
      | Sleep/delay detection | Strong — `Thread.sleep`, `Awaitility`, `TimeUnit.sleep` |
      | Skip/ignore detection | Strong — `@Disabled`, `@Ignore`, `Assume.*` |
      | Setup/teardown detection | Strong — `@BeforeEach`, `@BeforeAll`, etc. |
      | Tag support | **auto-edit** — JUnit 5 `@Tag`, JUnit 4 `@Category`, TestNG `groups` |
      
      ## Test File Identification
      
      | Framework | File convention | Test method markers |
      |-----------|----------------|---------------------|
      | JUnit 4 | `*Test.java`, `*Tests.java`, `*IT.java` (integration) | `@Test`, classes typically `public` |
      | JUnit 5 (Jupiter) | same conventions | `@Test`, `@ParameterizedTest`, `@RepeatedTest`, `@TestFactory`, `@TestTemplate` |
      | TestNG | `*Test.java` | `@Test` (org.testng.annotations.Test) |
      
      ## Assertion APIs
      
      | Category | JUnit 4 (`Assert`) | JUnit 5 (`Assertions`) | TestNG (`Assert`) | AssertJ (`assertThat`) |
      |----------|--------------------|------------------------|-------------------|------------------------|
      | Equality | `assertEquals(expected, actual)` | `assertEquals(expected, actual)` | `assertEquals(actual, expected)` (note arg order!) | `assertThat(actual).isEqualTo(expected)` |
      | Boolean | `assertTrue(b)` / `assertFalse(b)` | `assertTrue(b)` / `assertFalse(b)` | `assertTrue(b)` | `assertThat(b).isTrue()` |
      | Null | `assertNull(x)` / `assertNotNull(x)` | `assertNull(x)` | `assertNull(x)` | `assertThat(x).isNull()` |
      | Exception | `@Test(expected = X.class)` / `try…catch` | `assertThrows(X.class, () -> {…})` | `assertThrows(X.class, () -> {…})` / `expectedExceptions = X.class` | `assertThatThrownBy(() -> {…}).isInstanceOf(X.class)` |
      | Type | `assertTrue(x instanceof T)` | `assertInstanceOf(T.class, x)` | `assertTrue(x instanceof T)` | `assertThat(x).isInstanceOf(T.class)` |
      | String | `assertEquals` then `contains` | `assertTrue(s.contains(sub))` | `assertEquals(s, expected)` | `assertThat(s).contains(sub).startsWith(...)` |
      | Collection | `assertEquals(list, expected)` | `assertIterableEquals(...)` | `assertEqualsNoOrder(actual, expected)` | `assertThat(col).containsExactly(...).hasSize(n)` |
      | Fail | `fail("reason")` | `fail("reason")` | `fail("reason")` | `Assertions.fail("reason")` |
      
      **TestNG quirk:** `Assert.assertEquals(actual, expected)` reverses the argument order vs JUnit. Misordered arguments are a common smell.
      
      Third-party libraries: AssertJ (`assertThat`), Hamcrest (`assertThat(x, is(y))`), Truth (Google), Mockito (`verify(mock).method(...)`).
      
      ## Sleep/Delay Patterns
      
      | Pattern | Example |
      |---------|---------|
      | Thread sleep | `Thread.sleep(2000)` |
      | TimeUnit sleep | `TimeUnit.SECONDS.sleep(2)` |
      | Awaitility (acceptable) | `await().atMost(5, SECONDS).until(() -> condition)` — replaces sleep with polling |
      | CompletableFuture timeouts | `future.get(5, TimeUnit.SECONDS)` |
      
      Flag raw `Thread.sleep` in tests as Sleepy Test. Awaitility-based waits are acceptable.
      
      ## Skip/Ignore Annotations
      
      | Framework | Annotation |
      |-----------|------------|
      | JUnit 4 | `@Ignore`, `@Ignore("reason")` |
      | JUnit 5 | `@Disabled`, `@Disabled("reason")`, `@DisabledOnOs`, `@EnabledIfSystemProperty`, `@EnabledIf(...)` |
      | JUnit 4/5 (dynamic) | `Assume.assumeTrue(cond)`, `Assumptions.assumeTrue(cond)` |
      | TestNG | `enabled = false` on `@Test`, `@Test(enabled = false)`, `throw new SkipException("reason")` |
      
      ## Exception Handling — Idiomatic Alternatives
      
      ```java
      // JUnit 5 (preferred):
      InvalidOrderException ex = assertThrows(
          InvalidOrderException.class,
          () -> service.placeOrder(emptyOrder));
      assertEquals("Order must contain at least one item", ex.getMessage());
      
      // AssertJ:
      assertThatThrownBy(() -> service.placeOrder(emptyOrder))
          .isInstanceOf(InvalidOrderException.class)
          .hasMessageContaining("at least one item");
      
      // TestNG:
      @Test(expectedExceptions = InvalidOrderException.class,
            expectedExceptionsMessageRegExp = ".*at least one item.*")
      public void placeOrder_empty_throws() { service.placeOrder(emptyOrder); }
      ```
      
      Flag legacy JUnit 4 `@Test(expected=...)` and bare `try/catch/fail` patterns as smells.
      
      ## Mystery Guest — Common Java Patterns
      
      | Indicator | What to look for |
      |-----------|------------------|
      | File system | `Files.readString`, `new File(...)`, hard-coded paths |
      | Database | `DriverManager.getConnection`, real Spring `@SpringBootTest(webEnvironment = RANDOM_PORT)` without `@MockBean`, `JdbcTemplate` against real DB |
      | Network | `HttpClient.send`, `RestTemplate.getForObject`, raw `Socket` |
      | Environment | `System.getenv`, `System.getProperty` (without test default) |
      | Acceptable | `@TempDir`, `MockWebServer` (OkHttp), `WireMock`, Testcontainers (acknowledged-integration), H2 in-memory, `@MockBean`, `MockMvc` |
      
      ## Integration Test Markers
      
      - File suffix: `*IT.java` (Failsafe convention), `*IntegrationTest.java`, `*E2ETest.java`
      - Annotations: `@SpringBootTest`, `@DataJpaTest`, `@Tag("integration")`, `@Category(IntegrationTests.class)` (JUnit 4)
      - TestNG: `@Test(groups = {"integration"})`
      - Use of Testcontainers, embedded Kafka/Mongo, or `@Sql` scripts
      
      ## Setup/Teardown
      
      | Framework | Per-test | Per-class |
      |-----------|----------|-----------|
      | JUnit 4 | `@Before` | `@BeforeClass` (static) |
      | JUnit 4 | `@After` | `@AfterClass` (static) |
      | JUnit 5 | `@BeforeEach` | `@BeforeAll` (static unless `@TestInstance(Lifecycle.PER_CLASS)`) |
      | JUnit 5 | `@AfterEach` | `@AfterAll` |
      | TestNG | `@BeforeMethod` | `@BeforeClass`, `@BeforeSuite`, `@BeforeGroups` |
      | TestNG | `@AfterMethod` | `@AfterClass`, `@AfterSuite`, `@AfterGroups` |
      
      ## Tag/Trait Attributes (for `test-tagging`)
      
      | Framework | Tag mechanism | Example |
      |-----------|---------------|---------|
      | JUnit 5 | `@Tag("name")` (stackable) | `@Tag("positive")`, `@Tag("boundary")` |
      | JUnit 4 | `@Category(NegativeTests.class)` (requires marker interfaces) | `@Category({NegativeTests.class, BoundaryTests.class})` |
      | TestNG | `@Test(groups = {"name"})` | `@Test(groups = {"positive", "critical-path"})` |
      
      For JUnit 4, marker interfaces must exist (e.g., `interface NegativeTests {}`). Suggest creating them rather than dropping `@Category` references with no target.
      
      For Maven Surefire, register groups in `pom.xml`:
      
      ```xml
      <configuration>
          <groups>positive,critical-path</groups>
      </configuration>
      ```
      
      ## Language-specific calibration notes
      
      - **Argument-order trap (TestNG):** `Assert.assertEquals(actual, expected)` reverses JUnit's order. Misordered comparisons produce backwards failure messages but still pass/fail correctly. Flag as smell when reviewing TestNG suites.
      - **JUnit 4 `@Test(expected=...)`** loses precise exception location and accepts subclasses; recommend migrating to `assertThrows`.
      - **`@SpringBootTest`** bootstraps the entire application — almost always an integration test.
      - **AssertJ chaining** is a single assertion conceptually; do not count each chained `.has...` as a separate assertion for assertion-count metrics.
      - **Mockito `verify(...)`** counts as a state/side-effect assertion when used to assert behavior — do not flag tests that only `verify` as assertion-free.
      - **Lombok `@SneakyThrows`** in tests is acceptable; do not flag.
      - **Parameterized tests** (`@ParameterizedTest` + `@MethodSource` / `@ValueSource`) are NOT duplicate tests; they are the consolidated form.
      
    • kotlin.md 7.2 KB
      # Kotlin Test Frameworks Reference (JUnit 5, Kotest, MockK)
      
      Reference data for analyzing Kotlin test code. Used by the polyglot test analysis skills.
      
      ## Capability tags
      
      | Capability | Support |
      |------------|---------|
      | Test discovery | Strong — JUnit 5 conventions, Kotest spec classes |
      | Assertion detection | Strong — JUnit + Kotest matchers + MockK verifications |
      | Sleep/delay detection | Strong — `Thread.sleep`, `delay()` |
      | Skip/ignore detection | Strong — `@Disabled`, `.config(enabled = false)` |
      | Setup/teardown detection | Strong — JUnit + Kotest lifecycle |
      | Tag support | **auto-edit** — JUnit 5 `@Tag`, Kotest `tags`, project-defined |
      
      ## Test File Identification
      
      | Framework | File convention | Test method markers |
      |-----------|----------------|---------------------|
      | JUnit 5 (Jupiter) | `*Test.kt`, `*Tests.kt`, `*IT.kt` | `@Test fun foo()` |
      | Kotest | `*Spec.kt` (any style) | inherits a spec class (`StringSpec`, `FunSpec`, `BehaviorSpec`, `ShouldSpec`, `DescribeSpec`, `FeatureSpec`, `WordSpec`, `FreeSpec`, `AnnotationSpec`) |
      | Spek | `*Spec.kt` | `object FooSpec : Spek({ ... })` |
      | TestNG | `*Test.kt` | `@Test fun foo()` (TestNG annotation) |
      
      ## Assertion APIs
      
      | Category | JUnit 5 (`Assertions`) | Kotest matchers | AssertK |
      |----------|------------------------|-----------------|---------|
      | Equality | `assertEquals(expected, actual)` | `actual shouldBe expected` | `assertThat(actual).isEqualTo(expected)` |
      | Boolean | `assertTrue(b)` / `assertFalse(b)` | `b.shouldBeTrue()` / `b.shouldBeFalse()` | `assertThat(b).isTrue()` |
      | Null | `assertNull(x)` / `assertNotNull(x)` | `x.shouldBeNull()` / `x.shouldNotBeNull()` | `assertThat(x).isNull()` |
      | Throws | `assertThrows<SomeException> { … }` | `shouldThrow<SomeException> { … }` | `assertFailure { … }.isInstanceOf(SomeException::class)` |
      | Type | `assertTrue(x is T)` | `x.shouldBeInstanceOf<T>()` | `assertThat(x).isInstanceOf(T::class)` |
      | String | `assertTrue(s.contains(sub))` | `s shouldContain sub` / `s shouldMatch Regex("...")` | `assertThat(s).contains(sub)` |
      | Collection | `assertIterableEquals(...)` | `col shouldContainExactly listOf(...)` | `assertThat(col).containsExactly(...)` |
      | Coroutine result | `runTest { ... }` block + assertEquals | `coroutineScope { ... } shouldBe expected` | within `runTest` |
      | Fail | `fail("reason")` | `fail("reason")` (Kotest) | `Assertions.fail("reason")` |
      
      MockK verifications: `verify(exactly = 1) { mock.method() }` — counts as a state/side-effect assertion.
      
      ## Sleep/Delay Patterns
      
      | Pattern | Example |
      |---------|---------|
      | Thread sleep | `Thread.sleep(2000)` |
      | Coroutine delay | `delay(1000)` inside `runBlocking { ... }` |
      | Acceptable (coroutine test) | `runTest { advanceTimeBy(1000) }` (virtual time, no real wait) |
      | Awaitility-style | `Awaitility.await().atMost(5, SECONDS).until { ... }` |
      
      Real `delay` inside `runBlocking { }` is a sleep smell; inside `runTest { }` it's virtual time and acceptable.
      
      ## Skip/Ignore Annotations
      
      | Framework | Annotation |
      |-----------|------------|
      | JUnit 5 | `@Disabled`, `@Disabled("reason")`, `@DisabledIf(...)`, `@EnabledIf(...)`, `@DisabledOnOs(OS.WINDOWS)` |
      | JUnit 5 (dynamic) | `Assumptions.assumeTrue(cond)` |
      | Kotest | `.config(enabled = false)`, `xtest("…")`, `xshould("…")`, `xdescribe("…")` |
      | Kotest (project-wide) | `EnabledCondition` / `EnabledIf` extensions |
      | TestNG | `@Test(enabled = false)`, `throw SkipException("reason")` |
      
      ## Exception Handling — Idiomatic Alternatives
      
      ```kotlin
      // JUnit 5:
      val ex = assertThrows<InvalidOrderException> {
          service.placeOrder(emptyOrder)
      }
      assertEquals("at least one item", ex.message)
      
      // Kotest:
      val ex = shouldThrow<InvalidOrderException> {
          service.placeOrder(emptyOrder)
      }
      ex.message shouldContain "at least one item"
      
      // AssertK:
      assertFailure { service.placeOrder(emptyOrder) }
          .isInstanceOf(InvalidOrderException::class)
          .messageContains("at least one item")
      ```
      
      Flag manual `try { ... fail() } catch (e: SomeException) { ... }` patterns.
      
      ## Mystery Guest — Common Kotlin/Android Patterns
      
      | Indicator | What to look for |
      |-----------|------------------|
      | File system | `File(path).readText()`, hard-coded paths |
      | Database | `Room.databaseBuilder(...)` without `inMemoryDatabaseBuilder`, real `Exposed` against file/server |
      | Network | `Retrofit.create<…>()` against a real base URL, `OkHttp` without `MockWebServer` |
      | Environment | `System.getenv("X")` |
      | Android | `Context.assets.open(...)`, file system writes to internal/external storage |
      | Acceptable | `MockWebServer`, `MockK`, `inMemoryDatabaseBuilder`, `@MockK`, Robolectric (acknowledged-integration), `TemporaryFolder` |
      
      ## Integration Test Markers
      
      - File suffix: `*IT.kt`, `*IntegrationTest.kt`, `*E2ETest.kt`
      - Annotations: `@SpringBootTest`, `@DataJpaTest`, `@Tag("integration")`
      - Kotest tags: `tag = listOf(IntegrationTag)`
      - Android: `androidTest/` source set is on-device/instrumented (integration); `test/` is JVM (unit)
      - Use of Testcontainers, embedded servers
      
      ## Setup/Teardown
      
      | Framework | Per-test | Per-class |
      |-----------|----------|-----------|
      | JUnit 5 | `@BeforeEach` | `@BeforeAll` (must be `@JvmStatic` in companion object unless `@TestInstance(PER_CLASS)`) |
      | JUnit 5 | `@AfterEach` | `@AfterAll` |
      | Kotest | `beforeTest { }` / `beforeEach { }` | `beforeSpec { }` |
      | Kotest | `afterTest { }` / `afterEach { }` | `afterSpec { }` |
      | TestNG | `@BeforeMethod` | `@BeforeClass`, `@BeforeSuite` |
      | Spek | `beforeEachTest { }` | `beforeGroup { }` |
      
      ## Tag/Trait Attributes (for `test-tagging`)
      
      | Framework | Tag mechanism | Example |
      |-----------|---------------|---------|
      | JUnit 5 | `@Tag("positive")` (stackable) | `@Tag("positive") @Tag("critical-path")` |
      | Kotest | per-test: `.config(tags = setOf(Positive))`; per-spec: `override fun tags() = setOf(Positive)` | tag objects: `object Positive : Tag()` |
      | TestNG | `@Test(groups = ["positive"])` | `@Test(groups = ["positive", "boundary"])` |
      
      For JUnit 5 in Gradle, register tag filters in `build.gradle.kts`:
      
      ```kotlin
      tasks.test {
          useJUnitPlatform {
              includeTags("positive")
              excludeTags("slow")
          }
      }
      ```
      
      ## Language-specific calibration notes
      
      - **Coroutine tests must use `runTest` / `runBlocking`** at the boundary; missing wrapper makes the test silently incomplete. Flag `suspend fun` test bodies without a coroutine scope.
      - **`runBlocking` vs `runTest`:** `runBlocking` waits in real time; `runTest` uses virtual time. Prefer `runTest` for testing time-dependent code.
      - **MockK `verify { }`** without `exactly = N` only checks at least once. Tests asserting exact behavior should set the count.
      - **Kotest's `forAll(...)` (data-driven)** is parametrized, NOT duplicate tests.
      - **`@OptIn(ExperimentalCoroutinesApi::class)`** is common in coroutine tests — not a smell.
      - **Android `@MediumTest` / `@LargeTest`** are size annotations from `androidx.test.filters`; treat as integration markers.
      - **Compose UI tests** (`createComposeRule`) are UI integration tests.
      - **Bare `assert(x)` in tests** is the Kotlin `kotlin.assert` — acceptable but recommend framework matchers for richer failure messages.
      - **`shouldBe` chained Kotest matchers** are single conceptual assertions; do not over-count chain length.
      
    • powershell.md 6.3 KB
      # PowerShell Test Framework Reference (Pester v5)
      
      Reference data for analyzing PowerShell test code. Used by the polyglot test analysis skills.
      
      ## Capability tags
      
      | Capability | Support |
      |------------|---------|
      | Test discovery | Strong — `*.Tests.ps1`, `Describe`/`Context`/`It` |
      | Assertion detection | Strong — `Should -Be*`, `-Throw`, `-HaveCount` |
      | Sleep/delay detection | Strong — `Start-Sleep` |
      | Skip/ignore detection | Strong — `-Skip`, `-Pending`, `Set-ItResult -Skipped` |
      | Setup/teardown detection | Strong — `BeforeEach`, `AfterAll`, etc. |
      | Tag support | **auto-edit** — `-Tag` parameter on `Describe`/`Context`/`It` |
      
      ## Test File Identification
      
      | Convention | Description |
      |------------|-------------|
      | `*.Tests.ps1` | Standard Pester test file convention |
      | `Describe '...' { ... }` | Top-level test group |
      | `Context '...' { ... }` | Sub-group |
      | `It 'should ...' { ... }` | Individual test case |
      | `InModuleScope ModuleName { ... }` | Access internal functions of a module |
      
      Pester v5+ uses block-scoped variables — `Describe`/`Context` blocks run during discovery; `BeforeAll` is required to initialize variables used by `It` blocks.
      
      ## Assertion APIs
      
      | Category | Pester v5 (`Should`) |
      |----------|----------------------|
      | Equality | `$x \| Should -Be $y` |
      | Strict equality | `$x \| Should -BeExactly $y` (case-sensitive for strings) |
      | Inequality | `$x \| Should -Not -Be $y` |
      | Boolean true/false | `$x \| Should -BeTrue` / `Should -BeFalse` |
      | Null | `$x \| Should -BeNullOrEmpty` |
      | Exception | `{ Get-Item missing } \| Should -Throw` / `Should -Throw -ExpectedMessage "*pattern*"` / `Should -Throw -ErrorId "ItemNotFound,..."` |
      | Type | `$x \| Should -BeOfType [int]` |
      | String contains | `$s \| Should -Match 'regex'` / `Should -BeLike 'wild*'` |
      | Collection | `$arr \| Should -Contain $item` / `Should -HaveCount 3` |
      | File exists | `'path' \| Should -Exist` |
      | Mocks | `Should -Invoke Get-Item -Times 1 -Exactly` / `Should -Invoke -ParameterFilter { $Path -eq '/x' }` |
      | Negation | `Should -Not -Be`, `Should -Not -Throw`, `Should -Not -BeNullOrEmpty` |
      
      `Should -Invoke` counts as a state/side-effect assertion — do not flag tests that only verify mock calls as assertion-free.
      
      ## Sleep/Delay Patterns
      
      | Pattern | Example |
      |---------|---------|
      | Sleep | `Start-Sleep -Seconds 5` |
      | Sleep ms | `Start-Sleep -Milliseconds 500` |
      | Wait-Job | `Wait-Job -Job $job -Timeout 10` (acceptable for legitimate job waits) |
      | Loop wait | `while (-not (Test-Ready)) { Start-Sleep -Seconds 1 }` |
      
      ## Skip/Ignore Annotations
      
      | Mechanism | Example |
      |-----------|---------|
      | `-Skip` | `It 'does x' -Skip { ... }` |
      | `-Pending` | `It 'does x' -Pending { ... }` (legacy v4; in v5, prefer `-Skip`) |
      | `Set-ItResult -Skipped -Because '<reason>'` | Inline skip from within an `It` body |
      | Conditional skip | `It 'is windows-only' -Skip:(-not $IsWindows) { ... }` |
      | `-Skip` on `Describe`/`Context` | skips all contained tests |
      
      ## Exception Handling — Idiomatic Alternatives
      
      ```powershell
      # Preferred: Should -Throw with scriptblock
      { Get-Item -Path 'C:\nonexistent' -ErrorAction Stop } |
          Should -Throw -ErrorId 'PathNotFound,Microsoft.PowerShell.Commands.GetItemCommand'
      
      # With pattern match on message:
      { Invoke-MyFunc -InvalidArg } | Should -Throw -ExpectedMessage '*invalid*'
      
      # Not throwing:
      { Invoke-MyFunc -ValidArg } | Should -Not -Throw
      ```
      
      Flag tests using `try { ... } catch { Write-Error ... }` patterns without subsequent `Should` assertion.
      
      ## Mystery Guest — Common PowerShell Patterns
      
      | Indicator | What to look for |
      |-----------|------------------|
      | File system | `Get-Content 'C:\hard\coded\path'`, `Test-Path` against real paths, `New-Item` without `TestDrive:` |
      | Registry | `Get-ItemProperty 'HKLM:\...'`, `Set-ItemProperty` against real registry |
      | Network | `Invoke-WebRequest`, `Invoke-RestMethod` against real URLs |
      | Environment | `$env:USERNAME`, `$env:COMPUTERNAME` (without mock or fallback) |
      | Acceptable | `TestDrive:` (Pester-provided per-test temp dir), `Mock` cmdlet, hashtables as fake config |
      
      ## Integration Test Markers
      
      - File suffix: `*.Integration.Tests.ps1`, `*.E2E.Tests.ps1`
      - `-Tag 'Integration'` / `-Tag 'E2E'`
      - Folder convention: `tests/integration/`, `tests/e2e/`
      - Real Azure/AWS module calls (`Connect-AzAccount`, `Get-S3Object`) imply integration
      
      ## Setup/Teardown
      
      | Scope | Setup | Teardown |
      |-------|-------|----------|
      | Per-test | `BeforeEach { }` | `AfterEach { }` |
      | Per-block (Describe/Context) | `BeforeAll { }` | `AfterAll { }` |
      
      Pester v5 requires `BeforeAll` to initialize variables used in `It` blocks (discovery vs run separation). A common mistake: defining variables at `Describe` scope and using them inside `It` — they will be `$null` at run time.
      
      ## Tag/Trait Attributes (for `test-tagging`)
      
      | Mechanism | Example |
      |-----------|---------|
      | `-Tag` on `It` | `It 'creates order' -Tag 'positive','critical-path' { ... }` |
      | `-Tag` on `Context` | inherits to contained `It`s |
      | `-Tag` on `Describe` | inherits to all nested blocks |
      | `Invoke-Pester -Tag 'positive' -ExcludeTag 'slow'` | filter by tag |
      
      ## Language-specific calibration notes
      
      - **Pester v5 vs v4 scoping differences**: v4 tests using `$script:` variables shared between `It` blocks won't work in v5 the same way. Note as migration debt if both styles coexist.
      - **`InModuleScope`** is the canonical way to test internal/non-exported module functions — not an implementation-coupling smell.
      - **`Mock` cmdlet** intercepts ANY function in scope; tests that mock built-in cmdlets (`Get-ChildItem`, etc.) without `ParameterFilter` are over-broad — flag as smell.
      - **`TestDrive:`** is an automatically-created temporary directory unique to each test — not a Mystery Guest.
      - **Pester `Should -Invoke` (v5) / `Assert-MockCalled` (v4)** are state/side-effect assertions.
      - **`Set-StrictMode -Version Latest`** in tests is a hygiene practice — acknowledge as positive.
      - **`Set-ItResult -Inconclusive`** marks a test as inconclusive (not failure, not skip).
      - **`-ForEach` / `-TestCases`** are parametrized — NOT duplicate tests.
      - **PSScriptAnalyzer integration**: tests that lint themselves (`Invoke-ScriptAnalyzer`) are quality-gate tests, not analyzer code.
      - **Pester v6 (preview)** changes some APIs; if the project targets v6, double-check assertion forms.
      
    • python.md 6.8 KB
      # Python Test Frameworks Reference (pytest, unittest)
      
      Reference data for analyzing Python test code. Used by the polyglot test analysis skills.
      
      ## Capability tags
      
      | Capability | Support |
      |------------|---------|
      | Test discovery | Strong — convention-driven (`test_*.py`, `*_test.py`, `Test*` classes) |
      | Assertion detection | Strong — bare `assert`, `unittest` methods, `pytest.raises` |
      | Sleep/delay detection | Strong — `time.sleep`, `asyncio.sleep` |
      | Skip/ignore detection | Strong — `@pytest.mark.skip`, `unittest.skip` |
      | Setup/teardown detection | Strong — fixtures and methods |
      | Tag support | **auto-edit** — `@pytest.mark.<tag>` (pytest), no canonical syntax in unittest |
      
      ## Test File Identification
      
      | Framework | Test file convention | Test method markers |
      |-----------|---------------------|---------------------|
      | pytest | `test_*.py` or `*_test.py` | functions starting with `test_`; classes starting with `Test` (no `__init__`) and methods starting with `test_` |
      | unittest | any module (often `test_*.py`) | classes inheriting `unittest.TestCase` with methods starting with `test` |
      
      ## Assertion APIs
      
      | Category | pytest | unittest |
      |----------|--------|----------|
      | Equality | `assert x == y` | `self.assertEqual(x, y)` |
      | Inequality | `assert x != y` | `self.assertNotEqual(x, y)` |
      | Boolean | `assert flag` / `assert not flag` | `self.assertTrue(flag)` / `self.assertFalse(flag)` |
      | None | `assert x is None` | `self.assertIsNone(x)` / `self.assertIsNotNone(x)` |
      | Exception | `with pytest.raises(SomeError) as exc_info: ...` | `with self.assertRaises(SomeError): ...` |
      | Type | `assert isinstance(x, T)` | `self.assertIsInstance(x, T)` |
      | Identity | `assert x is y` | `self.assertIs(x, y)` |
      | Membership | `assert item in collection` | `self.assertIn(item, collection)` |
      | Approximate | `assert x == pytest.approx(y, rel=0.01)` | `self.assertAlmostEqual(x, y, places=2)` |
      | String | `assert sub in s` / `assert s.startswith(...)` | `self.assertIn(sub, s)` |
      | Skip | `pytest.skip("reason")` | `self.skipTest("reason")` |
      | Fail | `pytest.fail("reason")` | `self.fail("reason")` |
      
      **Important:** Bare `assert` is the canonical pytest assertion and produces rich failure diffs via pytest's assertion rewriting. Do NOT flag bare `assert` as a missing-framework-API smell.
      
      Third-party assertion libraries: `assertpy`, `hamcrest` (`assert_that`), `expects`.
      
      ## Sleep/Delay Patterns
      
      | Pattern | Example |
      |---------|---------|
      | Sync sleep | `time.sleep(2)` |
      | Async sleep | `await asyncio.sleep(1)` |
      | Loop wait | `while not condition: time.sleep(0.1)` |
      | Trio/anyio | `await trio.sleep(...)`, `await anyio.sleep(...)` |
      
      ## Skip/Ignore Annotations
      
      | Framework | Annotation |
      |-----------|------------|
      | pytest | `@pytest.mark.skip(reason="...")`, `@pytest.mark.skipif(cond, reason="...")`, `@pytest.mark.xfail(reason="...")`, `pytest.skip("...")` inline |
      | unittest | `@unittest.skip("reason")`, `@unittest.skipIf(cond, "reason")`, `@unittest.skipUnless(cond, "reason")`, `@unittest.expectedFailure` |
      
      ## Exception Handling — Idiomatic Alternatives
      
      ```python
      # pytest (preferred):
      with pytest.raises(ValueError, match=r"must be positive"):
          parse_amount(-5)
      
      # unittest:
      with self.assertRaises(ValueError):
          parse_amount(-5)
      
      # To inspect the exception:
      with pytest.raises(ValueError) as exc_info:
          parse_amount(-5)
      assert "must be positive" in str(exc_info.value)
      ```
      
      Flag bare `try/except` in tests as Exception Handling smell only when no assertion follows or the exception is silently swallowed.
      
      ## Mystery Guest — Common Python Patterns
      
      | Indicator | What to look for |
      |-----------|------------------|
      | File system | `open()`, `pathlib.Path(...).read_text()`, `os.path.exists`, hard-coded absolute paths |
      | Database | direct `psycopg2`/`mysql.connector`/`sqlite3.connect` to a file path, `SQLAlchemy` engine pointing at a real DB URL |
      | Network | `requests.get/post`, `httpx.get/post`, `urllib.request.urlopen`, raw `socket` |
      | Environment | `os.getenv("X")` (especially without default), `os.environ["X"]` |
      | Acceptable | `io.StringIO` / `io.BytesIO`, `tmp_path` / `tmp_path_factory` pytest fixtures, `monkeypatch.setenv`, `responses` / `httpx.MockTransport`, `pytest-mock`, sqlite `:memory:` |
      
      ## Integration Test Markers
      
      - Folder names: `tests/integration/`, `tests/e2e/`, `tests/acceptance/`
      - Module/class/function names containing `Integration`, `E2E`, `EndToEnd`, `Acceptance`
      - `@pytest.mark.integration` / `@pytest.mark.e2e` (project-specific markers registered in `pytest.ini` / `pyproject.toml`)
      - Conftest fixtures that spin up containers / databases (`testcontainers`, `docker-compose` fixtures)
      
      ## Setup/Teardown
      
      | Framework | Setup | Teardown |
      |-----------|-------|----------|
      | pytest | `@pytest.fixture` (any scope), `autouse=True` fixtures | yield-based teardown inside fixture or `request.addfinalizer` |
      | pytest (class) | `setup_method` / `setup_class` | `teardown_method` / `teardown_class` |
      | unittest | `setUp` / `setUpClass` / `setUpModule` | `tearDown` / `tearDownClass` / `tearDownModule` |
      
      ## Tag/Trait Attributes (for `test-tagging`)
      
      | Framework | Tag mechanism | Example |
      |-----------|---------------|---------|
      | pytest | `@pytest.mark.<name>` (project-registered) | `@pytest.mark.positive`, `@pytest.mark.boundary` |
      | unittest | none built-in — use class organization, attributes, or `unittest.skipIf` toggles | *(report-only; recommend pytest markers or a project convention)* |
      
      For pytest, ensure the markers are registered in `pyproject.toml` / `pytest.ini` to avoid `PytestUnknownMarkWarning`:
      
      ```toml
      [tool.pytest.ini_options]
      markers = [
          "positive: verifies expected behavior under normal conditions",
          "negative: verifies handling of invalid input or error paths",
          "boundary: tests limits, thresholds, empty/null inputs",
      ]
      ```
      
      ## Language-specific calibration notes
      
      - **Bare `assert`** is the pytest idiom — do not flag it as assertion-free.
      - **Snapshot tests** (`syrupy`, `pytest-snapshot`) replace the `assert` call with an implicit snapshot compare; treat as a legitimate assertion.
      - **Property-based tests** (`hypothesis`): a `@given(...)`-decorated function is a real test even if it appears to have no body — the assertions live in the generated input cycles.
      - **Async tests** (`pytest-asyncio`, `anyio`): missing `await` on a coroutine call inside the test produces a `RuntimeWarning` and an effectively assertion-free test. Flag as a critical anti-pattern.
      - **Doctests** invoked via `--doctest-modules` are tests too; treat `>>>` blocks as test methods if the user includes them in scope.
      - **Parametrized tests** (`@pytest.mark.parametrize`) are *not* duplicates of the underlying function — treat them as the consolidated form.
      - **Fixtures used by only one test** are not General Fixture smells; pytest fixtures are pay-as-you-go (a fixture only runs when a test requests it).
      
    • ruby.md 6.6 KB
      # Ruby Test Frameworks Reference (RSpec, Minitest)
      
      Reference data for analyzing Ruby test code. Used by the polyglot test analysis skills.
      
      ## Capability tags
      
      | Capability | Support |
      |------------|---------|
      | Test discovery | Strong — `spec/**/*_spec.rb`, `test/**/*_test.rb` |
      | Assertion detection | Strong — `expect`, `assert_*` |
      | Sleep/delay detection | Strong — `sleep`, `Kernel#sleep` |
      | Skip/ignore detection | Strong — `skip`, `pending`, `xit` |
      | Setup/teardown detection | Strong — `before`, `setup` |
      | Tag support | **auto-edit** — RSpec metadata, Minitest `tag` (via gems) |
      
      ## Test File Identification
      
      | Framework | File convention | Test method markers |
      |-----------|----------------|---------------------|
      | RSpec | `spec/**/*_spec.rb` | `describe`, `context`, `it`, `specify`, `example` |
      | Minitest | `test/**/*_test.rb` | `Minitest::Test` subclass with methods starting `test_`, or `Minitest::Spec` with `it` |
      
      ## Assertion APIs
      
      | Category | RSpec (`expect`) | Minitest (`assert_*`) |
      |----------|------------------|-----------------------|
      | Equality | `expect(x).to eq(y)` / `eql(y)` | `assert_equal expected, actual` |
      | Identity | `expect(x).to be(y)` | `assert_same expected, actual` |
      | Boolean | `expect(x).to be_truthy` / `be_falsey` | `assert x` / `refute x` |
      | Nil | `expect(x).to be_nil` | `assert_nil x` / `refute_nil x` |
      | Exception | `expect { fn }.to raise_error(SomeError, /msg/)` | `assert_raises(SomeError) { fn }` |
      | Type | `expect(x).to be_a(T)` / `be_instance_of(T)` | `assert_kind_of T, x` / `assert_instance_of T, x` |
      | Membership | `expect(arr).to include(item)` | `assert_includes arr, item` |
      | String | `expect(s).to match(/regex/)` | `assert_match(/regex/, s)` |
      | Predicate | `expect(x).to be_empty` (auto: `x.empty?`) | `assert_empty x` |
      | Change | `expect { code }.to change(obj, :attr).from(x).to(y)` | manual before/after assertion |
      | Throw | `expect { throw :sym }.to throw_symbol(:sym)` | `assert_throws(:sym) { ... }` |
      | Output | `expect { puts "x" }.to output("x\n").to_stdout` | `assert_output("x\n") { puts "x" }` |
      | Fail | `fail("reason")` (built-in) | `flunk "reason"` |
      
      Third-party libraries: Shoulda Matchers, FactoryBot (for setup, not assertions), Capybara (`have_content`, `have_selector`).
      
      ## Sleep/Delay Patterns
      
      | Pattern | Example |
      |---------|---------|
      | Sleep | `sleep 1` / `sleep(0.5)` |
      | Capybara explicit wait (acceptable) | `using_wait_time(5) { find('#x') }` |
      | Loop wait | `until condition; sleep 0.1; end` |
      | Timecop / ActiveSupport::Testing::TimeHelpers (acceptable) | `travel_to(1.hour.from_now)` instead of real sleep |
      
      ## Skip/Ignore Annotations
      
      | Framework | Skip |
      |-----------|------|
      | RSpec | `skip("reason")`, `xit`, `xdescribe`, `xcontext`, `pending("reason")`, `it("...", :skip)`, `it("...", skip: "reason")`, focused `fit`, `fdescribe`, `fcontext` |
      | Minitest | `skip("reason")` inside a test method, `skip_until "<date>", "reason"` (via `minitest-skip-until` gem) |
      
      `fit` / `fdescribe` (focused) committed to source is anti-pattern when `--fail-if-no-examples` / RSpec `--only-failures` isn't gating it.
      
      ## Exception Handling — Idiomatic Alternatives
      
      ```ruby
      # RSpec (preferred):
      expect { service.place_order(empty_order) }
        .to raise_error(InvalidOrderError, /at least one item/)
      
      # Minitest:
      err = assert_raises(InvalidOrderError) { service.place_order(empty_order) }
      assert_match(/at least one item/, err.message)
      ```
      
      Flag tests with bare `begin/rescue` that swallow exceptions or `rescue => e` patterns without subsequent assertion.
      
      ## Mystery Guest — Common Ruby/Rails Patterns
      
      | Indicator | What to look for |
      |-----------|------------------|
      | File system | `File.read`, `File.open`, `Pathname#read`, hard-coded paths |
      | Database | direct `ActiveRecord::Base.connection.execute`, real DB writes outside transactional fixtures |
      | Network | `Net::HTTP`, `URI.open`, `RestClient`, `Faraday` against real URLs |
      | Environment | `ENV["X"]` (especially without `ENV.fetch("X", default)`) |
      | Acceptable | `WebMock`, `VCR`, `Tempfile`, `StringIO`, `ActiveRecord` transactional fixtures, `database_cleaner`, factory builders |
      
      ## Integration Test Markers
      
      - Folder convention: `spec/system/`, `spec/features/`, `spec/integration/`, `test/integration/`, `test/system/`
      - RSpec metadata: `it "...", type: :system`, `:feature`, `:request`, `:integration`
      - Rails: `ActionDispatch::IntegrationTest` subclass, `ActionDispatch::SystemTestCase`
      - Capybara involvement implies system/feature test
      
      ## Setup/Teardown
      
      | Framework | Per-test | Per-suite |
      |-----------|----------|-----------|
      | RSpec | `before(:each)` / `before { ... }` | `before(:all)` / `before(:context)` |
      | RSpec | `after(:each)` | `after(:all)` |
      | RSpec | `around { |ex| ex.run }` (wrapping) | n/a |
      | Minitest | `setup` method | `before_all` (via `minitest-hooks` gem) |
      | Minitest | `teardown` method | `after_all` (via gem) |
      | Rails | `ActiveSupport::TestCase` `setup` / `teardown` blocks | `setup do ... end` |
      
      ## Tag/Trait Attributes (for `test-tagging`)
      
      | Framework | Tag mechanism | Example |
      |-----------|---------------|---------|
      | RSpec | metadata hash | `it "creates order", :positive, :critical_path do ... end` |
      | RSpec | metadata key/value | `describe Order, type: :model, tag: :positive do ... end` |
      | Minitest | `tag` via `minitest-tagz` / `minitest-tagged` gems | varies by gem |
      | Rails | `test_tagged` helper (Rails 7.1+) | `test "x", tag: :positive do ... end` |
      
      RSpec filters can drive tag selection: `rspec --tag positive`, `rspec --tag ~slow`.
      
      ## Language-specific calibration notes
      
      - **Predicate matchers** (`be_empty`, `be_valid`) auto-derive from `?` methods on the object. Treat as state/side-effect assertions.
      - **`change` matcher** is a state assertion: `expect { code }.to change(obj, :attr)` verifies side effects. Do not treat as missing assertion.
      - **Shared examples** (`it_behaves_like "...")` and shared contexts are NOT duplicate tests — they are the consolidated form.
      - **`let` / `let!`** for fixtures: `let!` runs eagerly per test, `let` lazily. Tests that create heavy `let!` blocks for fields used by only one test are General Fixture smells.
      - **Implicit subject** (`subject { described_class.new(args) }`, `it { is_expected.to be_valid }`) is a valid concise form.
      - **FactoryBot `build` vs `create`**: `create` hits the database, `build` does not. Tests that `create` records for assertions that don't need persistence inflate test time — note but don't flag as critical.
      - **Capybara `find` without an explicit selector** can be slow/flaky; recommend more specific selectors.
      - **RSpec `pending` differs from `skip`**: `pending` runs the test and expects failure; `skip` does not run it.
      
    • rust.md 7.2 KB
      # Rust Test Framework Reference (built-in `#[test]`, `cargo test`)
      
      Reference data for analyzing Rust test code. Used by the polyglot test analysis skills.
      
      ## Capability tags
      
      | Capability | Support |
      |------------|---------|
      | Test discovery | Strong — `#[test]` / `#[tokio::test]` / `#[cfg(test)] mod tests` / `tests/` integration directory |
      | Assertion detection | Strong — `assert!`, `assert_eq!`, `assert_ne!`, `?` on `Result` tests |
      | Sleep/delay detection | Strong — `thread::sleep`, `tokio::time::sleep` |
      | Skip/ignore detection | Strong — `#[ignore]`, `#[cfg(...)]` gating |
      | Setup/teardown detection | Moderate — no built-in fixtures; uses constructors and `Drop`, or external crates |
      | Tag support | **report-only / convention-based** — no canonical attribute; some crates (`rstest`, `nextest`) support test filters by name |
      
      ## Test File Identification
      
      | Convention | Description |
      |------------|-------------|
      | `#[test]` | Standard test attribute |
      | `#[cfg(test)] mod tests { ... }` | Unit tests co-located with source |
      | `tests/*.rs` | Integration tests (each file is a separate crate) |
      | `#[tokio::test]` / `#[async_std::test]` | Async tests (need async runtime crate) |
      | `#[rstest]` | Parametric tests via the `rstest` crate |
      | `#[should_panic]` | Tests that expect a panic |
      | Doc tests | `///` comments containing executable code blocks |
      | `#[bench]` (nightly) / `criterion` benchmarks | Benchmarks |
      
      ## Assertion APIs
      
      | Category | Built-in | proptest / quickcheck |
      |----------|----------|-----------------------|
      | Equality | `assert_eq!(actual, expected)` | (manual `prop_assert_eq!`) |
      | Inequality | `assert_ne!(actual, expected)` | `prop_assert_ne!` |
      | Boolean | `assert!(condition, "msg")` | `prop_assert!(...)` |
      | Pattern match | `assert!(matches!(value, Pattern))` | n/a |
      | Panic | `#[should_panic]` / `#[should_panic(expected = "msg")]` | n/a |
      | Error | `result.unwrap()` (panics on error) / `?` propagation | n/a |
      | Fail | `panic!("reason")` / `unreachable!()` | n/a |
      
      Third-party libraries: `pretty_assertions` (`assert_eq!` with colored diffs), `assert_matches`, `claim` (`assert_ok!`, `assert_err!`).
      
      **Result-returning tests** (Rust 2018+):
      ```rust
      #[test]
      fn parses_valid_input() -> Result<(), Box<dyn std::error::Error>> {
          let v = parse("1")?;
          assert_eq!(v, 1);
          Ok(())
      }
      ```
      
      ## Sleep/Delay Patterns
      
      | Pattern | Example |
      |---------|---------|
      | Sync sleep | `std::thread::sleep(Duration::from_secs(1))` |
      | Async sleep (tokio) | `tokio::time::sleep(Duration::from_secs(1)).await` |
      | async-std sleep | `async_std::task::sleep(Duration::from_secs(1)).await` |
      | Spin wait | `while !cond() { std::thread::sleep(...) }` |
      | Acceptable (tokio time control) | `tokio::time::pause()` + `tokio::time::advance(...)` |
      
      ## Skip/Ignore Annotations
      
      | Mechanism | Example |
      |-----------|---------|
      | `#[ignore]` | Excluded by default; run with `cargo test -- --ignored` |
      | `#[ignore = "reason"]` | With reason (Rust 1.55+) |
      | `#[cfg(feature = "x")]` | Skip unless feature enabled |
      | `#[cfg(target_os = "linux")]` | Skip on other OS |
      | `#[cfg(not(miri))]` | Skip under Miri interpreter |
      | Conditional skip | manual `if !cfg!(...) { return; }` (anti-pattern) |
      
      ## Exception Handling — Idiomatic Alternatives
      
      ```rust
      // should_panic with specific message:
      #[test]
      #[should_panic(expected = "must be positive")]
      fn parses_negative_panics() {
          parse_amount(-5);
      }
      
      // Result return + ?:
      #[test]
      fn places_order_ok() -> anyhow::Result<()> {
          let order = service.place_order(valid_order())?;
          assert_eq!(order.id, 42);
          Ok(())
      }
      
      // Match on Err for specific variant:
      let err = service.place_order(empty).unwrap_err();
      assert!(matches!(err, OrderError::Empty));
      ```
      
      Flag tests that use `.unwrap()` on `Result` returns from production code without asserting the error variant — they conflate "unexpected error" with test failure.
      
      ## Mystery Guest — Common Rust Patterns
      
      | Indicator | What to look for |
      |-----------|------------------|
      | File system | `std::fs::read`, `std::fs::write`, hard-coded paths |
      | Database | `sqlx::PgPool::connect` against real DB, `rusqlite::Connection::open(path)` |
      | Network | `reqwest::get`, `hyper` client to real URLs, raw `TcpStream::connect` |
      | Environment | `std::env::var("X").unwrap()` |
      | Acceptable | `tempfile::TempDir`, `httpmock`, `wiremock-rs`, `mockito`, `sqlx` test pool against in-memory SQLite, `tokio::test` with `start_paused = true` |
      
      ## Integration Test Markers
      
      - `tests/` top-level directory contains integration tests
      - Test names containing `_integration_`, `_e2e_`, `_acceptance_`
      - Feature flags: `#[cfg(feature = "integration-tests")]`
      - Crates like `testcontainers` imply integration
      - `cargo nextest` profile names (`[profile.integration]`)
      
      ## Setup/Teardown
      
      Rust has no native fixture framework. Common patterns:
      
      | Pattern | Description |
      |---------|-------------|
      | Helper function | `fn setup() -> Foo { ... }` invoked at the start of each test |
      | `Drop` implementation | Side-effect cleanup on test-local guard structs |
      | `rstest` fixtures | `#[fixture] fn db() -> Db { ... }` + `#[rstest] fn t(db: Db) { ... }` |
      | `test-context` crate | Per-test `setup` / `teardown` traits |
      | `serial_test` crate | Avoid parallel test interference with `#[serial]` |
      | `once_cell` / `lazy_static` | Lazy global init (use cautiously — shared state across tests) |
      
      ## Tag/Trait Attributes (for `test-tagging`)
      
      **Default mode: report-only / convention-based.** Rust has no canonical per-test tag attribute. Strategies:
      
      - **Module grouping**: `mod positive { ... }`, `mod boundary { ... }` — works with `cargo test boundary::`
      - **Test name prefixes**: `fn test_negative_invalid_input_returns_error()` — filterable via `cargo test negative_`
      - **Feature flags** for integration/E2E: `#[cfg(feature = "e2e")]`
      - **`cargo nextest`** supports test groups via `nextest.toml` filtering expressions
      
      Only switch to `auto-edit` mode when the project already follows one convention.
      
      ## Language-specific calibration notes
      
      - **Doc tests** are real tests — `cargo test` runs them. Treat as tests if user includes lib doc comments in scope.
      - **`#[should_panic]` without `expected = "..."`** passes on ANY panic — that's a smell (overly broad).
      - **`.unwrap()` and `.expect()` in tests** are acceptable for type-correct unwrapping but obscure error sources. Recommend `?` on `Result`-returning tests where possible.
      - **Property-based tests** (`proptest!`, `quickcheck!`) generate input cases; treat as parametrized tests, not duplicates.
      - **`#[ignore]` without a reason** is a smell — flag as Ignored Test with low severity.
      - **Async tests requiring `#[tokio::test]` but missing it** silently never run. Flag any `async fn` test missing the runtime attribute.
      - **`thread::sleep` in tests** is a Sleepy Test; prefer `tokio::time::pause()` for async or explicit polling for sync.
      - **Tests that mutate `static mut` or global `Mutex<...>` state** require `#[serial]` (from `serial_test`) — otherwise flaky under parallel `cargo test`.
      - **`#[cfg(test)]` modules cross-compiled with `#![deny(warnings)]`** sometimes fail builds — note but don't flag as smell.
      - **Bare `assert!(x)` with no message** in `assert_eq!`-suitable positions is acceptable; do not require messages.
      
    • swift.md 6.8 KB
      # Swift Test Frameworks Reference (XCTest, Swift Testing)
      
      Reference data for analyzing Swift test code. Used by the polyglot test analysis skills.
      
      ## Capability tags
      
      | Capability | Support |
      |------------|---------|
      | Test discovery | Strong — `XCTestCase` subclasses, `@Test` functions |
      | Assertion detection | Strong — `XCTAssert*`, `#expect`, `#require` |
      | Sleep/delay detection | Strong — `Thread.sleep`, `Task.sleep`, `XCTWaiter` |
      | Skip/ignore detection | Strong — `XCTSkip`, `.disabled(...)` |
      | Setup/teardown detection | Strong — `setUp/tearDown`, `init/deinit` for Swift Testing |
      | Tag support | **auto-edit** (Swift Testing) — `@Test(.tags(...))` / `@Suite(.tags(...))`; XCTest: report-only |
      
      ## Test File Identification
      
      | Framework | File convention | Test method markers |
      |-----------|----------------|---------------------|
      | XCTest | `*Tests.swift` (Swift Package Manager: `Tests/<Module>Tests/`) | `class FooTests: XCTestCase` with methods starting `test` |
      | Swift Testing | same conventions | `@Test func foo() async throws { ... }`, optionally inside `@Suite` types |
      
      Both frameworks can coexist in one target.
      
      ## Assertion APIs
      
      | Category | XCTest | Swift Testing |
      |----------|--------|---------------|
      | Equality | `XCTAssertEqual(actual, expected)` | `#expect(actual == expected)` |
      | Inequality | `XCTAssertNotEqual` | `#expect(actual != expected)` |
      | Boolean | `XCTAssertTrue` / `XCTAssertFalse` | `#expect(condition)` |
      | Nil | `XCTAssertNil` / `XCTAssertNotNil` | `#expect(value == nil)` / `#expect(value != nil)` |
      | Throws | `XCTAssertThrowsError(try fn()) { error in ... }` | `#expect(throws: SomeError.self) { try fn() }` / `try #require(throws: ...)` |
      | No throw | `XCTAssertNoThrow(try fn())` | implicit (just call `try fn()`) |
      | Identical (reference) | `XCTAssertIdentical` | `#expect(a === b)` |
      | Approximate | `XCTAssertEqual(x, y, accuracy: 0.01)` | `#expect(abs(x - y) < 0.01)` |
      | Type | `XCTAssertTrue(x is T)` | `#expect(x is T)` |
      | Membership | `XCTAssertTrue(arr.contains(item))` | `#expect(arr.contains(item))` |
      | Fail | `XCTFail("reason")` | `Issue.record("reason")` |
      | Soft fail (continue) | continues on `XCTAssert*` by default | `#expect` (records issues, continues) |
      | Hard fail (stop) | `XCTSkipIf` is skip; no hard-fail at test level | `try #require(...)` aborts the test |
      
      ## Sleep/Delay Patterns
      
      | Pattern | Example |
      |---------|---------|
      | Thread sleep | `Thread.sleep(forTimeInterval: 1.0)` |
      | Async sleep | `try await Task.sleep(nanoseconds: 1_000_000_000)` (Swift 5.5+) |
      | Async sleep (newer) | `try await Task.sleep(for: .seconds(1))` (Swift 5.7+) |
      | XCTest waiter | `wait(for: [exp], timeout: 5)` (acceptable for expectation-based tests) |
      | Async waiter | `await fulfillment(of: [exp], timeout: 5)` (Xcode 14+) |
      
      `XCTestExpectation` + `wait(for:timeout:)` is the idiomatic async-coordination pattern in XCTest — not a sleep smell.
      
      ## Skip/Ignore Annotations
      
      | Framework | Annotation |
      |-----------|------------|
      | XCTest | `throw XCTSkip("reason")`, `XCTSkipIf(cond, "reason")`, `XCTSkipUnless(cond, "reason")` |
      | Swift Testing | `@Test(.disabled("reason"))`, `@Test(.disabled(if: cond, "reason"))`, `@Test(.enabled(if: cond))` |
      
      ## Exception Handling — Idiomatic Alternatives
      
      ```swift
      // XCTest:
      XCTAssertThrowsError(try service.placeOrder(emptyOrder)) { error in
          guard case OrderError.empty = error else {
              XCTFail("Expected .empty, got \(error)")
              return
          }
      }
      
      // Swift Testing:
      #expect(throws: OrderError.self) {
          try service.placeOrder(emptyOrder)
      }
      
      // Specific case (Swift Testing):
      let err = try #require(throws: OrderError.self) { try service.placeOrder(emptyOrder) }
      #expect(err == .empty)
      ```
      
      Flag manual `do { try fn(); XCTFail("expected throw") } catch { ... }` patterns and recommend the framework-native form.
      
      ## Mystery Guest — Common Swift Patterns
      
      | Indicator | What to look for |
      |-----------|------------------|
      | File system | `FileManager.default.contents(atPath:)`, hard-coded `Bundle.main` paths |
      | Database | direct `SQLite.swift` against real file, raw `CoreData` saves outside in-memory store |
      | Network | `URLSession.shared.dataTask` without `URLProtocol` stub, `Alamofire` to real URL |
      | Environment | `ProcessInfo.processInfo.environment["X"]` |
      | Acceptable | `URLProtocol` stubs, OHHTTPStubs, `Mocker`, `NSPersistentContainer` with in-memory store type, `Bundle.module` resource paths |
      
      ## Integration Test Markers
      
      - Folder convention: `IntegrationTests/`, `UITests/`, `E2ETests/`
      - Class name suffix: `*IntegrationTests`, `*UITests`
      - `XCUITest` (`XCUIApplication`, `XCUIElement`) → UI/E2E test
      - Swift Testing `@Tag` named `.integration` or `.ui`
      
      ## Setup/Teardown
      
      | Framework | Per-test | Per-class/suite |
      |-----------|----------|-----------------|
      | XCTest | `setUp() / setUpWithError()` | `override class func setUp()` |
      | XCTest | `tearDown() / tearDownWithError()` | `override class func tearDown()` |
      | Swift Testing | `init(...) async throws` per instance | static via `@Suite` type |
      | Swift Testing | `deinit` for cleanup | static via `@Suite` type |
      
      Swift Testing creates a fresh instance per test by default — fields initialized in `init` are reset between tests.
      
      ## Tag/Trait Attributes (for `test-tagging`)
      
      | Framework | Tag mechanism | Example |
      |-----------|---------------|---------|
      | Swift Testing | `@Test(.tags(.positive, .boundary))` (predefined or custom tag) | requires `extension Tag { @Tag static var positive: Self }` |
      | Swift Testing (suite) | `@Suite(.tags(...))` | inherits tags to contained tests |
      | XCTest | none built-in — use class organization, naming, or test plans (.xctestplan) | *(report-only)* |
      
      For Swift Testing, define tags in a single module-level location:
      
      ```swift
      extension Tag {
          @Tag static var positive: Self
          @Tag static var negative: Self
          @Tag static var boundary: Self
          @Tag static var integration: Self
      }
      ```
      
      ## Language-specific calibration notes
      
      - **Swift Testing `#expect` continues on failure**; `try #require` aborts. Tests that mix preconditions and assertions should use `try #require` for preconditions.
      - **`XCTAssert*` continues on failure** — tests with multiple cascading assertions may log many failures from one root cause.
      - **Async tests must `await`** — missing `await` causes warnings and silent skips on async APIs.
      - **Combine tests** with `expectation(description:)` are XCTest's idiomatic async pattern; not a sleep smell.
      - **Snapshot testing** (`SnapshotTesting` library) — treat snapshot comparisons as legitimate assertions; flag stale records.
      - **Parametrized tests** (`@Test(arguments: [...])`) are NOT duplicates.
      - **Test plans (`.xctestplan`)** can filter by tags / configurations; mention as a structural alternative to per-test tagging.
      - **`continueAfterFailure`** — when `false`, XCTest stops on first failure (useful for fast-fail integration tests).
      
    • typescript.md 7.4 KB
      # TypeScript / JavaScript Test Frameworks Reference (Jest, Vitest, Mocha, Jasmine, node:test)
      
      Reference data for analyzing JS/TS test code. Used by the polyglot test analysis skills.
      
      ## Capability tags
      
      | Capability | Support |
      |------------|---------|
      | Test discovery | Strong — `*.test.ts`, `*.spec.ts`, `__tests__/` |
      | Assertion detection | Strong — `expect`, `assert`, `chai` |
      | Sleep/delay detection | Strong — `setTimeout`, `sleep`, `wait` helpers |
      | Skip/ignore detection | Strong — `.skip`, `xit`, `xdescribe` |
      | Setup/teardown detection | Strong — `beforeEach`, `afterEach`, hooks |
      | Tag support | **report-only** by default — no canonical attribute; some frameworks accept `tags` option (Vitest test.options.tag) or describe-based grouping |
      
      ## Test File Identification
      
      | Framework | File convention | Test method markers |
      |-----------|----------------|---------------------|
      | Jest | `*.test.ts/js/tsx/jsx`, `*.spec.*`, files in `__tests__/` | `test()`, `it()`, `describe()` |
      | Vitest | `*.test.ts/js`, `*.spec.*` | `test()`, `it()`, `describe()` (same shape as Jest) |
      | Mocha | `test/**/*.js` (configurable) | `it()`, `describe()` |
      | Jasmine | `*Spec.js`, `*.spec.js` | `it()`, `describe()` |
      | node:test | `*.test.js`, `test/**/*.js` | `test()` from `node:test` |
      
      ## Assertion APIs
      
      | Category | Jest / Vitest (`expect`) | Mocha + Chai (`expect`) | node:test (`assert`) |
      |----------|--------------------------|-------------------------|---------------------|
      | Equality | `expect(x).toBe(y)` / `.toEqual()` | `expect(x).to.equal(y)` / `.deep.equal()` | `assert.strictEqual(x, y)` / `assert.deepStrictEqual()` |
      | Inequality | `expect(x).not.toBe(y)` | `expect(x).to.not.equal(y)` | `assert.notStrictEqual(x, y)` |
      | Truthy/Falsy | `.toBeTruthy()` / `.toBeFalsy()` | `.to.be.true` / `.to.be.false` | `assert.ok(x)` |
      | Null/Undefined | `.toBeNull()` / `.toBeUndefined()` / `.toBeDefined()` | `.to.be.null` / `.to.be.undefined` | `assert.equal(x, null)` |
      | Exception | `expect(() => fn()).toThrow(Error)` / `await expect(promise).rejects.toThrow()` | `expect(fn).to.throw(Error)` | `assert.throws(fn, Error)` / `await assert.rejects(promise)` |
      | Type | `.toBeInstanceOf(Cls)` | `.to.be.instanceOf(Cls)` | `assert.ok(x instanceof Cls)` |
      | Membership | `.toContain(item)` | `.to.include(item)` | `assert.ok(arr.includes(item))` |
      | String | `.toMatch(/regex/)` / `.toContain('sub')` | `.to.match(/regex/)` | `assert.match(s, /regex/)` |
      | Object shape | `.toMatchObject({...})` | `.to.deep.include({...})` | (manual) |
      | Snapshot | `.toMatchSnapshot()` / `.toMatchInlineSnapshot()` | *(via plugin)* | *(via plugin)* |
      | Mock calls | `expect(mock).toHaveBeenCalledWith(...)` | `sinon.assert.calledWith(...)` | (manual) |
      
      Third-party libraries: `chai`, `should`, `sinon-chai`, `@vitest/expect`.
      
      ## Sleep/Delay Patterns
      
      | Pattern | Example |
      |---------|---------|
      | setTimeout sleep | `await new Promise(r => setTimeout(r, 1000))` |
      | Hard sleep helpers | `sleep(1000)`, `await delay(500)` |
      | Loop wait | `while (!condition) await sleep(100)` |
      | Jest fake timers | `jest.advanceTimersByTime(...)` (acceptable, not a sleep) |
      
      ## Skip/Ignore Annotations
      
      | Framework | Skip | Focused (only) |
      |-----------|------|----------------|
      | Jest | `test.skip`, `it.skip`, `describe.skip`, `xit`, `xdescribe`, `xtest` | `test.only`, `fit`, `fdescribe` |
      | Vitest | `test.skip`, `it.skip`, `describe.skip`, `test.todo`, `test.skipIf(cond)` | `test.only` |
      | Mocha | `it.skip`, `describe.skip`, `xit`, `xdescribe` | `it.only`, `describe.only` |
      | Jasmine | `xit`, `xdescribe`, `pending()` | `fit`, `fdescribe` |
      | node:test | `test(name, { skip: true }, fn)`, `test.skip(...)`, `test.todo(...)` | `test(name, { only: true }, fn)` |
      
      `.only` patterns are an anti-pattern when committed — they silently disable the rest of the suite.
      
      ## Exception Handling — Idiomatic Alternatives
      
      ```ts
      // Jest / Vitest (sync):
      expect(() => parseAmount(-5)).toThrow(RangeError);
      
      // Jest / Vitest (async):
      await expect(parseAmountAsync(-5)).rejects.toThrow(RangeError);
      
      // Chai:
      expect(() => parseAmount(-5)).to.throw(RangeError, /must be positive/);
      
      // node:test:
      assert.throws(() => parseAmount(-5), RangeError);
      await assert.rejects(parseAmountAsync(-5), RangeError);
      ```
      
      Flag `try { ... } catch (e) { /* nothing */ }` and `try { ... } catch { expect(...) }` patterns as Exception Handling smells unless the catch performs a specific assertion.
      
      ## Mystery Guest — Common JS/TS Patterns
      
      | Indicator | What to look for |
      |-----------|------------------|
      | File system | `fs.readFileSync`, `fs.promises.readFile`, hard-coded absolute paths |
      | Database | direct `pg.Client`, `mongodb.MongoClient` against a real DB |
      | Network | `fetch`, `axios.get/post`, `http.request` without a mock adapter |
      | Environment | `process.env.X` (especially without default) |
      | Acceptable | `memfs`, `mock-fs`, `nock`, `msw`, `axios-mock-adapter`, `vi.mock` / `jest.mock` |
      
      ## Integration Test Markers
      
      - Folder names: `__tests__/integration/`, `tests/e2e/`, `cypress/`, `playwright/`
      - File suffix: `*.integration.test.ts`, `*.e2e.test.ts`
      - `describe('Integration: …', …)` wrappers
      - Playwright/Cypress/WebdriverIO usage almost always implies E2E
      
      ## Setup/Teardown
      
      | Framework | Per-test | Per-suite |
      |-----------|----------|-----------|
      | Jest / Vitest / Mocha / Jasmine | `beforeEach()` / `afterEach()` | `beforeAll()` / `afterAll()` (Mocha: `before` / `after`) |
      | node:test | `beforeEach(fn)` / `afterEach(fn)` from `node:test` | `before(fn)` / `after(fn)` |
      
      ## Tag/Trait Attributes (for `test-tagging`)
      
      **Default mode: report-only.** JS/TS test frameworks generally have no canonical tag attribute. Strategies:
      
      - **describe-based grouping** — wrap tests in `describe('@positive | OrderService', ...)` and grep the prefix.
      - **Test name prefixes** — `it('[boundary] handles zero quantity', ...)`.
      - **Vitest options object** — Vitest accepts arbitrary metadata on tests but no first-class tag filter.
      - **Custom reporters** — projects can read JSDoc-style `@tags` and surface them.
      
      Only switch to `auto-edit` mode when the project already follows one of these conventions (detect by sampling existing tests).
      
      ## Language-specific calibration notes
      
      - **Async tests missing `await`** are a critical smell. `expect(promise).resolves.toBe(...)` without `await` resolves nothing and the test passes silently. Flag any unawaited promise inside a test body (linters: `@typescript-eslint/no-floating-promises`, `vitest/no-disabled-tests`).
      - **Snapshot tests** count as assertions — but flag stale or always-passing snapshots (no `expect.assertions(n)` and only `toMatchSnapshot`).
      - **`expect.assertions(n)`** is a useful guardrail; tests using it lock in assertion count.
      - **Implicit assertion via mock matchers**: `expect(mock).toHaveBeenCalled()` is a valid assertion — do not treat as assertion-free.
      - **Done callbacks** in Mocha-style tests (`it('x', (done) => { ... done(); })`) are legacy; absence of `done()` call in a callback test is a silent pass.
      - **`xit`/`xdescribe`** are commits of disabled tests — flag like `[Ignore]`.
      - **`.only`** committed to source is a critical smell — silently disables the rest of the file/suite.
      - **describe.each / test.each** are parametrized; not duplicate tests.
      - **`fail()` is removed in Jest 27+** — flag `if (cond) fail('msg')` patterns and recommend `throw new Error('msg')` or an explicit failing assertion such as `expect(value).toBe(...)` instead.
      
  • SKILL.md 4.5 KB
    ---
    name: test-analysis-extensions
    description: >-
      Provides file paths to language-specific reference files for the test
      ANALYSIS skills (assertion-quality, test-anti-patterns, test-gap-analysis,
      test-smell-detection, test-tagging). Call this skill to discover available
      extension files (e.g., dotnet.md for .NET/MSTest/xUnit/NUnit/TUnit,
      python.md for pytest/unittest, typescript.md for Jest/Vitest/Mocha,
      java.md for JUnit/TestNG, etc.). Do not use directly — invoked by the
      test-quality-auditor agent and polyglot analysis skills that need
      framework-specific lookup tables (test markers, assertion APIs, skip
      annotations, sleep patterns, mystery guest indicators, integration
      markers, setup/teardown, tag-support capability).
    user-invocable: false
    disable-model-invocation: true
    license: MIT
    ---
    
    # Test Analysis Extensions
    
    This skill provides access to per-language reference files used by the polyglot test analysis skills. Call this skill to get the list of available extension files, then read the one matching the target codebase's language and test framework.
    
    ## Available Extension Files
    
    | File | Languages / Frameworks | Contents |
    |------|------------------------|----------|
    | [extensions/dotnet.md](extensions/dotnet.md) | .NET (C#/F#/VB) — MSTest, xUnit, NUnit, TUnit | Test markers, assertion APIs, sleep/delay patterns, skip annotations, mystery guest, integration markers, setup/teardown, tag support |
    | [extensions/python.md](extensions/python.md) | Python — pytest, unittest | Same categories, with pytest fixtures/markers and unittest TestCase |
    | [extensions/typescript.md](extensions/typescript.md) | TypeScript / JavaScript — Jest, Vitest, Mocha, Jasmine, node:test | Same categories, with async/await pitfalls |
    | [extensions/java.md](extensions/java.md) | Java — JUnit 4, JUnit 5 (Jupiter), TestNG | Same categories, with `@Tag` / `@Category` / groups |
    | [extensions/go.md](extensions/go.md) | Go — `testing` package, testify | Same categories, with table-driven idiom and build tags |
    | [extensions/ruby.md](extensions/ruby.md) | Ruby — RSpec, Minitest | Same categories, with RSpec metadata and Minitest tags |
    | [extensions/rust.md](extensions/rust.md) | Rust — built-in `#[test]`, `cargo test` | Same categories, with `#[ignore]`, `#[should_panic]`, feature flags |
    | [extensions/swift.md](extensions/swift.md) | Swift — XCTest, Swift Testing | Same categories, with `@Test`, `@Tag`, `@Suite` |
    | [extensions/kotlin.md](extensions/kotlin.md) | Kotlin — JUnit 5, Kotest, MockK | Same categories, with `@Tag` and Kotest tags |
    | [extensions/powershell.md](extensions/powershell.md) | PowerShell — Pester v5 | Same categories, with `-Tag` and `Skip` |
    | [extensions/cpp.md](extensions/cpp.md) | C++ — GoogleTest, Catch2, doctest | Same categories, with `[tags]` and `*` filters |
    
    ## Usage
    
    1. Detect the target codebase's primary language and test framework.
    2. Read the matching extension file before performing analysis.
    3. If multiple test frameworks are present (e.g., a project mixing Jest and Mocha), read all relevant extensions.
    4. Each extension file documents the same categories so analysis skills can be language-neutral.
    
    ## Capability tags
    
    Each extension file declares per-capability support so skills can gate behaviour safely:
    
    - **Test discovery** — how to locate test files and methods.
    - **Assertion detection** — framework-specific and language-level assertion forms.
    - **Sleep/delay patterns** — synchronous and asynchronous waits.
    - **Skip / ignore** — how to recognize skipped/ignored tests.
    - **Setup / teardown** — fixture and lifecycle hooks.
    - **Mystery guest indicators** — common file/db/network/env coupling patterns.
    - **Integration markers** — conventions that mark a test as integration/E2E.
    - **Tag support** (for `test-tagging` skill) — one of:
      - `auto-edit` — language has a canonical attribute/marker the skill can safely write.
      - `report-only` — no canonical syntax; produce audit reports without edits.
      - `convention-based` — tags exist via name/comment conventions only.
    
    ## Notes for skill authors
    
    - Treat extension files as data, not as guidance to follow verbatim. They tell skills *how to detect things* in each language, not *what to think* about findings.
    - When language detection is uncertain, prefer reading multiple extension files over guessing.
    - If the user explicitly names a framework that does not have an extension file yet, fall back to the closest one (e.g., Pest → python.md/pytest semantics) and note the gap in the report.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related