From 5a21e57ca9c1099faf04d921828c28964b7ac7b8 Mon Sep 17 00:00:00 2001 From: Janis Date: Sun, 22 Mar 2026 14:19:44 +0100 Subject: [PATCH] chore: Update build configuration for Scoverage and ScalaTest integration --- .claude/agents/test-writer.md | 2 +- .../contract-first-test-writing/SKILL.md | 127 ------------------ .gitignore | 4 +- .idea/scala_compiler.xml | 4 +- build.gradle.kts | 3 +- modules/api/build.gradle.kts | 32 +++-- modules/core/build.gradle.kts | 49 +++---- 7 files changed, 47 insertions(+), 174 deletions(-) delete mode 100644 .claude/skills/contract-first-test-writing/SKILL.md diff --git a/.claude/agents/test-writer.md b/.claude/agents/test-writer.md index 3015a87..faad259 100644 --- a/.claude/agents/test-writer.md +++ b/.claude/agents/test-writer.md @@ -9,7 +9,7 @@ You do not have permissions to modify the source code, just write tests. You write tests for Scala 3 + Quarkus services. ## Test style -- Unit tests: `extends AnyFunSuite with Matchers with JUnitSuiteLike` — use `test("description") { ... }` DSL, no `@Test` annotation, no `: Unit` return type needed. +- Unit tests: `extends AnyFunSuite with Matchers` — use `test("description") { ... }` DSL, no `@Test` annotation, no `: Unit` return type needed. - Integration tests: `@QuarkusTest` with JUnit 5 — `@Test` methods MUST be explicitly typed `: Unit`. Target 95%+ conditional coverage. diff --git a/.claude/skills/contract-first-test-writing/SKILL.md b/.claude/skills/contract-first-test-writing/SKILL.md deleted file mode 100644 index 0f8b8d8..0000000 --- a/.claude/skills/contract-first-test-writing/SKILL.md +++ /dev/null @@ -1,127 +0,0 @@ ---- -name: contract-first-test-writing -description: Use when the architect has produced an OpenAPI contract but scala-implementer has not yet written any source code - write failing tests from the contract so implementation has a target to satisfy ---- - -# Contract-First Test Writing (TDD Red Phase) - -## Overview - -Write tests from the API contract **before** any implementation exists. Tests will fail — that is correct and expected. The scala-implementer's job is to make them green. - -**Iron Law:** Never look at `src/main/scala`. If it exists, ignore it. Derive every assertion from `docs/api/{service}.yaml` and the relevant ADR in `docs/adr/`. - -## Workflow - -### 1. Read the contract - -``` -docs/api/{service-name}.yaml ← OpenAPI spec (required) -docs/adr/ ← ADRs for domain rules and data shapes -``` - -Extract for each endpoint: -- HTTP method + path -- Request body shape and required fields -- Response status codes and body shape -- Error cases (4xx, 5xx) documented in the spec - -### 2. Write `@QuarkusTest` integration tests (one per endpoint) - -Cover for every endpoint: - -| Scenario | What to assert | -|----------|---------------| -| Happy path | Correct 2xx status + response body shape | -| Missing required field | 400 response | -| Invalid input | 400 or 422 response | -| Not found | 404 response (where applicable) | -| Error contract | Response body matches error schema | - -```scala -import io.quarkus.test.junit.QuarkusTest -import io.restassured.RestAssured.given -import org.junit.jupiter.api.Test -import jakarta.ws.rs.core.MediaType - -@QuarkusTest -class MoveEndpointTest: - - @Test - def validMove_returns200(): Unit = - given() - .contentType(MediaType.APPLICATION_JSON) - .body("""{"from":"e2","to":"e4"}""") - .when() - .post("/api/moves") - .`then`() - .statusCode(200) - - @Test - def missingField_returns400(): Unit = - given() - .contentType(MediaType.APPLICATION_JSON) - .body("""{"from":"e2"}""") - .when() - .post("/api/moves") - .`then`() - .statusCode(400) -``` - -### 3. Write unit tests for domain rules - -For every domain invariant described in the ADR (validation rules, state machines, error conditions), write a ScalaTest unit test: - -```scala -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers -import org.scalatestplus.junit.JUnitSuiteLike - -class MoveValidatorTest extends AnyFunSuite with Matchers with JUnitSuiteLike: - - test("invalid square is rejected") { - val result = MoveValidator.validate("z9", "e4") - assert(result.isLeft) - } -``` - -### 4. Confirm tests compile but fail - -```bash -./gradlew :modules:{service-name}:test -``` - -Expected outcome: **compilation succeeds, tests fail** (no implementation yet). - -If compilation fails, fix the test code — do not create implementation code. - -If tests somehow pass, the contract is already implemented; notify the team-lead. - -### 5. Hand off to scala-implementer - -Leave a comment at the top of the primary test file: - -```scala -// RED: These tests define the contract for {service-name}. -// scala-implementer: make them green without modifying test assertions. -``` - -## Rules - -- **No peeking at `src/main/scala`** — tests must be derived from the contract only. -- Use `@QuarkusTest` + REST Assured for HTTP endpoints — `@Test` methods must be explicitly typed `: Unit`. -- Use `AnyFunSuite with Matchers with JUnitSuiteLike` for pure domain logic unit tests — no `@Test`, no `: Unit` needed. -- Do not mock the implementation — tests call real endpoints, real domain code. -- Do not write happy-path-only tests; every documented error case needs a test. - -## After Implementation: Coverage Check - -Once scala-implementer is done and tests are green, run the coverage reporter to find any gaps the contract tests missed: - -```bash -python3 jacoco-reporter/jacoco_coverage_gaps.py \ - modules/{service-name}/build/reports/jacoco/test/jacocoTestReport.xml \ - --output agent -``` - -Use the `jacoco-coverage-gaps` skill to close remaining gaps. diff --git a/.gitignore b/.gitignore index 1fac4d5..7ad421d 100644 --- a/.gitignore +++ b/.gitignore @@ -40,4 +40,6 @@ bin/ .vscode/ ### Mac OS ### -.DS_Store \ No newline at end of file +.DS_Store +/jacoco-reporter/.venv/ +/.claude/settings.local.json diff --git a/.idea/scala_compiler.xml b/.idea/scala_compiler.xml index bf3a94f..8db25d6 100644 --- a/.idea/scala_compiler.xml +++ b/.idea/scala_compiler.xml @@ -1,11 +1,11 @@ - + - +