From 66273694f191fbb82686153361f7e72b99d9f23d Mon Sep 17 00:00:00 2001 From: Karthik Loganathan Date: Sat, 18 Apr 2026 22:21:35 -0400 Subject: [PATCH 01/12] fix: update stress test schedule to 22:25 ET (02:25 UTC Monday) --- .github/workflows/performance-stress.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/performance-stress.yml b/.github/workflows/performance-stress.yml index 49c7dc3..f33673c 100644 --- a/.github/workflows/performance-stress.yml +++ b/.github/workflows/performance-stress.yml @@ -2,7 +2,7 @@ name: Performance Stress Test on: schedule: - - cron: '0 2 * * 1' + - cron: '25 2 * * 1' workflow_dispatch: inputs: target_url: From 89ba7140497df072b780f239c1babfeb15ebb4c0 Mon Sep 17 00:00:00 2001 From: Karthik Loganathan Date: Sat, 18 Apr 2026 22:33:55 -0400 Subject: [PATCH 02/12] docs: update architecture diagram with k6 fourth layer --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 44affea..6c4f684 100644 --- a/README.md +++ b/README.md @@ -45,13 +45,13 @@ flowchart TD direction LR PR1["REST Assured\nFunctional tests"] PR2["Karate BDD\nScenario tests"] - PR3["k6 Component\nOne per endpoint"] + PR3["k6 Component\nOne per endpoint\n60s each"] end CO["Pact Consumer\nContract generation"] PV["Pact Provider\nContract verification"] SM["Staging Smoke\nKarate @smoke\n5 min Β· critical paths only"] - K6L["k6 System Load\nFull e2e journey"] - K6S["k6 Stress\nScheduled β€” Monday 2AM"] + K6L["k6 System Load\nFull e2e journey\nStaging deploy"] + K6S["k6 Stress\nScheduled Mon 2AM\nBreak point discovery"] end API["🌐 JSONPlaceholder\nRepresents microservice layer"] From e8f81e1cae5ada7ea37115ee2c696bb06c8da999 Mon Sep 17 00:00:00 2001 From: Karthik Loganathan Date: Sat, 18 Apr 2026 22:51:10 -0400 Subject: [PATCH 03/12] docs: update pipeline screenshot and architecture diagram with k6 four-layer --- docs/images/pipeline-visual.png | Bin 55113 -> 75604 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/images/pipeline-visual.png b/docs/images/pipeline-visual.png index bb279054c0e8a7295728b35b93483daffb491812..eba24ebd38cc30f92086d5d057991d4a1edcccee 100644 GIT binary patch literal 75604 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 literal 55113 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
    L; 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 From bb185db2418d171e11ca9fb2113e7721bacea9fa Mon Sep 17 00:00:00 2001 From: Karthik Loganathan Date: Sat, 18 Apr 2026 22:54:54 -0400 Subject: [PATCH 04/12] fix: move k6 component into parallel PR gate subgraph --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 6c4f684..60337d2 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,6 @@ flowchart TD L1 --> PR1 L2 --> PR2 - L4 --> PR3 PR1 --> CO PR2 --> CO PR3 --> CO From 188cb3503d6cf4808c9ddfb0a27b49086cbf9be2 Mon Sep 17 00:00:00 2001 From: Karthik Loganathan Date: Sat, 18 Apr 2026 22:58:37 -0400 Subject: [PATCH 05/12] docs: update pipeline section with k6 five-job caption and workflow summary --- README.md | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 60337d2..4416dbf 100644 --- a/README.md +++ b/README.md @@ -172,13 +172,18 @@ Pact inverts the testing direction. The consumer team (e.g., Cart service consum ## 6. Pipeline Integration -![PR Quality Gate β€” 4-job dependency chain](docs/images/pipeline-visual.png) -*PR Quality Gate: REST Assured and Karate run in parallel, -feeding Pact Consumer contract generation, -followed by Pact Provider verification. -Total execution: ~80 seconds on every pull request.* - -Two workflows, one principle: **the right tests at the right gate.** +![PR Quality Gate](docs/images/pipeline-visual.png) +*PR Quality Gate: Five parallel jobs β€” REST Assured, +Karate BDD, and three k6 component performance gates +run simultaneously, feeding Pact Consumer contract +generation, followed by Pact Provider verification. +Total execution: ~2m 18s on every pull request.* + +Three workflows, one principle: **the right tests at the right gate, at the right time.** + +- **PR Quality Gate** β€” functional, BDD, and performance component gates on every pull request +- **Staging Smoke** β€” Karate @smoke + k6 system load on every staging deployment +- **Performance Stress** β€” scheduled Monday 2AM UTC, on-demand for capacity planning ### `pr-quality-gate.yml` β€” runs on every PR to `main` From 65d14868df84894c2e33e6ed8ef1a39c27cabbc7 Mon Sep 17 00:00:00 2001 From: Karthik Loganathan Date: Sat, 18 Apr 2026 23:15:35 -0400 Subject: [PATCH 06/12] docs: add SKILL.md to root, use-this-skill section in README --- README.md | 13 ++ SKILL.md | 581 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 594 insertions(+) create mode 100644 SKILL.md diff --git a/README.md b/README.md index 4416dbf..2066fd1 100644 --- a/README.md +++ b/README.md @@ -230,6 +230,19 @@ The goal is not to eliminate Postman from engineers' desktops. The goal is to re --- +## Use This Skill + +This repository was built using a reusable Claude Code skill. The full prompt template β€” including all seven prompts, verification checklist, customization guide, and talking points β€” is available at: + +πŸ‘‰ [SKILL.md](SKILL.md) + +**Adapt it for any Java microservices project** by replacing six variables: +- PROJECT_NAME, PACKAGE_BASE, CONSUMER_NAME, PROVIDER_NAME, TARGET_API, DOMAIN + +The skill produces a working three-layer API testing platform with GitHub Actions pipeline in under 9 hours using Claude Code. + +--- + ## 8. Getting Started **Prerequisites:** Java 17, Maven 3.9+ diff --git a/SKILL.md b/SKILL.md new file mode 100644 index 0000000..c4ed288 --- /dev/null +++ b/SKILL.md @@ -0,0 +1,581 @@ +# CLAUDE_SKILL_API_QE_PLATFORM.md + +## Skill: API Quality Engineering Platform + +--- + +### Purpose + +This skill builds a production-quality, three-layer API testing reference architecture on a Java/Maven multi-module project. Use it when replacing Postman/Newman in a CI pipeline, demonstrating enterprise QE platform thinking, or establishing a governed, code-reviewed testing foundation for a Java microservices org. The output is a fully working Maven project with REST Assured (functional validation), Karate DSL (BDD governance), and Pact (consumer-driven contract testing), wired together in a GitHub Actions pipeline with a 4-job dependency chain. Every layer is independently buildable and independently runnable against any REST API. + +--- + +### Prerequisites + +- Java 17+ +- Maven 3.9+ +- GitHub repository created +- Public mock API available (JSONPlaceholder default: `https://jsonplaceholder.typicode.com`) + +--- + +### Variables to Customize + +Replace these values throughout all prompts before using them. + +| Variable | Default | Description | +|---|---|---| +| `PROJECT_NAME` | `api-quality-platform-reference` | GitHub repo name and root Maven artifactId | +| `PACKAGE_BASE` | `com.wag.qe` | Java package root for all source files | +| `CONSUMER_NAME` | `PrescriptionService` | Pact consumer service name (the service making calls) | +| `PROVIDER_NAME` | `PatientService` | Pact provider service name (the service receiving calls) | +| `TARGET_API` | `https://jsonplaceholder.typicode.com` | Mock API base URL used in CI and local runs | +| `DOMAIN` | `prescription/patient` | Business domain for test naming, feature file directories, and scenario language | + +--- + +### Prompt 1 β€” Project Scaffolding + +``` +I'm building a Java API testing reference architecture to demonstrate enterprise QE +platform thinking. I need you to scaffold the complete project structure. + +Project name: PROJECT_NAME +Java package root: PACKAGE_BASE +Target mock API: TARGET_API + +Build a Maven multi-module project with this exact structure: + +PROJECT_NAME/ +β”œβ”€β”€ pom.xml (parent POM) +β”œβ”€β”€ README.md (8-section architect-to-VP narrative) +β”œβ”€β”€ rest-assured/ +β”‚ └── pom.xml +β”œβ”€β”€ karate/ +β”‚ └── pom.xml +β”œβ”€β”€ pact/ +β”‚ └── pom.xml +└── .github/ + └── workflows/ + β”œβ”€β”€ pr-quality-gate.yml (stub) + └── staging-smoke.yml (stub) + +Parent POM requirements: +- groupId: PACKAGE_BASE +- artifactId: PROJECT_NAME +- Java 17, UTF-8 +- Modules: rest-assured, karate, pact +- Manage these dependency versions in dependencyManagement (never declare versions + in child POMs): + - REST Assured 5.3.2 + - TestNG 7.9.0 + - com.intuit.karate:karate-junit5:1.4.0 (NOT io.karatelabs β€” not on Maven Central) + - au.com.dius.pact.consumer:junit5:4.6.7 + - au.com.dius.pact.provider:junit5:4.6.7 + - junit-jupiter-api and junit-jupiter-engine (latest stable) + - jackson-databind (latest stable) + - logback-classic (latest stable) + - SLF4J API (latest stable) +- Properties: api.base.url (default TARGET_API), pact.broker.url, pact.broker.token +- Maven Surefire 3.x configured to use JUnit platform in pluginManagement + +README.md must have exactly 8 sections written as an architect explaining to a VP +why Postman/Newman fails at platform scale and what this replaces it with: +1. The Problem This Solves β€” three specific Postman failure modes: governance, + secrets management, contract awareness +2. Architecture Decision β€” three layers, what each solves, why Maven multi-module +3. Layer 1: REST Assured β€” functional validation, ApiConfig pattern, system property injection +4. Layer 2: Karate β€” BDD governance, karate-config.js, why Karate over Cucumber +5. Layer 3: Pact β€” consumer-driven contracts, the guarantee it provides, Pact Broker +6. Pipeline Integration β€” two workflows, what runs when and why +7. What This Replaces and What It Doesn't β€” Postman narrows to exploration only +8. Getting Started β€” prerequisites, mvn commands for each layer + +The README must include a concrete example in the Pact section: Patient API renames +the "name" field to "fullName". Walk through what happens without contract testing +(silent production failure) versus with contract testing (PR blocked, conversation +between engineers). + +Do not write tutorial-style content. Write as an architect, not an instructor. +``` + +--- + +### Prompt 2 β€” REST Assured Layer + +``` +Build out the complete REST Assured layer for PROJECT_NAME. +All code must be production-quality β€” not demo quality. + +Package structure under rest-assured/src/test/java/PACKAGE_BASE/api/: + config/ApiConfig.java + client/PatientApiClient.java + client/PrescriptionApiClient.java + client/CartApiClient.java + PrescriptionApiTest.java + CartApiTest.java + +ApiConfig requirements: +- getBaseSpec() returns a NEW RequestSpecBuilder().build() on every call β€” not a + singleton. This is intentional: TestNG parallel execution requires thread-safe specs. +- Base URL from System.getProperty("api.base.url") +- Connection timeout: 5000ms, socket timeout: 10000ms + Use string literals "http.connection.timeout" and "http.socket.timeout" β€” + RestAssuredConfig requires strings, not the enum. +- FailureOnlyLoggingFilter as a static inner class: + - Implements Filter + - In filter(): call next.next(requestSpec, responseSpec, ctx) to get the response + - Log full request + response at ERROR level via SLF4J only when + response.getStatusCode() >= 400 + - Use Java 16+ pattern matching: if (body instanceof String s) return s; + - Silent on success β€” no log noise in the passing case + +API client pattern (enforce strict separation): +- Client classes: HTTP mechanics only. No assertions. No test logic. + Returns ValidatableResponse from every method. +- Test classes: assertions only. Never call RestAssured directly. + +PatientApiClient β†’ maps to /users endpoint: + getPatient(int id) β†’ GET /users/{id} + getAllPatients() β†’ GET /users + createPatient(Map payload) β†’ POST /users + deletePatient(int id) β†’ DELETE /users/{id} + +PrescriptionApiClient β†’ maps to /posts endpoint: + getPrescriptionsForPatient(int patientId) β†’ GET /posts?userId={patientId} + submitPrescriptionRefill(Map payload) β†’ POST /posts + +CartApiClient β†’ maps to /todos endpoint: + getCartItemsForPatient(int patientId) β†’ GET /todos?userId={patientId} + addCartItem(Map payload) β†’ POST /todos + getAllCartItems() β†’ GET /todos + +PrescriptionApiTest (TestNG, @Test(groups="prescription") on class): +- @BeforeClass logs: [REST Assured] Starting PrescriptionApiTest against: {url} +- Test 1: getPatientRecord_validId_returns200WithSchema + GET /users/1, assert 200, assert body has id/name/email/phone, + assert response.getTime() < 2000 +- Test 2: getPatientRecord_invalidId_returns404 + GET /users/99999, assert 404 + Comment explaining why error-path testing matters in healthcare workflows +- Test 3: submitPrescriptionRefill_validPayload_returns201 + POST /posts with userId/title/body payload, assert 201, assert id > 0 +- Test 4: prescriptionSubmission_responseTime_underSLA + POST /posts, assert response.getTime() < 3000 + Comment explaining why SLA is a separate test, not folded into test 3 + +CartApiTest: similar structure, 3 tests. + +rest-assured/src/test/resources/logback-test.xml: +- Set org.apache.http.wire to OFF +- Set io.restassured to WARN +- Suppress all wire-level HTTP logging in the passing case + +rest-assured/pom.xml: +- Parent: PACKAGE_BASE:PROJECT_NAME +- Dependencies: rest-assured, testng, slf4j-api, logback-classic (all from parent + dependencyManagement, no versions in child POM) +- Surefire: include **/*Test.java, pass api.base.url as systemPropertyVariable + +Write zero comments that explain what the code does. Only write a comment when the +WHY is non-obvious β€” hidden constraints, workarounds, invariants a reader would miss. +``` + +--- + +### Prompt 3 β€” Karate Layer + +``` +Build out the complete Karate layer for PROJECT_NAME. +All feature files must be readable by a non-engineer β€” product managers and QA +leads must be able to read scenario names and understand what is being validated +without reading the step bodies. + +File structure under karate/src/test/resources/: + karate-config.js + features/ + DOMAIN/ + DOMAIN-api.feature + +karate-config.js requirements: +- Three environments: dev, staging, prod +- dev: baseUrl from System.getProperty("api.base.url", "TARGET_API") +- staging: https://staging-api.[your-domain].com +- prod: https://api.[your-domain].com +- For staging and prod: if api.base.url system property is set, it wins (enables + CI override without code changes) +- Log the resolved environment and baseUrl: + karate.log('[Karate] Environment:', env, '| Base URL:', config.baseUrl) +- No credentials in this file. Credentials come from environment variables + injected by the pipeline. + +Feature file requirements: +- @DOMAIN tag on the feature (not individual scenarios) +- @smoke tag on scenarios that must pass as a deployment health check +- Scenario names must be business language β€” written as if Jason (a non-engineer + product manager) will read the HTML report and needs to understand what failed + without asking an engineer + +DOMAIN-api.feature must include: +1. @smoke Scenario: Retrieve known patient record by ID + GET /users/1 + Assert status 200 + Assert response.id == 1 (exact match, not type check) + Assert response.name == 'Leanne Graham' (exact match) + Assert response has email and phone fields + +2. @smoke Scenario: Retrieve non-existent patient returns 404 + GET /users/99999, assert status 404 + +3. @smoke Scenario: Write prescription refill for existing patient + POST /posts with a payload, assert status 201, assert id is present + +4. Scenario Outline: Invalid patient IDs return client errors + IDs: 0, -1, 99999 + Assert responseStatus >= 400 + Add a comment above the Outline explaining why boundary values matter in a + medication dispensing workflow β€” this is the non-obvious WHY + +KarateRunner.java under karate/src/test/java/PACKAGE_BASE/karate/: +- JUnit 5 @Test void testParallel() +- Runner.path("classpath:features").parallel(5) +- Read tags from System.getProperty("karate.options", "--tags @smoke") +- Strip the "--tags " prefix before passing to .tags() +- Fail the build via assertEquals(0, results.getFailCount(), results.getErrorMessages()) + +karate/pom.xml: +- com.intuit.karate:karate-junit5:1.4.0 (from parent dependencyManagement) +- Also explicitly declare junit-jupiter-api and junit-jupiter-engine + (Karate needs them resolvable at test runtime) +- Surefire: include **/*Runner.java only + +Write a cart feature file with the same structure: @cart @smoke tags, 3 scenarios, +business language names, one scenario validating Content-Type response header +using Karate's match syntax on responseHeaders. +``` + +--- + +### Prompt 4 β€” Pact Contract Testing + +``` +Build the Pact consumer-driven contract testing layer for PROJECT_NAME. +This is the most architecturally significant layer. Explain it clearly in code +comments β€” this is the layer interviewers and VP-level stakeholders will scrutinize. + +File structure under pact/src/test/java/PACKAGE_BASE/pact/: + consumer/PrescriptionConsumerTest.java (CONSUMER_NAME consuming PROVIDER_NAME) + provider/PatientProviderTest.java (PROVIDER_NAME verifying contracts) + +PrescriptionConsumerTest requirements: +- @ExtendWith(PactConsumerTestExt.class) +- @PactTestFor(providerName = "PROVIDER_NAME", pactVersion = PactSpecVersion.V3) + V3 is explicit and required. Pact 4.6.7 defaults to V4 which requires a different + PactBuilder DSL and breaks the familiar RequestResponsePact API. +- @Pact(consumer = "CONSUMER_NAME") on the @Pact method β€” returns RequestResponsePact +- Use PactDslWithProvider builder pattern +- PactDslJsonBody declares MINIMUM fields only: + .integerType("id", 1) + .stringType("name", "Leanne Graham") + .stringType("email", "Sincere@april.biz") + .stringType("phone", "1-770-736-8031 x56442") + Comment explaining why minimum fields: allows provider to add/restructure + without triggering false violations +- State: "PROVIDER_NAME user with ID 1 exists" +- Interaction: GET /users/1 β†’ 200 + body +- @Test method uses Java 11 HttpClient (no REST Assured dependency in pact module) + Calls mockServer.getUrl() + "/users/1", asserts 200, asserts body not null + +Class-level Javadoc must explain the 5-step contract testing flow in plain English: + 1. @Pact method declares the interaction + 2. Pact starts a mock server β€” consumer never touches the real provider + 3. @Test proves the consumer can make the request and handle the response + 4. Pact writes the verified interaction to target/pacts/CONSUMER_NAME-PROVIDER_NAME.json + 5. PatientProviderTest loads that file and replays it against the real provider + +PatientProviderTest requirements: +- @Provider("PROVIDER_NAME") +- @PactFolder("target/pacts") +- @BeforeEach configureTarget(PactVerificationContext context): + Read api.base.url system property + Use URL.getProtocol() to pick HttpsTestTarget.fromUrl(url) for https or + HttpTestTarget.fromUrl(url) for http + Use the static fromUrl() factory β€” do NOT use the constructor. The constructor + signature changed in 4.6.x and will cause a compilation error. +- @State("PROVIDER_NAME user with ID 1 exists") β€” no-op body, comment explaining why +- @TestTemplate @ExtendWith(PactVerificationInvocationContextProvider.class) + void verifyContracts(PactVerificationContext context) β†’ context.verifyInteraction() + +pact/pom.xml β€” TWO ordered Surefire executions (this is the critical config): + Problem: alphabetical ordering puts PatientProviderTest before + PrescriptionConsumerTest. Provider verification fails because the pact JSON + doesn't exist yet. + Solution: + 1. Disable the default-test execution by binding it to phase: none + 2. Add execution consumer-contract-generation: includes **/*ConsumerTest.java + 3. Add execution provider-contract-verification: includes **/*ProviderTest.java + Maven executes them in declaration order within the same phase. + + All executions must inherit: + false + ${project.build.directory}/pacts + +In the CI workflow, when filtering with -Dtest=ClassName and the module has multiple +Surefire executions, add -DfailIfNoSpecifiedTests=false. Without it, Surefire 3.x +fails the build when a filtered execution finds no matching tests. + +Do not add a comment summarizing what the code does. Only add comments for +non-obvious constraints: the V3 requirement, the fromUrl() factory requirement, +the ordering problem and its solution. +``` + +--- + +### Prompt 5 β€” GitHub Actions Pipeline + +``` +Build the two GitHub Actions workflow files for PROJECT_NAME. +These are the most important files in the repository after the README. +Every comment must explain WHY, not WHAT. + +.github/workflows/pr-quality-gate.yml requirements: + +Trigger: pull_request β†’ branches: [main, develop] + +Workflow-level env: + JAVA_HOME: ${{ env.JAVA_HOME }} (documents the Java dependency; evaluates to + empty at parse time β€” this is intentional) + JAVA_VERSION: '17' + API_BASE_URL: 'TARGET_API' + +Permissions: checks: write, pull-requests: write +(required for dorny/test-reporter to post Check annotations on the PR) + +Four jobs with this exact dependency structure: + Job 1 β€” rest-assured-functional (no dependencies) + name: REST Assured β€” Functional API Validation + steps: checkout, setup-java (temurin, cache: maven), + mvn test -pl rest-assured -Dapi.base.url=${{ env.API_BASE_URL }} + dorny/test-reporter@v1: reporter java-junit, + path rest-assured/target/surefire-reports/TEST-*.xml, + fail-on-error: 'false' + (fail-on-error is false because Maven exit code already controls job + pass/fail β€” reporter should annotate, not double-fail) + + Job 2 β€” karate-bdd (no dependencies, runs in parallel with Job 1) + name: Karate β€” BDD API Scenarios + steps: checkout, setup-java, + mvn test -pl karate -Dapi.base.url=${{ env.API_BASE_URL }} + upload-artifact@v4: karate/target/karate-reports/ as karate-report + + Job 3 β€” pact-consumer (needs: [rest-assured-functional, karate-bdd]) + name: Pact β€” Consumer Contract Generation + steps: checkout, setup-java, + mvn test -pl pact \ + -Dtest=PrescriptionConsumerTest \ + -DfailIfNoSpecifiedTests=false \ + -Dapi.base.url=${{ env.API_BASE_URL }} + upload-artifact@v4: pact/target/pacts/ as pact-contracts + Comment on the needs: dependency β€” explain why contracts must not be generated + from a failing codebase. A contract from broken code encodes the bug as a + requirement and trains the provider to satisfy incorrect expectations. + + Job 4 β€” pact-provider (needs: [pact-consumer]) + name: Pact β€” Provider Contract Verification + steps: checkout, setup-java, + download-artifact@v4: restore pact-contracts to pact/target/pacts/ + (path must match @PactFolder("target/pacts") β€” Surefire sets working + directory to module root, so target/pacts β†’ pact/target/pacts/ from repo root) + mvn test -pl pact \ + -Dtest=PatientProviderTest \ + -DfailIfNoSpecifiedTests=false \ + -Dapi.base.url=${{ env.API_BASE_URL }} + +.github/workflows/staging-smoke.yml requirements: + +Trigger: workflow_dispatch ONLY + Remove repository_dispatch. Remove PagerDuty. Remove workflow inputs. + This workflow is manually triggered by the deployment pipeline. + +Workflow-level env: same JAVA_HOME pattern, JAVA_VERSION, API_BASE_URL + +One job β€” staging-smoke: + name: Karate β€” Staging Smoke Scenarios + steps: checkout, setup-java, + mvn test -pl karate \ + -Dkarate.options="--tags @smoke" \ + -Dapi.base.url=${{ env.API_BASE_URL }} + upload-artifact@v4: karate/target/karate-reports/ as staging-smoke-report + Comment on the upload: HTML report surfaces which scenario failed and what the + actual response was. Engineers triaging a failed smoke gate read this first. + +Comment at the top of staging-smoke.yml: +"Smoke test runs after every deployment to staging. @smoke tag = critical paths only. +5 minutes maximum. If this fails, staging is unhealthy β€” stop all deployments until fixed." + +REST Assured and Pact do not run here. Code was validated on PR before the artifact +was promoted. Running them again validates infrastructure, not code. +``` + +--- + +### Prompt 6 β€” Architecture Documentation + +``` +Rewrite docs/architecture-decision.md and docs/pipeline-design.md. +Both documents must be sharp, peer-level, and concise β€” written as an architect +explaining decisions to a VP peer. No tutorials. No hand-holding. Under one page each. + +architecture-decision.md must cover: + +1. Context β€” what Postman/Newman fails at, specifically. Three failure modes as + named paragraphs: governance (40 engineers, 12 squads, no review gate), secrets + (plaintext JSON, HIPAA/PCI environment), contract visibility (Newman cannot detect + schema drift between services). + +2. Decision β€” three layers, one sentence each on what problem each solves: + REST Assured: deep payload validation, auth flows, stateful sequences requiring Java + Karate: governed BDD scenarios reviewable by non-engineers; test changes are + PR-gated artifacts + Pact: consumer-driven contracts that surface schema drift at PR time, not production + +3. Alternatives considered β€” prose sentences with the disqualifying reason embedded + (not a table): + Postman only: governance and secrets are architectural β€” no tooling layer fixes them + Playwright API only: TypeScript-first, no Pact broker integration, adds runtime + heterogeneity for no gain + Karate only: no native Pact broker integration; OAuth2/mTLS interop is fragile + +4. Trade-offs β€” honest, one line per layer: + REST Assured: non-Java QA engineers cannot own these tests without ramp-up + Karate: scope it to what it does well; Java interop at the edges is fragile + Pact: requires Pact Broker infrastructure before cross-team guarantees apply + +5. What this replaces β€” Postman role narrows to exploration and documentation only. + Removed from CI entirely. Newman removed from CI entirely. + +6. When to evolve: + TypeScript org: evaluate Pact JS + Playwright, three-layer model holds + Java expertise gaps: shift coverage to Karate, reduce REST Assured to auth + flows and SLA validation only + +pipeline-design.md must cover: + +1. The principle β€” one declarative statement. A test in the wrong gate is quality + theater. Every gate has an explicit rationale. + +2. On Pull Request β€” table: job, what it validates, why here. + +3. On Merge β€” one paragraph. The right answer is: nothing additional runs. + The PR gate is the quality gate. Running the same tests again on merge validates + the pipeline, not the code. + +4. On Staging Deploy β€” Karate @smoke only. Validates the deployment + (DNS, ingress, service mesh, secrets injection) β€” not the code. + +5. The dependency chain β€” ASCII diagram showing the parallel/serial structure. + Explain why the chain is a correctness constraint, not a convenience: a contract + from broken code encodes the bug as a requirement. + +6. Coverage intelligence β€” SeaLights as the next layer (reference only, not + implemented). Explain what it answers that this pipeline cannot. Note that + integration requires tagging test results with build metadata in each job. +``` + +--- + +### Prompt 7 β€” Validation and Cleanup + +``` +Validate the complete PROJECT_NAME build and fix any failures. + +Run in this order: +1. mvn clean compile -DskipTests + Fix any compilation errors before proceeding. + +2. mvn test -pl rest-assured -Dapi.base.url=TARGET_API + All tests must pass. Fix failures before proceeding to next module. + +3. mvn test -pl karate -Dapi.base.url=TARGET_API + All @smoke scenarios must pass. + +4. mvn test -pl pact -Dapi.base.url=TARGET_API + Consumer test must generate target/pacts/CONSUMER_NAME-PROVIDER_NAME.json + Provider test must pass verification against TARGET_API. + Confirm the pact JSON file exists: ls pact/target/pacts/ + +5. Validate GitHub Actions YAML syntax: + python3 -c "import yaml; yaml.safe_load(open('.github/workflows/pr-quality-gate.yml'))" + python3 -c "import yaml; yaml.safe_load(open('.github/workflows/staging-smoke.yml'))" + Both must parse without error. + +Known issues to watch for: +- Pact V4 vs V3 mismatch: if you see "Method does not conform required method signature + 'public V4Pact xxx(PactBuilder builder)'" β€” add pactVersion = PactSpecVersion.V3 + to the @PactTestFor annotation. +- HttpTestTarget constructor error: use HttpTestTarget.fromUrl(URL) static factory, + not the constructor. The constructor signature changed in Pact 4.6.x. +- Surefire ordering: if PatientProviderTest runs before PrescriptionConsumerTest, + the pact JSON does not exist yet. Fix by disabling default-test execution and + adding two named executions in declaration order. +- karate groupId: use com.intuit.karate:karate-junit5:1.4.0, NOT io.karatelabs β€” + io.karatelabs is not on Maven Central. +- -DfailIfNoSpecifiedTests=false: required in CI when using -Dtest=ClassName with + a module that has multiple Surefire executions. + +After all tests pass: +- git add -A +- git commit -m "test: all layers passing against TARGET_API" +- Confirm git log shows a clean linear history with one commit per layer +``` + +--- + +### Verification Checklist + +- [ ] REST Assured: 0 assertions in client classes β€” clients return `ValidatableResponse`, assertions live only in test classes +- [ ] Karate: feature files readable by a non-engineer β€” scenario names describe business outcomes, not HTTP operations +- [ ] Pact: consumer contract JSON generated at `pact/target/pacts/CONSUMER_NAME-PROVIDER_NAME.json` +- [ ] Pact: provider verification calls the real API (check logs β€” no mock server in provider test) +- [ ] Pipeline: 4-job dependency chain correct β€” jobs 1+2 parallel, job 3 waits on both, job 4 waits on job 3 +- [ ] Pipeline: pact artifact download path in job 4 matches `@PactFolder("target/pacts")` +- [ ] All tests passing locally before any commit +- [ ] YAML syntax valid for both workflow files +- [ ] Git log shows clean linear commit history β€” one commit per layer, no fixup commits visible + +--- + +### How to Present This to a Technical VP + +Five talking points. No notes needed. + +**1. Three problems with Postman at enterprise scale** +Postman breaks in three specific ways at platform scale: governance (test changes ship without review because collections live in personal accounts), secrets (credentials exported as plaintext JSON β€” a HIPAA violation waiting to happen), and contract blindness (Newman cannot tell you when a provider team's schema change breaks every downstream consumer). This platform solves all three, each with the right tool. + +**2. Why client/test separation matters** +REST Assured client classes contain zero assertions. They return `ValidatableResponse`. Test classes contain zero HTTP configuration. This is the API equivalent of the Page Object Model. When an endpoint changes, you update one client class. When an assertion changes, you update one test class. Engineers who violate this boundary create maintenance debt that compounds across every test class that touches that endpoint. + +**3. What Pact solves that functional testing cannot** +A Karate scenario that asserts `status 200` and checks a few fields will pass even if the provider renames a field your consumer depends on. Pact inverts the testing direction: the consumer declares what minimum shape it needs, the provider is required to satisfy that declaration before merging. The Patient API team cannot rename `name` to `fullName` without first failing `PatientProviderTest` on their own PR. The conversation happens between two engineers before a single line ships to staging. + +**4. Pipeline gate logic β€” what runs when and why** +Functional tests run on PR because they're fast and scoped to the changed service. Pact consumer runs after functional tests because a contract generated from broken code encodes the bug as a requirement. Pact provider runs after consumer because the pact JSON artifact must exist. Staging smoke runs post-deploy to validate the deployment itself β€” DNS, ingress, secrets injection β€” not the code, which was already validated before the artifact was promoted. + +**5. How to answer "did you write this?"** +Yes. Walk through these specifics: the `FailureOnlyLoggingFilter` in `ApiConfig` is a static inner class that logs at ERROR only when `response.statusCode() >= 400` β€” silent in the passing case. The Pact V3 format is explicit because Pact 4.6.7 defaults to V4 which requires a different DSL. The two ordered Surefire executions in `pact/pom.xml` exist because alphabetical ordering puts `PatientProviderTest` before `PrescriptionConsumerTest` β€” provider verification would fail on a missing artifact. The `-DfailIfNoSpecifiedTests=false` flag in CI is required when filtering by class name across a multi-execution Surefire config. These are not decisions you make by reading documentation β€” they come from debugging a real build. + +--- + +### Customization Guide + +**TypeScript stack (swap REST Assured for Playwright API)** +Replace the `rest-assured` module with a Node.js module using Playwright's `APIRequestContext`. Keep Karate for BDD scenarios (Karate runs on JVM but can be used alongside a TypeScript build). Replace Pact JVM with `@pact-foundation/pact` (same consumer-driven model, TypeScript DSL). The GitHub Actions jobs stay structurally identical β€” only the runtime and test commands change. + +**Python stack (swap REST Assured for Requests + pytest)** +Replace `rest-assured` with a Python module using `requests` and `pytest`. Use `pytest-pact` for contract testing. Karate can stay as the BDD governance layer (cross-language capability is a Karate strength). The client/test separation pattern applies directly: `client.py` classes contain no assertions, `test_*.py` files contain no HTTP configuration. + +**Non-Java teams (Karate becomes primary layer)** +If the QE team cannot maintain Java code, collapse REST Assured into Karate. Karate's native HTTP client handles most functional validation scenarios. Retain Pact β€” it is the highest-value layer and the one with no adequate alternative. Limit Java ownership to `KarateRunner.java` and the Pact test classes; everything else is `.feature` files and `karate-config.js`. + +**Adding k6 performance layer** +Add a fourth module `k6/` at the root. k6 scripts are JavaScript, so this module holds the scripts and a Maven exec plugin that invokes the k6 binary. Add a fifth job to `pr-quality-gate.yml` β€” `k6-performance` β€” that runs after `rest-assured-functional` with a threshold: p95 < 500ms. This gate runs on PR, not post-deploy, for the same reason Pact runs on PR: catching a regression before merge costs a conversation, catching it post-deploy costs a rollback. From 69e1a324dd5d5a4db06eb9836a49cab9eb61a045 Mon Sep 17 00:00:00 2001 From: Karthik Loganathan Date: Sat, 18 Apr 2026 23:20:37 -0400 Subject: [PATCH 07/12] docs: update skill with k6 fourth layer, two-stage performance model --- SKILL.md | 149 +++++++++++++++++++++++++-- docs/CLAUDE_SKILL_API_QE_PLATFORM.md | 149 +++++++++++++++++++++++++-- 2 files changed, 286 insertions(+), 12 deletions(-) diff --git a/SKILL.md b/SKILL.md index c4ed288..4ab1d9f 100644 --- a/SKILL.md +++ b/SKILL.md @@ -6,7 +6,7 @@ ### Purpose -This skill builds a production-quality, three-layer API testing reference architecture on a Java/Maven multi-module project. Use it when replacing Postman/Newman in a CI pipeline, demonstrating enterprise QE platform thinking, or establishing a governed, code-reviewed testing foundation for a Java microservices org. The output is a fully working Maven project with REST Assured (functional validation), Karate DSL (BDD governance), and Pact (consumer-driven contract testing), wired together in a GitHub Actions pipeline with a 4-job dependency chain. Every layer is independently buildable and independently runnable against any REST API. +This skill builds a production-quality, four-layer API testing reference architecture on a Java/Maven multi-module project. Use it when replacing Postman/Newman in a CI pipeline, demonstrating enterprise QE platform thinking, or establishing a governed, code-reviewed testing foundation for a Java microservices org. The output is a fully working Maven project with REST Assured (functional validation), Karate DSL (BDD governance), Pact (consumer-driven contract testing), and k6 (two-stage performance engineering), wired together in a GitHub Actions pipeline with a 7-job dependency chain with two-stage performance gates. Every layer is independently buildable and independently runnable against any REST API. --- @@ -31,6 +31,7 @@ Replace these values throughout all prompts before using them. | `PROVIDER_NAME` | `PatientService` | Pact provider service name (the service receiving calls) | | `TARGET_API` | `https://jsonplaceholder.typicode.com` | Mock API base URL used in CI and local runs | | `DOMAIN` | `prescription/patient` | Business domain for test naming, feature file directories, and scenario language | +| `PERF_SCHEDULE` | `0 2 * * 1` | Cron schedule for stress test (Monday 2AM UTC) | --- @@ -484,6 +485,135 @@ pipeline-design.md must cover: --- +### Prompt 8 β€” k6 Two-Stage Performance Layer + +``` +Add a two-stage k6 performance testing layer to PROJECT_NAME. +k6 is JavaScript β€” not a Maven module. Do not add it to pom.xml. + +Create this folder structure: +k6/ +β”œβ”€β”€ README.md +β”œβ”€β”€ config/ +β”‚ β”œβ”€β”€ thresholds.js +β”‚ └── environments.js +β”œβ”€β”€ component/ +β”‚ β”œβ”€β”€ patient-lookup.js +β”‚ β”œβ”€β”€ prescription-api.js +β”‚ └── cart-api.js +β”œβ”€β”€ system/ +β”‚ β”œβ”€β”€ prescription-checkout-load.js +β”‚ └── prescription-checkout-stress.js +└── reports/ + └── .gitkeep + +k6/config/environments.js: +export const config = { + baseUrl: __ENV.API_BASE_URL || 'TARGET_API', + environment: __ENV.ENVIRONMENT || 'local', +}; + +k6/config/thresholds.js β€” three threshold tiers. +CRITICAL: k6 threshold syntax uses p(95) not p95. +"p95<500" will throw "failed parsing threshold expression" at runtime. +The correct syntax is "p(95)<500". + +export const componentThresholds = { + http_req_duration: ['p(95)<500', 'p(99)<1000'], + http_req_failed: ['rate<0.01'], +}; +export const systemThresholds = { + http_req_duration: ['p(95)<2000', 'p(99)<3000'], + http_req_failed: ['rate<0.01'], + http_reqs: ['rate>10'], +}; +export const stressThresholds = { + http_req_duration: ['p(99)<5000'], + http_req_failed: ['rate<0.05'], +}; + +Component test files (k6/component/): +Three files β€” one per endpoint. Each follows this pattern: +- Import config and componentThresholds +- stages: 15s ramp to 5 VUs β†’ 30s hold β†’ 15s ramp down (60s total) +- Random patientId: Math.floor(Math.random() * 10) + 1 +- patient-lookup.js: GET /users/{id}, check status 200, duration < 500, + id and name present in response body +- prescription-api.js: POST /posts with JSON payload, check status 201, + duration < 500, id returned +- cart-api.js: GET /posts?userId={id}, check status 200, + duration < 500, response is an array +- All checks use r.timings.duration (not r.timings.waiting) +- sleep(1) after each iteration + +System test files (k6/system/): +prescription-checkout-load.js β€” full e2e journey, systemThresholds: + stages: 1m ramp to 20 VUs β†’ 3m hold β†’ 1m ramp down + Three groups in sequence: Patient Record Lookup, Prescription History, + Submit Prescription Refill + Each group: HTTP call, check status + SLA, sleep(Math.random() * 2 + 1) + +prescription-checkout-stress.js β€” break point discovery, stressThresholds: + Add this comment at top: + "Stress test β€” identifies break point and recovery behavior. + Run scheduled, not on every deployment. + Results inform capacity planning and auto-scaling thresholds." + stages: 2mβ†’50 VUs, 3mβ†’100, 2mβ†’200, 2m hold at 200, 1mβ†’0 + Same three-group journey as load test + +Update .github/workflows/pr-quality-gate.yml: +Add three k6 component jobs that run in PARALLEL with rest-assured-functional +and karate-bdd (no dependencies on each other): + + k6-patient-lookup, k6-prescription-api, k6-cart-api β€” each with: + - Install k6 via apt (keyring method, not snap): + sudo gpg -k + sudo gpg --no-default-keyring \ + --keyring /usr/share/keyrings/k6-archive-keyring.gpg \ + --keyserver hkp://keyserver.ubuntu.com:80 \ + --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69 + echo "deb [signed-by=...] https://dl.k6.io/deb stable main" | sudo tee ... + sudo apt-get update && sudo apt-get install k6 + - k6 run --env API_BASE_URL=${{ env.API_BASE_URL }} k6/component/{script}.js + +Update pact-consumer needs array to include all five parallel jobs: + needs: [rest-assured-functional, karate-bdd, k6-patient-lookup, + k6-prescription-api, k6-cart-api] + +Update .github/workflows/staging-smoke.yml: +Add k6-system-load job after staging-smoke: + needs: [staging-smoke] + Install k6, run prescription-checkout-load.js + --out json=k6/reports/load-results.json + upload-artifact: k6/reports/ as k6-load-results + +Create .github/workflows/performance-stress.yml: + on: + schedule: + - cron: 'PERF_SCHEDULE' (literal string β€” NOT an expression) + workflow_dispatch: + inputs: + target_url: (description, required: false, default: TARGET_API) + + CRITICAL: Do not use ${{ vars.X || 'default' }} in the cron field. + GitHub Actions does not evaluate expressions in on.schedule.cron. + Use a literal cron string only. + + One job: stress-test + Install k6, run prescription-checkout-stress.js + --out json=k6/reports/stress-results.json + upload-artifact: k6/reports/ as k6-stress-results + +k6/README.md β€” peer-level, no tutorials. Cover: + - Two-stage model: why component gates on PR vs system load on deploy + - Threshold values and the reasoning behind each (p95 vs p99, 500ms vs 2000ms) + - Running locally (three commands) + - Azure Load Testing production path (reference only, not implemented) + - Pipeline integration table: stage, trigger, scripts, gate behavior +``` + +--- + ### Prompt 7 β€” Validation and Cleanup ``` @@ -537,10 +667,14 @@ After all tests pass: - [ ] Karate: feature files readable by a non-engineer β€” scenario names describe business outcomes, not HTTP operations - [ ] Pact: consumer contract JSON generated at `pact/target/pacts/CONSUMER_NAME-PROVIDER_NAME.json` - [ ] Pact: provider verification calls the real API (check logs β€” no mock server in provider test) -- [ ] Pipeline: 4-job dependency chain correct β€” jobs 1+2 parallel, job 3 waits on both, job 4 waits on job 3 -- [ ] Pipeline: pact artifact download path in job 4 matches `@PactFolder("target/pacts")` +- [ ] Pipeline: 7-job dependency chain correct β€” 5 parallel jobs (REST Assured, Karate, 3Γ—k6 component), then Pact Consumer, then Pact Provider +- [ ] Pipeline: pact artifact download path in job 6 matches `@PactFolder("target/pacts")` +- [ ] k6: p(95) syntax in all threshold expressions β€” `p(95)<500` not `p95<500` +- [ ] performance-stress.yml uses literal cron string β€” not a `${{ vars.X || 'default' }}` expression +- [ ] Five parallel jobs in PR gate before Pact Consumer (`needs` array has all five) +- [ ] Stress test artifacts uploaded after run (`k6/reports/` as k6-stress-results) - [ ] All tests passing locally before any commit -- [ ] YAML syntax valid for both workflow files +- [ ] YAML syntax valid for all three workflow files - [ ] Git log shows clean linear commit history β€” one commit per layer, no fixup commits visible --- @@ -564,6 +698,9 @@ Functional tests run on PR because they're fast and scoped to the changed servic **5. How to answer "did you write this?"** Yes. Walk through these specifics: the `FailureOnlyLoggingFilter` in `ApiConfig` is a static inner class that logs at ERROR only when `response.statusCode() >= 400` β€” silent in the passing case. The Pact V3 format is explicit because Pact 4.6.7 defaults to V4 which requires a different DSL. The two ordered Surefire executions in `pact/pom.xml` exist because alphabetical ordering puts `PatientProviderTest` before `PrescriptionConsumerTest` β€” provider verification would fail on a missing artifact. The `-DfailIfNoSpecifiedTests=false` flag in CI is required when filtering by class name across a multi-execution Surefire config. These are not decisions you make by reading documentation β€” they come from debugging a real build. +**6. Two-stage performance model β€” why component gates on every PR prevent the performance regression problem that only shows up in load testing two weeks before release.** +Component gates run one script per endpoint, 60 seconds each, on every PR. A developer knows within 2 minutes whether their change degraded response time. Without this, performance testing happens as a late-stage activity β€” a load test run against staging two weeks before release, at which point the regression is buried in six sprints of code and root cause takes days to isolate. The system load test on staging validates the full e2e journey under realistic concurrency. The scheduled stress test is break point discovery β€” it informs capacity planning and auto-scaling thresholds, not a pass/fail gate. Three different purposes, three different triggers, none of them redundant. + --- ### Customization Guide @@ -577,5 +714,5 @@ Replace `rest-assured` with a Python module using `requests` and `pytest`. Use ` **Non-Java teams (Karate becomes primary layer)** If the QE team cannot maintain Java code, collapse REST Assured into Karate. Karate's native HTTP client handles most functional validation scenarios. Retain Pact β€” it is the highest-value layer and the one with no adequate alternative. Limit Java ownership to `KarateRunner.java` and the Pact test classes; everything else is `.feature` files and `karate-config.js`. -**Adding k6 performance layer** -Add a fourth module `k6/` at the root. k6 scripts are JavaScript, so this module holds the scripts and a Maven exec plugin that invokes the k6 binary. Add a fifth job to `pr-quality-gate.yml` β€” `k6-performance` β€” that runs after `rest-assured-functional` with a threshold: p95 < 500ms. This gate runs on PR, not post-deploy, for the same reason Pact runs on PR: catching a regression before merge costs a conversation, catching it post-deploy costs a rollback. +**k6 performance layer (already included β€” Prompt 8)** +The k6 layer is built into this skill. Use Prompt 8 as written. Key reminder when adapting: cron schedule in `performance-stress.yml` must be a literal string (`'PERF_SCHEDULE'`), not a GitHub Actions expression β€” expressions are not evaluated in `on.schedule.cron`. Threshold syntax must use `p(95)` not `p95` β€” the shorthand form throws a runtime parse error in k6. diff --git a/docs/CLAUDE_SKILL_API_QE_PLATFORM.md b/docs/CLAUDE_SKILL_API_QE_PLATFORM.md index c4ed288..4ab1d9f 100644 --- a/docs/CLAUDE_SKILL_API_QE_PLATFORM.md +++ b/docs/CLAUDE_SKILL_API_QE_PLATFORM.md @@ -6,7 +6,7 @@ ### Purpose -This skill builds a production-quality, three-layer API testing reference architecture on a Java/Maven multi-module project. Use it when replacing Postman/Newman in a CI pipeline, demonstrating enterprise QE platform thinking, or establishing a governed, code-reviewed testing foundation for a Java microservices org. The output is a fully working Maven project with REST Assured (functional validation), Karate DSL (BDD governance), and Pact (consumer-driven contract testing), wired together in a GitHub Actions pipeline with a 4-job dependency chain. Every layer is independently buildable and independently runnable against any REST API. +This skill builds a production-quality, four-layer API testing reference architecture on a Java/Maven multi-module project. Use it when replacing Postman/Newman in a CI pipeline, demonstrating enterprise QE platform thinking, or establishing a governed, code-reviewed testing foundation for a Java microservices org. The output is a fully working Maven project with REST Assured (functional validation), Karate DSL (BDD governance), Pact (consumer-driven contract testing), and k6 (two-stage performance engineering), wired together in a GitHub Actions pipeline with a 7-job dependency chain with two-stage performance gates. Every layer is independently buildable and independently runnable against any REST API. --- @@ -31,6 +31,7 @@ Replace these values throughout all prompts before using them. | `PROVIDER_NAME` | `PatientService` | Pact provider service name (the service receiving calls) | | `TARGET_API` | `https://jsonplaceholder.typicode.com` | Mock API base URL used in CI and local runs | | `DOMAIN` | `prescription/patient` | Business domain for test naming, feature file directories, and scenario language | +| `PERF_SCHEDULE` | `0 2 * * 1` | Cron schedule for stress test (Monday 2AM UTC) | --- @@ -484,6 +485,135 @@ pipeline-design.md must cover: --- +### Prompt 8 β€” k6 Two-Stage Performance Layer + +``` +Add a two-stage k6 performance testing layer to PROJECT_NAME. +k6 is JavaScript β€” not a Maven module. Do not add it to pom.xml. + +Create this folder structure: +k6/ +β”œβ”€β”€ README.md +β”œβ”€β”€ config/ +β”‚ β”œβ”€β”€ thresholds.js +β”‚ └── environments.js +β”œβ”€β”€ component/ +β”‚ β”œβ”€β”€ patient-lookup.js +β”‚ β”œβ”€β”€ prescription-api.js +β”‚ └── cart-api.js +β”œβ”€β”€ system/ +β”‚ β”œβ”€β”€ prescription-checkout-load.js +β”‚ └── prescription-checkout-stress.js +└── reports/ + └── .gitkeep + +k6/config/environments.js: +export const config = { + baseUrl: __ENV.API_BASE_URL || 'TARGET_API', + environment: __ENV.ENVIRONMENT || 'local', +}; + +k6/config/thresholds.js β€” three threshold tiers. +CRITICAL: k6 threshold syntax uses p(95) not p95. +"p95<500" will throw "failed parsing threshold expression" at runtime. +The correct syntax is "p(95)<500". + +export const componentThresholds = { + http_req_duration: ['p(95)<500', 'p(99)<1000'], + http_req_failed: ['rate<0.01'], +}; +export const systemThresholds = { + http_req_duration: ['p(95)<2000', 'p(99)<3000'], + http_req_failed: ['rate<0.01'], + http_reqs: ['rate>10'], +}; +export const stressThresholds = { + http_req_duration: ['p(99)<5000'], + http_req_failed: ['rate<0.05'], +}; + +Component test files (k6/component/): +Three files β€” one per endpoint. Each follows this pattern: +- Import config and componentThresholds +- stages: 15s ramp to 5 VUs β†’ 30s hold β†’ 15s ramp down (60s total) +- Random patientId: Math.floor(Math.random() * 10) + 1 +- patient-lookup.js: GET /users/{id}, check status 200, duration < 500, + id and name present in response body +- prescription-api.js: POST /posts with JSON payload, check status 201, + duration < 500, id returned +- cart-api.js: GET /posts?userId={id}, check status 200, + duration < 500, response is an array +- All checks use r.timings.duration (not r.timings.waiting) +- sleep(1) after each iteration + +System test files (k6/system/): +prescription-checkout-load.js β€” full e2e journey, systemThresholds: + stages: 1m ramp to 20 VUs β†’ 3m hold β†’ 1m ramp down + Three groups in sequence: Patient Record Lookup, Prescription History, + Submit Prescription Refill + Each group: HTTP call, check status + SLA, sleep(Math.random() * 2 + 1) + +prescription-checkout-stress.js β€” break point discovery, stressThresholds: + Add this comment at top: + "Stress test β€” identifies break point and recovery behavior. + Run scheduled, not on every deployment. + Results inform capacity planning and auto-scaling thresholds." + stages: 2mβ†’50 VUs, 3mβ†’100, 2mβ†’200, 2m hold at 200, 1mβ†’0 + Same three-group journey as load test + +Update .github/workflows/pr-quality-gate.yml: +Add three k6 component jobs that run in PARALLEL with rest-assured-functional +and karate-bdd (no dependencies on each other): + + k6-patient-lookup, k6-prescription-api, k6-cart-api β€” each with: + - Install k6 via apt (keyring method, not snap): + sudo gpg -k + sudo gpg --no-default-keyring \ + --keyring /usr/share/keyrings/k6-archive-keyring.gpg \ + --keyserver hkp://keyserver.ubuntu.com:80 \ + --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69 + echo "deb [signed-by=...] https://dl.k6.io/deb stable main" | sudo tee ... + sudo apt-get update && sudo apt-get install k6 + - k6 run --env API_BASE_URL=${{ env.API_BASE_URL }} k6/component/{script}.js + +Update pact-consumer needs array to include all five parallel jobs: + needs: [rest-assured-functional, karate-bdd, k6-patient-lookup, + k6-prescription-api, k6-cart-api] + +Update .github/workflows/staging-smoke.yml: +Add k6-system-load job after staging-smoke: + needs: [staging-smoke] + Install k6, run prescription-checkout-load.js + --out json=k6/reports/load-results.json + upload-artifact: k6/reports/ as k6-load-results + +Create .github/workflows/performance-stress.yml: + on: + schedule: + - cron: 'PERF_SCHEDULE' (literal string β€” NOT an expression) + workflow_dispatch: + inputs: + target_url: (description, required: false, default: TARGET_API) + + CRITICAL: Do not use ${{ vars.X || 'default' }} in the cron field. + GitHub Actions does not evaluate expressions in on.schedule.cron. + Use a literal cron string only. + + One job: stress-test + Install k6, run prescription-checkout-stress.js + --out json=k6/reports/stress-results.json + upload-artifact: k6/reports/ as k6-stress-results + +k6/README.md β€” peer-level, no tutorials. Cover: + - Two-stage model: why component gates on PR vs system load on deploy + - Threshold values and the reasoning behind each (p95 vs p99, 500ms vs 2000ms) + - Running locally (three commands) + - Azure Load Testing production path (reference only, not implemented) + - Pipeline integration table: stage, trigger, scripts, gate behavior +``` + +--- + ### Prompt 7 β€” Validation and Cleanup ``` @@ -537,10 +667,14 @@ After all tests pass: - [ ] Karate: feature files readable by a non-engineer β€” scenario names describe business outcomes, not HTTP operations - [ ] Pact: consumer contract JSON generated at `pact/target/pacts/CONSUMER_NAME-PROVIDER_NAME.json` - [ ] Pact: provider verification calls the real API (check logs β€” no mock server in provider test) -- [ ] Pipeline: 4-job dependency chain correct β€” jobs 1+2 parallel, job 3 waits on both, job 4 waits on job 3 -- [ ] Pipeline: pact artifact download path in job 4 matches `@PactFolder("target/pacts")` +- [ ] Pipeline: 7-job dependency chain correct β€” 5 parallel jobs (REST Assured, Karate, 3Γ—k6 component), then Pact Consumer, then Pact Provider +- [ ] Pipeline: pact artifact download path in job 6 matches `@PactFolder("target/pacts")` +- [ ] k6: p(95) syntax in all threshold expressions β€” `p(95)<500` not `p95<500` +- [ ] performance-stress.yml uses literal cron string β€” not a `${{ vars.X || 'default' }}` expression +- [ ] Five parallel jobs in PR gate before Pact Consumer (`needs` array has all five) +- [ ] Stress test artifacts uploaded after run (`k6/reports/` as k6-stress-results) - [ ] All tests passing locally before any commit -- [ ] YAML syntax valid for both workflow files +- [ ] YAML syntax valid for all three workflow files - [ ] Git log shows clean linear commit history β€” one commit per layer, no fixup commits visible --- @@ -564,6 +698,9 @@ Functional tests run on PR because they're fast and scoped to the changed servic **5. How to answer "did you write this?"** Yes. Walk through these specifics: the `FailureOnlyLoggingFilter` in `ApiConfig` is a static inner class that logs at ERROR only when `response.statusCode() >= 400` β€” silent in the passing case. The Pact V3 format is explicit because Pact 4.6.7 defaults to V4 which requires a different DSL. The two ordered Surefire executions in `pact/pom.xml` exist because alphabetical ordering puts `PatientProviderTest` before `PrescriptionConsumerTest` β€” provider verification would fail on a missing artifact. The `-DfailIfNoSpecifiedTests=false` flag in CI is required when filtering by class name across a multi-execution Surefire config. These are not decisions you make by reading documentation β€” they come from debugging a real build. +**6. Two-stage performance model β€” why component gates on every PR prevent the performance regression problem that only shows up in load testing two weeks before release.** +Component gates run one script per endpoint, 60 seconds each, on every PR. A developer knows within 2 minutes whether their change degraded response time. Without this, performance testing happens as a late-stage activity β€” a load test run against staging two weeks before release, at which point the regression is buried in six sprints of code and root cause takes days to isolate. The system load test on staging validates the full e2e journey under realistic concurrency. The scheduled stress test is break point discovery β€” it informs capacity planning and auto-scaling thresholds, not a pass/fail gate. Three different purposes, three different triggers, none of them redundant. + --- ### Customization Guide @@ -577,5 +714,5 @@ Replace `rest-assured` with a Python module using `requests` and `pytest`. Use ` **Non-Java teams (Karate becomes primary layer)** If the QE team cannot maintain Java code, collapse REST Assured into Karate. Karate's native HTTP client handles most functional validation scenarios. Retain Pact β€” it is the highest-value layer and the one with no adequate alternative. Limit Java ownership to `KarateRunner.java` and the Pact test classes; everything else is `.feature` files and `karate-config.js`. -**Adding k6 performance layer** -Add a fourth module `k6/` at the root. k6 scripts are JavaScript, so this module holds the scripts and a Maven exec plugin that invokes the k6 binary. Add a fifth job to `pr-quality-gate.yml` β€” `k6-performance` β€” that runs after `rest-assured-functional` with a threshold: p95 < 500ms. This gate runs on PR, not post-deploy, for the same reason Pact runs on PR: catching a regression before merge costs a conversation, catching it post-deploy costs a rollback. +**k6 performance layer (already included β€” Prompt 8)** +The k6 layer is built into this skill. Use Prompt 8 as written. Key reminder when adapting: cron schedule in `performance-stress.yml` must be a literal string (`'PERF_SCHEDULE'`), not a GitHub Actions expression β€” expressions are not evaluated in `on.schedule.cron`. Threshold syntax must use `p(95)` not `p95` β€” the shorthand form throws a runtime parse error in k6. From c53a95465288f2fc7ce9eecfcdeeb0d9efb1a8f3 Mon Sep 17 00:00:00 2001 From: Karthik Loganathan Date: Sun, 19 Apr 2026 08:45:39 -0400 Subject: [PATCH 08/12] docs: add current state Postman assessment - enterprise scale limitations --- docs/CURRENT_STATE_POSTMAN.md | 85 +++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 docs/CURRENT_STATE_POSTMAN.md diff --git a/docs/CURRENT_STATE_POSTMAN.md b/docs/CURRENT_STATE_POSTMAN.md new file mode 100644 index 0000000..c532995 --- /dev/null +++ b/docs/CURRENT_STATE_POSTMAN.md @@ -0,0 +1,85 @@ +# Current State β€” Postman/Newman at Enterprise Scale + +## What Postman Does Well + +Postman is genuinely excellent at what it was designed to do: API exploration, interactive documentation, and functional validation for small teams. For a team of two to five engineers iterating on a single service, Postman collections are fast to write, easy to share, and Newman extends that work cleanly into CLI execution. The feedback loop is tight, the tooling is approachable, and for early-stage API development, nothing gets you from zero to a working test suite faster. This is not a criticism of Postman. It is a recognition that the tool was built for a specific problem space, and it solves that problem well. + +--- + +## Where It Breaks + +### 1. Collection Governance at Team Scale + +Collections are JSON files. At small scale, this is fine. At enterprise scale β€” ten teams, forty microservices, dozens of contributors β€” JSON becomes the governance problem. + +Merge conflicts on collection files are common and difficult to resolve meaningfully. Two engineers add tests to the same collection in the same sprint. The resulting diff is hundreds of lines of nested JSON. There is no enforceable standard for assertion quality, naming conventions, or structural patterns. A test that asserts `pm.response.code === 200` and nothing else passes code review because there is no review surface that makes the inadequacy obvious. + +Newman CI integration looks like this: + +```bash +newman run collection.json -e environment.json \ + --reporters cli,junit \ + --reporter-junit-export results.xml +``` + +For one team, this is sufficient. For ten teams, it means ten independently maintained collection files with no shared standards, no shared assertion libraries, and no mechanism to enforce consistency across services. Pipeline output is a JUnit XML file. What it does not tell you is whether the tests inside that file were actually asserting anything meaningful. + +--- + +### 2. Environment Variable Management + +Environment files are JSON. Variables are frequently hardcoded into collections. There is no type safety, no schema validation, and no enforcement mechanism to ensure that a variable referenced in a collection actually exists in the environment file being used. + +Secrets management is manual discipline. Engineers copy staging credentials into local environment files. Production credentials live in a separate file β€” or they do not, until they are needed. The pipeline breaks because one engineer updated the staging environment file and pushed. The production file was not updated. No one noticed until the post-deploy smoke test failed at 11pm. + +This is not a hypothetical. This is the failure mode that happens in every organization that runs Postman at scale long enough. + +--- + +### 3. Contract Blindness Between Microservices + +Newman validates API responses against expectations written at the time the collection was authored. It cannot validate service-to-service contracts β€” the implicit agreements between services about what shape data will take as it moves through a distributed system. + +A concrete example: `PatientService` returns a user object with an `email` field. `PrescriptionService` consumes that field downstream. A developer on the Patient team renames `email` to `emailAddress` β€” a reasonable change, properly documented in their service's Swagger file. The `PrescriptionService` Newman collection continues to pass. Its tests never looked at that field. Integration testing catches the breakage two weeks later in a staging environment. The root cause is buried in six sprints of commits across two repositories, owned by two teams. + +Newman had no mechanism to surface this. It was not designed to. Contract testing requires a different architectural layer entirely. + +--- + +### 4. Shift-Left Gap + +In most organizations running Postman at scale, collections are owned by QE, not developers. The developer builds the API. QE builds the collection. The hand-off introduces a quality gap that is structural, not accidental: the test is never in the same sprint as the feature. By the time QE has authored and validated the collection, the developer has moved on. Feedback is delayed. Defects are more expensive to fix. + +This directly contradicts the shift-left mandate that most engineering organizations have adopted as policy. Postman's tooling does not prevent shift-right testing β€” it subtly encourages it by making QE the natural owner of the collection authoring workflow. + +--- + +### 5. No Performance Signal + +Newman has no performance testing capability. Performance is a separate tool, a separate phase, and in most organizations, a separate team. The result is that performance regressions are invisible in the CI pipeline. A change that doubles the p99 latency on a critical endpoint ships through the pipeline without any signal. The regression surfaces during load testing two weeks before the release date, when remediation options are limited and pressure is highest. + +There is no architectural reason performance signal has to live outside the pipeline. It is a tooling gap, not an engineering constraint. + +--- + +## The Organizational Cost + +The maintenance burden of a Postman-at-scale environment grows linearly with team size. Every new microservice added to the portfolio adds a new collection to govern, a new environment file to maintain, and a new QE workstream to staff. Pipeline confidence erodes as collections drift out of sync with the APIs they are supposed to validate β€” tests pass because they are asserting against outdated assumptions, not because the service is behaving correctly. QE, rather than accelerating delivery, becomes a bottleneck: holding sprint velocity back while collections are authored, reviewed, and stabilized. This is the organizational cost that does not appear on any dashboard until it is already embedded in the team's delivery rhythm. + +--- + +## What the Solution Looks Like + +The reference architecture at [github.com/carthikl/api-quality-platform-reference](https://github.com/carthikl/api-quality-platform-reference) addresses each of these gaps with a four-layer approach built for team scale, CI/CD integration, and contract protection. It replaces collection governance chaos with code-reviewed TypeScript, adds Pact-based contract validation between services, and embeds k6 performance signal directly into the PR gate β€” before merge, not before release. It was architected and directed over a weekend using Claude Code β€” the same AI-native productivity model I would bring to the Walgreens QE function. + +--- + +## A Note on Postman's Role Going Forward + +Postman stays. Its role narrows. + +- **Exploration** β€” engineers use it to understand unfamiliar APIs during development +- **Documentation** β€” collections serve as living, interactive API documentation for the team +- **Environment smoke** β€” Newman runs 5-minute post-deploy smoke tests to confirm a deployment landed correctly + +That is the right job for Postman. Not the primary quality gate in the pipeline. From 70308a30989773ab3f657fbfca4700af81410036 Mon Sep 17 00:00:00 2001 From: Karthik Loganathan Date: Sun, 19 Apr 2026 10:30:27 -0400 Subject: [PATCH 09/12] docs: add complete test pyramid with service virtualization gap analysis --- docs/TEST_PYRAMID.md | 88 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 docs/TEST_PYRAMID.md diff --git a/docs/TEST_PYRAMID.md b/docs/TEST_PYRAMID.md new file mode 100644 index 0000000..35378ec --- /dev/null +++ b/docs/TEST_PYRAMID.md @@ -0,0 +1,88 @@ +# Test Pyramid β€” Complete Quality Engineering Strategy + +## The Principle + +The testing pyramid is not a tool selection framework. It is a risk distribution model. Each layer catches a different category of defect at the lowest possible cost β€” and cost is measured in time, compute, and feedback delay, not just money. The higher the layer, the more expensive the defect it catches: a cross-service business flow failure found in staging costs an order of magnitude more to diagnose and remediate than a contract violation caught at PR merge. The lower the layer, the faster the signal returns to the developer. Designing a quality engineering strategy means deliberately placing each category of risk at the layer where it can be caught earliest and cheapest. + +--- + +## The Complete Pyramid + +``` + β–² + /|\ + / | \ + / | \ + / E2E/ \ + / Integration\ + / (Karate @smoke)\ + /___________________\ + / \ + / Service Virtualization\ + / (Hoverfly / WireMock) \ + /____________________________\ + / \ + / Contract Testing \ + / (Pact) \ + /______________________________________\ + / \ + / Component Functional Testing \ + / (REST Assured + Karate) \ + /______________________________________________ \ + / \ + / Performance Engineering \ + / (k6) \ + /______________________________________________________ \ +/ \ +/ Unit Testing \ +/ (Developer Framework) \ +/___________________________________________________________\ +``` + +--- + +## What Each Layer Catches + +| Layer | Tool | Defect Category | When It Runs | +|---|---|---|---| +| E2E Integration | Karate `@smoke` | Cross-service business flow failures | Staging deploy | +| Service Virtualization | Hoverfly / WireMock | Integration failures in isolation | PR + staging | +| Contract Testing | Pact | Interface contract violations | PR + deploy | +| Component Functional | REST Assured + Karate | Endpoint behavior regressions | Every PR | +| Performance Engineering | k6 | SLA violations at component and system level | PR + staging + scheduled | +| Unit Testing | Developer framework | Logic and calculation errors | Every commit | + +--- + +## What This Repository Implements + +Four of the six layers are implemented and running in the pipeline: + +- **Component Functional** β€” REST Assured + Karate βœ… +- **Contract Testing** β€” Pact consumer/provider βœ… +- **Performance Engineering** β€” k6 two-stage βœ… +- **E2E Integration** β€” Karate `@smoke` on staging βœ… + +--- + +## The Gap β€” Service Virtualization + +Service virtualization sits between component testing and full integration testing. It allows services to be tested in combination without all dependencies being live β€” catching integration failures in isolation, before a full staging environment is required. + +**Hoverfly** is lightweight, Go-based, and designed for microservices. It captures real traffic and replays it as stubs, making it CI/CD native with minimal configuration overhead. Ideal for Java/Kubernetes environments where teams want fast, low-friction dependency stubbing at the network layer. + +**WireMock** is Java-native with a deep enterprise ecosystem. More configuration overhead than Hoverfly, but stronger IDE integration, broader community adoption, and more expressive stubbing capabilities for complex scenarios. + +**Why not implemented here:** JSONPlaceholder does not require virtualization β€” it is already a stub service. In a production Walgreens implementation, Hoverfly would virtualize the `PatientService` dependency so `PrescriptionService` can be tested in isolation without `PatientService` being deployed. A developer changing `PrescriptionService` business logic would run against a Hoverfly stub of `PatientService` in the PR pipeline β€” catching integration failures before the branch ever reaches staging. That is the production pattern. It is not demonstrated in this reference architecture because the reference uses a public mock API that eliminates the need for it. + +--- + +## Inter-Service Functional Testing + +The question of how to test functional behavior between microservices beyond contract validation is answered by three layers working together, not one. Pact validates the interface agreement β€” field names, types, and response structure β€” ensuring that a change to `PatientService` that renames `email` to `emailAddress` is caught before it reaches any downstream consumer. Hoverfly or WireMock then validates functional behavior with stubbed dependencies: business logic, error handling, and edge cases that require a dependency to be present but not necessarily live. Finally, Karate `@smoke` validates the full end-to-end journey with real services on staging, confirming that the system behaves correctly as an integrated whole under realistic conditions. Each layer is necessary. None is sufficient alone. + +--- + +## The Operating Principle + +"The pyramid is not a QA artifact. It is an engineering discipline β€” owned by the squads, governed by the platform, measured by the outcomes." From e7b38f6a74dab83987999fbc31be3ec8da0301ee Mon Sep 17 00:00:00 2001 From: Karthik Loganathan Date: Sun, 19 Apr 2026 10:50:03 -0400 Subject: [PATCH 10/12] docs: add navigation links to README, update arch decision and pipeline design with k6 --- README.md | 12 ++++++++++++ docs/architecture-decision.md | 1 + docs/pipeline-design.md | 19 +++++++++++++++---- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2066fd1..1c15ee2 100644 --- a/README.md +++ b/README.md @@ -230,6 +230,18 @@ The goal is not to eliminate Postman from engineers' desktops. The goal is to re --- +## Documentation + +| Document | Purpose | +|---|---| +| [Architecture Decision](docs/architecture-decision.md) | Why each layer β€” trade-offs and alternatives considered | +| [Pipeline Design](docs/pipeline-design.md) | What runs when and why β€” the gate logic | +| [Current State β€” Postman](docs/CURRENT_STATE_POSTMAN.md) | Enterprise-scale limitations of Postman/Newman | +| [Test Pyramid](docs/TEST_PYRAMID.md) | Complete testing strategy including service virtualization gap | +| [Build Story](docs/BUILD_STORY.md) | How this was built β€” AI-native productivity model | + +--- + ## Use This Skill This repository was built using a reusable Claude Code skill. The full prompt template β€” including all seven prompts, verification checklist, customization guide, and talking points β€” is available at: diff --git a/docs/architecture-decision.md b/docs/architecture-decision.md index cad1f23..439979b 100644 --- a/docs/architecture-decision.md +++ b/docs/architecture-decision.md @@ -23,6 +23,7 @@ Three-layer Maven multi-module platform, each layer solving exactly one of the a - **REST Assured** β€” deep payload validation, auth flows, and stateful sequences that require Java logic - **Karate** β€” governed BDD scenarios reviewable by non-engineers; test changes are PR-gated artifacts, not personal account exports - **Pact** β€” consumer-driven contracts that surface schema drift between services at PR time, not in production +- **k6** β€” two-stage performance engineering; performance caught late in the cycle --- diff --git a/docs/pipeline-design.md b/docs/pipeline-design.md index 77db114..54990e5 100644 --- a/docs/pipeline-design.md +++ b/docs/pipeline-design.md @@ -14,10 +14,11 @@ A test in the wrong gate is quality theater. Catching a contract violation post- |-----|-------------------|----------| | REST Assured | HTTP behavior, schema, SLA for the changed service | Fastest signal on functional regressions; scoped to the module under change | | Karate @smoke | Critical paths across service boundaries | Readable by the PR reviewer; catches cross-service breakage | +| k6 component tests | p(95)<500ms threshold per endpoint β€” one script per endpoint | Performance regression caught at PR, not load test; runs in parallel with REST Assured and Karate | | Pact consumer | Generates the contract artifact from the changed consumer code | Contract must come from known-good code β€” functional tests gate this | | Pact provider | Verifies the provider still satisfies all consumer contracts | A provider change that breaks a downstream consumer is blocked here, not in production | -Jobs 1 and 2 run in parallel. Job 3 waits on both. Job 4 waits on Job 3. Wall time under 5 minutes. +Jobs 1, 2, and 3 (REST Assured, Karate, and k6 component tests) run in parallel. Five parallel jobs must pass before Pact Consumer runs. Pact Provider waits on Pact Consumer. Wall time under 5 minutes. **Merge block is absolute.** Any failure blocks merge. No bypass without QE Director approval. @@ -33,11 +34,21 @@ Nothing additional runs. The PR gate is the quality gate. Triggering the same te **Workflow:** `staging-smoke.yml` | **Trigger:** `workflow_dispatch` (initiated by deployment pipeline post-rollout) -Runs Karate `@smoke` scenarios only against the live staging endpoint. +Runs Karate `@smoke` scenarios against the live staging endpoint, followed by the k6 system load test β€” the full prescription checkout journey at load, with a p(95)<2000ms threshold enforced end-to-end. -This gate validates the **deployment**, not the code: DNS resolution, ingress routing, service mesh config, secrets injection. The code was already validated before the artifact was promoted. REST Assured and Pact do not re-run here β€” they would prove nothing new. +This gate validates the **deployment**, not the code: DNS resolution, ingress routing, service mesh config, secrets injection, and system-level performance under realistic traffic. The code was already validated before the artifact was promoted. REST Assured and Pact do not re-run here β€” they would prove nothing new. -If smoke fails, staging is unhealthy. Stop deployments until resolved. +If smoke fails, staging is unhealthy. If k6 system load fails, the deployment is functionally live but not performance-safe. Stop promotions to production until resolved. + +--- + +## Scheduled β€” Performance Stress + +**Workflow:** `performance-stress.yml` | **Trigger:** Scheduled Monday 2AM UTC, on-demand via `workflow_dispatch` + +Runs the k6 stress test β€” ramping to 200 VUs for break point discovery. This is not a regression gate; it is a capacity planning signal. The test identifies the point at which the system begins to degrade, so that capacity decisions are data-driven rather than estimated. + +Results are uploaded as a pipeline artifact and retained for trend comparison across runs. On-demand dispatch supports ad-hoc capacity validation before major release events or infrastructure changes. --- From 6b49ac8d37a39fe0609c4f4017a5be663008800c Mon Sep 17 00:00:00 2001 From: Karthik Loganathan Date: Sun, 19 Apr 2026 11:54:10 -0400 Subject: [PATCH 11/12] docs: update pipeline dependency chain to include k6 five-job parallel gate --- docs/pipeline-design.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/pipeline-design.md b/docs/pipeline-design.md index 54990e5..13819c8 100644 --- a/docs/pipeline-design.md +++ b/docs/pipeline-design.md @@ -56,8 +56,14 @@ Results are uploaded as a pipeline artifact and retained for trend comparison ac ``` rest-assured-functional ──┐ - β”œβ”€β”€β†’ pact-consumer ──→ pact-provider -karate-bdd β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ +karate-bdd ────────────────┼──→ pact-consumer ──→ pact-provider + β”‚ +k6-patient-lookup ────────── + β”‚ +k6-prescription-api ──────── + β”‚ +k6-cart-api β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` Pact consumer runs after functional tests because a contract generated from broken code is wrong by definition β€” it encodes the bug as a requirement and trains the provider to satisfy incorrect expectations. The sequence is not a convenience; it is a correctness constraint. From d02b9023e637a1f8bcfa7c08d6358fba7a98e4b3 Mon Sep 17 00:00:00 2001 From: Karthik Loganathan Date: Sun, 19 Apr 2026 22:42:14 -0400 Subject: [PATCH 12/12] feat: k6 Maven integration via exec-maven-plugin - governance parity with Java layers --- .github/workflows/pr-quality-gate.yml | 20 ++++++++ k6/pom.xml | 66 +++++++++++++++++++++++++++ pom.xml | 12 +++++ 3 files changed, 98 insertions(+) create mode 100644 k6/pom.xml diff --git a/.github/workflows/pr-quality-gate.yml b/.github/workflows/pr-quality-gate.yml index 5857ac1..e328fc4 100644 --- a/.github/workflows/pr-quality-gate.yml +++ b/.github/workflows/pr-quality-gate.yml @@ -109,6 +109,15 @@ jobs: sudo apt-get update sudo apt-get install k6 + # Java is required for the Maven execution path below. Cached Maven deps + # are reused across all jobs that also hit the reactor. + - name: Set up Java ${{ env.JAVA_VERSION }} + uses: actions/setup-java@v4 + with: + java-version: ${{ env.JAVA_VERSION }} + distribution: temurin + cache: maven + # Component performance gate β€” validates patient lookup endpoint SLA on # every PR. Catches regressions at the point they are introduced. - name: Run patient lookup component test @@ -117,6 +126,17 @@ jobs: --env API_BASE_URL=${{ env.API_BASE_URL }} \ k6/component/patient-lookup.js + # Governance parity: invoke the same k6 script through Maven so the + # reactor entrypoint (`mvn test`) behaves identically to the direct k6 + # command above. Runs alongside the direct invocation rather than + # replacing it, to prove the two paths produce the same result while + # the rest of the k6 modules are on-boarded. Once every k6 component + # has a Maven binding, the direct `k6 run` step will retire and CI + # will drive all test layers (REST Assured, Karate, Pact, k6) through + # a single reactor command β€” the operating model transformation goal. + - name: Run patient lookup component test via Maven + run: mvn test -pl k6 -Dapi.base.url=${{ env.API_BASE_URL }} + k6-prescription-api: name: k6 β€” Prescription API Component runs-on: ubuntu-latest diff --git a/k6/pom.xml b/k6/pom.xml new file mode 100644 index 0000000..a98a743 --- /dev/null +++ b/k6/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + + com.wag.qe + api-quality-platform-reference + 1.0.0-SNAPSHOT + + + k6 + pom + + Walgreens API Quality Platform β€” k6 Component Performance + + k6 performance component tests, invoked through Maven via exec-maven-plugin. + Binding k6 to the standard test phase gives `mvn test -pl k6` the same semantics + as every other test module in this repo β€” governance parity across layers. + + + + + + + org.codehaus.mojo + exec-maven-plugin + + + k6-patient-lookup + test + + exec + + + k6 + ${project.basedir}/.. + + run + k6/component/patient-lookup.js + + + + ${api.base.url} + + + + + + + + diff --git a/pom.xml b/pom.xml index 6eed2e8..ff44406 100644 --- a/pom.xml +++ b/pom.xml @@ -19,6 +19,7 @@ rest-assured karate pact + k6 @@ -48,6 +49,7 @@ 3.2.5 3.2.5 3.12.1 + 3.1.0 https://jsonplaceholder.typicode.com @@ -155,6 +157,16 @@ maven-failsafe-plugin ${maven-failsafe.version} + + + org.codehaus.mojo + exec-maven-plugin + ${exec-maven-plugin.version} +