Claude Skill

api-test-restassure

Use this skill when you need to parse multi-format API definitions and generate Rest Assured Java test classes; triggers include Rest Assured, RestAssured, and Java API automation.

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

Full trust report

Download naodeng-awesome-qa-skills-skills_en_testing-types_api-test-restassure-c44b892.zip · 18 KB
Part of naodeng/awesome-qa-skills — 97 skills

Install

skills CLI npx skills add https://github.com/naodeng/awesome-qa-skills/tree/main/skills/en/testing-types/api-test-restassure
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install naodeng-awesome-qa-skills@llmmart
Git git clone https://github.com/naodeng/awesome-qa-skills.git

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

README

api-test-restassure (EN)

Skill Overview

Need API outputs that should land in REST Assured based automation; The project is Java-based or already uses REST Assured.

How to Use

  1. Open SKILL.md in this folder and confirm this skill fits your task.
  2. In your AI tool, call @skill api-test-restassure, then add your real project context and goal.
  3. If you need a specific output format (table, checklist, report), include it directly in your request.

One-Click Install Script

Run from the repository root:

macOS / Linux

bash ./scripts/install-skills-mac.sh --tool codex --lang en --skill api-test-restassure

Windows PowerShell

powershell -ExecutionPolicy Bypass -File .\scripts\install-skills-windows.ps1 -Tool codex -Lang en -Skill api-test-restassure

Skill manifest

api-test-restassure (EN)

Chinese version: See the corresponding Chinese skill.

When to Use

  • Need API outputs that should land in REST Assured based automation.
  • The project is Java-based or already uses REST Assured.

Workflow

  1. Read and follow the main prompt listed under Progressive disclosure (coverage, structure, quality bar).
  2. Add only project context that changes the result: scope, environment, constraints, risks, dependencies, expected deliverable.
  3. If input is incomplete, return a usable first draft and explicitly mark assumptions and gaps.
  4. Default to Markdown; switch formats only when the user asks.

Core Constraints

  • Prioritize by risk / business impact — do not treat everything equally.
  • Separate confirmed facts from current assumptions.
  • Do not invent endpoints, fields, environments, or root causes the user did not provide.
  • Use placeholders or env-var semantics for auth/secrets; never hardcode real credentials.
  • Keep output executable: concrete scenarios, clear priority, clear next steps.

Progressive Disclosure

  • Before producing output, read and follow prompts/api-test-restassure.md (minimum coverage, output structure, quality bar).
  • When a ready-made template fits: use matching files under output-templates/.
  • When the user wants examples or alignment with existing assets: read relevant examples/.
  • For deep framework/troubleshoot/schema notes: read only the relevant file(s) under references/, do not load the whole directory.
  • For format conversion or helper checks: prefer existing scripts/ over reinventing.
  • For evaluating/regressing this skill: use evals/ with skill-up.

Pre-delivery Checklist

  • Followed the main prompt's output structure
  • Minimum coverage focus: suite structure, common setup, auth handling, priority endpoints, positive scenarios, negative and boundary scenarios, assertion focus, test data strategy, ... (details in main prompt)
  • Covered the minimum checklist, or explained omissions
  • High-risk items have explicit priority
  • Did not invent details the user did not provide
  • Assumptions and gaps are marked

Common Pitfalls

  • Do not pretend completeness when scope/context is missing.
  • Do not treat every item as equally important.
  • Do not skip assumptions and information gaps.
  • Do not dump generic theory unrelated to the current toolchain.
Files (awesome-qa-skills)
  • agents
    • openai.yaml 486 B
      version: 1
      metadata:
        key: "api-test-restassure"
        last_verified: "2026-03-24"
      interface:
        display_name: "API Test RestAssure"
        short_description: "Use this skill when you need to parse multi-format API definitions and generate Rest Assured Java test classes; triggers include Rest Assured, RestAssured, and…"
        default_prompt: "Use api-test-restassure to complete the task with local scripts, prompts, and examples in this skill folder."
      policy:
        allow_implicit_invocation: true
      
  • evals
    • cases
      • basic-success.yaml 902 B
        id: basic-success
        title: "RestAssured: OpenAPI/curl yields suite structure"
        description: |
          With a short OpenAPI and curl snippet, produce REST Assured Java suite/base setup/assertion structure.
        
        input:
          prompt: |
            Use api-test-restassure.
            Env: SIT. Auth: Bearer {{token}} (do not hardcode real tokens). Stack: Java + Maven.
            OpenAPI snippet:
              POST /v1/orders
              GET /v1/orders/{id}
            Desensitized curl:
              curl -X GET https://api.example.com/v1/orders/ORD-1 -H "Authorization: Bearer $TOKEN"
            Produce a REST Assured plan: package layout, BaseApiTest/common setup, priority cases and assertion focus. Avoid long code dumps.
        
        expect:
          must_contain:
            - "REST Assured"
            - "BaseApiTest"
          must_not_contain:
            - "TODO"
            - "I cannot"
        
        judge:
          type: rule_based
          success:
            - output_contains:
                all:
                  - "Task Understanding"
                  - "REST Assured"
        
      • edge-bad-or-neighbor.yaml 735 B
        id: edge-bad-or-neighbor
        title: "RestAssured: broken YAML or pytest neighbor"
        description: |
          With broken YAML input or a pytest neighbor request, correct and stay on REST Assured.
        
        input:
          prompt: |
            I activated api-test-restassure. Input is incomplete YAML (not a valid OpenAPI):
              paths:
                /orders:
                  post:
            And I also asked: write a pytest suite too.
            Call out the incomplete input, still draft a REST Assured plan, and explain why pytest is out of this skill.
        
        expect:
          must_contain:
            - "REST Assured"
            - "pytest"
          must_not_contain:
            - "TODO"
            - "I cannot"
        
        judge:
          type: rule_based
          success:
            - output_contains:
                all:
                  - "REST Assured"
                  - "Open Questions"
        
      • edge-incomplete-input.yaml 663 B
        id: edge-incomplete-input
        title: "RestAssured: one-line ask still yields draft"
        description: |
          With only one sentence and no schema, still draft RestAssured structure and list gaps/assumptions.
        
        input:
          prompt: |
            Use api-test-restassure. I only know: automate points redemption. No OpenAPI and no existing Maven layout notes.
            Give a usable REST Assured draft structure and list required gaps and assumptions.
        
        expect:
          must_contain:
            - "assumption"
            - "Open Questions"
          must_not_contain:
            - "TODO"
            - "I cannot"
        
        judge:
          type: rule_based
          success:
            - output_contains:
                all:
                  - "assumption"
                  - "REST Assured"
        
    • eval.yaml 559 B
      schema_version: v1alpha1
      
      environment:
        type: none
      
      skills:
        - source: local_path
          path: .
      
      engine:
        name: claude_code
        # model is optional; omit to use engine default
        # model:
        #   provider: anthropic
        #   name: claude-sonnet-4-6
      
      cases:
        files:
          - evals/cases/basic-success.yaml
          - evals/cases/edge-incomplete-input.yaml
          - evals/cases/edge-bad-or-neighbor.yaml
        defaults:
          timeout_seconds: 180
          max_turns: 8
          expect:
            exit_code: 0
            must_not_contain:
              - "TODO"
              - "I cannot"
      
      report:
        formats: [json]
      
  • examples
    • bruno
      • sample.bru 137 B · in bundle
    • ci
      • github-actions-restassure.yml 905 B
        name: API RestAssured CI
        
        on:
          workflow_dispatch:
          pull_request:
            paths:
              - "explore/api-test-restassure/**"
        
        jobs:
          test:
            runs-on: ubuntu-latest
            defaults:
              run:
                working-directory: explore/api-test-restassure/scripts/templates/restassured
            steps:
              - name: Checkout
                uses: actions/checkout@v4
        
              - name: Setup Java
                uses: actions/setup-java@v4
                with:
                  distribution: temurin
                  java-version: "17"
                  cache: maven
        
              - name: Run tests
                env:
                  BASE_URL: ${{ secrets.API_BASE_URL }}
                  API_TOKEN: ${{ secrets.API_TOKEN }}
                run: mvn -q test
        
              - name: Upload surefire reports
                if: always()
                uses: actions/upload-artifact@v4
                with:
                  name: surefire-reports
                  path: explore/api-test-restassure/scripts/templates/restassured/target/surefire-reports/**
        
      • Jenkinsfile.restassure 605 B · in bundle
    • sample.curl 212 B · in bundle
    • sample.insomnia.json 502 B
      {
        "_type": "export",
        "__export_format": 4,
        "__export_date": "2026-03-23T00:00:00.000Z",
        "__export_source": "insomnia.desktop.app:v9.0.0",
        "resources": [
          {
            "_id": "req_1",
            "_type": "request",
            "name": "List Products",
            "method": "GET",
            "url": "https://api.example.com/v1/products"
          },
          {
            "_id": "req_2",
            "_type": "request",
            "name": "Create Product",
            "method": "POST",
            "url": "https://api.example.com/v1/products"
          }
        ]
      }
      
    • sample.openapi.yaml 240 B
      openapi: 3.0.3
      info:
        title: Sample API
        version: 1.0.0
      paths:
        /v1/users:
          get:
            summary: list users
          post:
            summary: create user
        /v1/users/{id}:
          get:
            summary: get user
          delete:
            summary: delete user
      
    • sample.opencollection.json 243 B
      {
        "name": "OpenCollection Sample",
        "requests": [
          {
            "name": "Get Orders",
            "method": "GET",
            "path": "/v1/orders"
          },
          {
            "name": "Create Order",
            "method": "POST",
            "path": "/v1/orders"
          }
        ]
      }
      
    • sample.postman_collection.json 704 B
      {
        "info": {
          "name": "Sample API Collection",
          "_postman_id": "d89a0d30-4444-4444-9999-111133335555",
          "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
        },
        "item": [
          {
            "name": "Get Users",
            "request": {
              "method": "GET",
              "url": {
                "raw": "https://api.example.com/v1/users",
                "path": [
                  "v1",
                  "users"
                ]
              }
            }
          },
          {
            "name": "Create User",
            "request": {
              "method": "POST",
              "url": {
                "raw": "https://api.example.com/v1/users",
                "path": [
                  "v1",
                  "users"
                ]
              }
            }
          }
        ]
      }
      
    • sample.wsdl 534 B · in bundle
  • output-templates
    • template-markdown.md 83 B
      # Output Template
      
      ## Summary
      - Scope:
      - Key Points:
      
      ## Details
      - Item 1
      - Item 2
      
  • prompts
    • api-test-restassure.md 5.4 KB
      # API Test REST Assured Prompt
      
      From the materials the user provides, produce a REST Assured (JUnit 5) API automation plan or test-asset structure for direct implementation.
      
      ## Role
      
      - Act as a senior QA and API automation expert who turns API materials into a maintainable Java / REST Assured suite.
      
      
      ## Input parsing order
      
      Parse in this priority order. Higher priority wins on conflicts; when sources disagree, state the conflict and source — **do not silently invent a merged “truth”**:
      
      1. Existing Java test assets (`src/test/java`, base classes, `pom.xml` / Gradle, TestNG/JUnit)
      2. OpenAPI / Swagger
      3. Postman Collection, Insomnia, Bruno, or OpenCollection
      4. curl examples
      5. Loose notes (tables, Markdown, verbal endpoint lists)
      
      Also absorb when present: business scope, auth, environments, release priority, CI, dependency versions.
      
      Extract only paths, methods, params, fields, and sample values that **actually appear** in the materials. Put gaps in “missing information”.
      
      ## Defaults (use these unless the user specifies otherwise)
      
      Prefer defaults; do not present a framework menu.
      
      **Directory layout (Maven default)**
      
      ```text
      src/test/java/com/example/api/
        BaseApiTest.java          # shared RequestSpecification
        <Resource>ApiTest.java    # per resource or critical flow
      src/test/resources/
        test.properties           # non-secret defaults; secrets prefer env vars
      ```
      
      Build: Maven + JUnit 5 + REST Assured by default. If the project already uses Gradle/TestNG, **align to it** — do not force a stack change.
      
      **Naming**
      
      - classes: `PascalCase` + `ApiTest` suffix (e.g. `OrdersApiTest`)
      - methods: `camelCase` behavior (e.g. `createOrderShouldReturn201`, `getUserWithoutTokenShouldReturn401`)
      - package: reuse the project package; otherwise `com.example.api`
      
      **Shared setup and auth**
      
      - `BaseApiTest` builds `RequestSpecification`: `baseUri`, JSON Content-Type, Authorization
      - `BASE_URL` / `API_TOKEN`: prefer `System.getenv`, then `test.properties`; property files may only hold placeholders (`replace-me`) — never real secrets
      - Cases call `given().spec(requestSpec)`
      
      **Assertion style**
      
      - Fluent: `.statusCode(...)` + `.body("field", equalTo(...))` (fields must come from materials)
      - Minimum: status + critical fields; Hamcrest matchers
      - Unknown error bodies: assert status family only and mark the assumption — do not invent errorCode
      
      **Layers (default)**
      
      - JUnit 5 tags: `smoke` / `contract` / `negative`; CI runs `@Tag("smoke")` first
      
      If a base class or layering already exists, **align to it**.
      
      ## Gotchas
      
      - **Never** hardcode real tokens, passwords, or cookies in `test.properties`, sample code, or output.
      - When migrating from curl/Postman: redact sensitive headers.
      - **Do not invent** paths, fields, status codes, or JSON paths the user did not provide.
      - Do not switch to Spring MockMvc / Karate / a non-Java stack unless the user asks.
      - If materials give relative paths without a host, use a placeholder `baseUrl` and list the gap — do not invent a gateway hostname as confirmed fact.
      - If information is incomplete, still ship a usable first version (package layout + Base + confirmed case outlines) and list assumptions.
      - Unless the user asks for runnable files, prefer structure and method outlines over huge full class dumps.
      
      ## Minimum coverage checklist
      
      Unless the user explicitly narrows scope, the result must cover:
      
      - suite / package structure and Base class responsibilities
      - shared `RequestSpecification` and config sources
      - how auth and permission cases are organized
      - high-priority endpoints (P0/P1)
      - positive scenarios
      - negative and boundary scenarios
      - assertion focus (status + body)
      - test-data strategy
      - CI or local runs (including tag filters)
      - missing information and assumptions
      
      ## Output
      
      Return results in this order:
      
      ### 1. Task Understanding
      - API / domain under test
      - test goal
      - in-scope endpoints or flows
      - out-of-scope or unclear areas
      - input sources and conflict handling
      
      ### 2. REST Assured Test Plan or Structure
      - proposed packages and class list
      - `BaseApiTest` / config responsibilities
      - env vars and `test.properties` keys (no real secrets)
      - tag / layer strategy
      - alignment with existing Maven/Gradle suites (if any)
      
      ### 3. Priority Coverage
      For each P0/P1 case:
      - class and method names
      - method / path (confirmed only)
      - priority and risk rationale
      - positive / negative / boundary points
      - assertion focus (`statusCode`, body JSON paths)
      - dependencies on `requestSpec` or data
      
      ### 4. Setup and Data Notes
      - how auth is injected (Bearer placeholder, etc.)
      - test-data setup and cleanup
      - multi-environment switching
      
      ### 5. Execution Suggestions
      - local: `mvn test` / tag-filter examples
      - smoke vs regression scope
      - minimal CI steps and secret variable names
      - release-blocking checks
      
      ### 6. Open Questions
      - gaps and assumptions used this round
      
      ## Pre-delivery checklist
      
      - [ ] Inputs followed the parsing order; conflicts and gaps are called out
      - [ ] Package layout / Base / env placeholders match defaults (or explain reuse of existing)
      - [ ] No real secrets; no invented paths/fields/JSON paths
      - [ ] P0/P1 cases have concrete class/method names and assertion focus
      - [ ] Smoke tags and CI path are actionable
      
      ## Quality bar
      
      - Stay REST Assured + JUnit 5 specific (or the user’s equivalent existing stack).
      - Prioritize by risk.
      - Separate confirmed facts from assumptions.
      - Avoid huge Java dumps unless the user asks for runnable files.
      
  • references
    • local
      • api-testing_EN.md 492 B
        # Archived Local Reference
        
        This file is a lightweight legacy note. The previous long snapshot was removed to avoid duplicate and outdated guidance.
        
        ## Use Instead
        
        - Main prompt: `prompts/api-test-restassure.md`
        - Main entry: `SKILL.md`
        
        ## Notes
        
        - Keep using the current prompt and `SKILL.md` as the source of truth.
        - Load `references/`, `examples/`, or `scripts/` only when the task really needs extra detail.
        - Do not rely on this file for the latest prompt wording or workflow rules.
        
    • framework-spec.md 1.5 KB
      # Rest Assured Framework Specification
      
      ## 1. Directory Convention
      
      - `scripts/parse_api_sources.py`: multi-format API source parser
      - `scripts/generate_restassured_tests.py`: normalized endpoint -> Java test class
      - `scripts/templates/restassured/`: runnable Maven test template
      - `generated-tests/`: output Java tests generated from parsed endpoints
      
      ## 2. Test Layer Convention
      
      - `smoke`: core endpoint availability
      - `contract`: status code + response shape checks
      - `business`: key API flows and state transitions
      - `negative`: invalid params, auth failure, boundary inputs
      
      ## 3. Assertion Rules
      
      - status code assertions are mandatory
      - key response field assertions are mandatory
      - latency checks on critical APIs are recommended
      - negative tests should validate error payload structure
      
      ## 4. Data and Environment Rules
      
      - use environment/properties for base URL and token
      - never commit secrets in test sources
      - use deterministic data or isolated test fixtures
      - define cleanup strategy for mutable endpoints
      
      ## 5. Parsing Scope Rules
      
      Supported parser inputs:
      - curl
      - Postman collections
      - Swagger/OpenAPI (including v3)
      - Bruno
      - OpenCollection
      - Insomnia
      - WSDL
      - ZIP containing any supported format
      
      Parser output must be normalized endpoint JSON with:
      - method
      - path
      - source format
      - optional base URL, headers, query/body hints
      
      ## 6. CI Guidance
      
      - run smoke tests on each PR
      - run full suite on main/nightly
      - fail on P0 regression
      - export surefire/junit XML for trend tracking
      
    • report-schema.md 800 B
      # Unified API Test Report Schema
      
      Use these fields for all generated API test reports:
      
      - `run_id`: unique run identifier
      - `tool`: `supertest` / `pytest` / `bruno` / `restassured`
      - `env`: environment name (staging/prod-like)
      - `case_id`: stable case identifier
      - `api_name`: human-readable endpoint name
      - `method`: HTTP method
      - `path`: endpoint path
      - `status`: `pass` / `fail` / `skip`
      - `status_code`: actual HTTP status code
      - `expected_status_code`: expected status code or range
      - `latency_ms`: measured request latency
      - `error_rate`: optional aggregated failure rate per run
      - `assertions_total`: total assertions
      - `assertions_passed`: passed assertions
      - `message`: failure or debug message
      - `timestamp`: ISO8601 UTC timestamp
      
      Recommended rule:
      - P0 failures should fail CI pipelines.
      
    • setup-and-ci.md 420 B
      # Setup and CI Guide
      
      ## Local
      
      ```bash
      cd scripts/templates/restassured
      mvn -q test
      ```
      
      ## One-Click Flow
      
      ```bash
      cd scripts
      ./run.sh ../examples
      ```
      
      ## CI Recommendation
      
      - PR: run smoke/generated subset
      - main/nightly: run full suite
      - archive surefire reports
      
      ## CI Template
      
      - `examples/ci/github-actions-restassure.yml`
      - `examples/ci/Jenkinsfile.restassure`
      
      ## Report Schema
      
      - `references/report-schema.md`
      
  • scripts
    • templates
      • restassured
        • src
          • test
            • java
              • com
                • example
                  • api
                    • BaseApiTest.java 1.1 KB · in bundle
                    • HealthApiTest.java 456 B · in bundle
            • resources
              • test.properties 49 B · in bundle
        • pom.xml 1.3 KB · in bundle
    • generate_restassured_tests.py 2.7 KB
      #!/usr/bin/env python3
      import argparse
      import json
      import re
      from pathlib import Path
      
      
      def _safe_method(m: str) -> str:
          return m.upper() if m.upper() in {"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"} else "GET"
      
      
      def _safe_name(method: str, path: str, idx: int) -> str:
          s = f"{idx}_{method}_{path}".lower()
          return re.sub(r"[^a-z0-9]+", "_", s).strip("_")
      
      
      def _invoke_line(method: str, path: str) -> str:
          m = method.lower()
          if m in {"post", "put", "patch"}:
              return (
                  f'given().spec(requestSpec).body("{{}}").when().{m}("{path}")'
                  '.then().statusCode(anyOf(is(200), is(201), is(202), is(204), is(400), is(401), is(403), is(404)))'
                  '.time(lessThan(3000L));'
              )
          return (
              f'given().spec(requestSpec).when().{m}("{path}")'
              '.then().statusCode(anyOf(is(200), is(201), is(202), is(204), is(400), is(401), is(403), is(404)))'
              '.time(lessThan(3000L));'
          )
      
      
      def build_java_class(endpoints):
          lines = [
              "package com.example.api;",
              "",
              "import org.junit.jupiter.api.Test;",
              "",
              "import static io.restassured.RestAssured.given;",
              "import static org.hamcrest.Matchers.anyOf;",
              "import static org.hamcrest.Matchers.is;",
              "import static org.hamcrest.Matchers.lessThan;",
              "",
              "public class GeneratedApiTest extends BaseApiTest {",
              "",
              "    @Test",
              "    void generatedCollectionNotEmpty() {",
              f"        org.junit.jupiter.api.Assertions.assertTrue({len(endpoints)} >= 0);",
              "    }",
              "",
          ]
          for idx, ep in enumerate(endpoints, start=1):
              method = _safe_method(str(ep.get("method", "GET")))
              path = str(ep.get("path", "/"))
              name = _safe_name(method, path, idx)
              lines.extend(
                  [
                      "    @Test",
                      f"    void test_{name}() {{",
                      f"        {_invoke_line(method, path)}",
                      "    }",
                      "",
                  ]
              )
          lines.append("}")
          lines.append("")
          return "\n".join(lines)
      
      
      def main() -> None:
          parser = argparse.ArgumentParser(description="Generate Rest Assured tests from normalized endpoint JSON")
          parser.add_argument("--input", required=True, type=Path, help="Normalized endpoint JSON")
          parser.add_argument("--output", required=True, type=Path, help="Output Java test class path")
          args = parser.parse_args()
      
          payload = json.loads(args.input.read_text(encoding="utf-8"))
          endpoints = payload.get("endpoints", [])
          java = build_java_class(endpoints)
          args.output.parent.mkdir(parents=True, exist_ok=True)
          args.output.write_text(java, encoding="utf-8")
          print(str(args.output))
      
      
      if __name__ == "__main__":
          main()
      
    • parse_api_sources.py 9.1 KB
      #!/usr/bin/env python3
      import argparse
      import json
      import re
      import zipfile
      from pathlib import Path
      from typing import Dict, List, Optional
      from xml.etree import ElementTree as ET
      
      try:
          import yaml  # type: ignore
      except Exception:
          yaml = None
      
      
      METHODS = {"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"}
      
      
      def _safe_text(raw: bytes) -> str:
          return raw.decode("utf-8", errors="ignore")
      
      
      def _append_endpoint(out: List[Dict], method: str, path: str, source: str, **extra) -> None:
          method = method.upper().strip()
          path = (path or "").strip()
          if method not in METHODS or not path:
              return
          rec = {"method": method, "path": path, "source": source}
          rec.update({k: v for k, v in extra.items() if v is not None})
          out.append(rec)
      
      
      def _parse_curl(text: str, source: str) -> List[Dict]:
          out: List[Dict] = []
          for line in [x.strip() for x in text.splitlines() if "curl " in x]:
              m_method = re.search(r"-X\s+([A-Za-z]+)", line)
              method = m_method.group(1).upper() if m_method else "GET"
              m_url = re.search(r"(https?://[^\s'\"\\]+)", line)
              if not m_url:
                  continue
              url = m_url.group(1)
              m_path = re.match(r"https?://[^/]+(.*)", url)
              path = m_path.group(1) if m_path else "/"
              if not path:
                  path = "/"
              _append_endpoint(out, method, path, source, raw_url=url, format="curl")
          return out
      
      
      def _parse_postman(obj: Dict, source: str) -> List[Dict]:
          out: List[Dict] = []
      
          def walk(items: List[Dict]) -> None:
              for it in items or []:
                  if "request" in it:
                      req = it.get("request", {})
                      method = str(req.get("method", "GET")).upper()
                      url_obj = req.get("url", {})
                      path = "/"
                      raw_url = None
                      if isinstance(url_obj, dict):
                          raw_url = url_obj.get("raw")
                          path_parts = url_obj.get("path")
                          if isinstance(path_parts, list) and path_parts:
                              path = "/" + "/".join(str(p) for p in path_parts)
                          elif isinstance(raw_url, str):
                              m = re.match(r"https?://[^/]+(.*)", raw_url)
                              path = m.group(1) if m and m.group(1) else "/"
                      elif isinstance(url_obj, str):
                          raw_url = url_obj
                          m = re.match(r"https?://[^/]+(.*)", url_obj)
                          path = m.group(1) if m and m.group(1) else "/"
                      _append_endpoint(out, method, path, source, raw_url=raw_url, format="postman")
                  if isinstance(it.get("item"), list):
                      walk(it["item"])
      
          walk(obj.get("item", []))
          return out
      
      
      def _parse_openapi(obj: Dict, source: str) -> List[Dict]:
          out: List[Dict] = []
          paths = obj.get("paths", {})
          if not isinstance(paths, dict):
              return out
          for path, methods in paths.items():
              if not isinstance(methods, dict):
                  continue
              for m in methods.keys():
                  method = str(m).upper()
                  if method in METHODS:
                      _append_endpoint(out, method, str(path), source, format="openapi")
          return out
      
      
      def _parse_insomnia(obj: Dict, source: str) -> List[Dict]:
          out: List[Dict] = []
          resources = obj.get("resources", [])
          if not isinstance(resources, list):
              return out
          for r in resources:
              if not isinstance(r, dict):
                  continue
              if r.get("_type") == "request":
                  method = str(r.get("method", "GET")).upper()
                  url = str(r.get("url", ""))
                  path = "/"
                  m = re.match(r"https?://[^/]+(.*)", url)
                  if m and m.group(1):
                      path = m.group(1)
                  elif url.startswith("/"):
                      path = url
                  _append_endpoint(out, method, path, source, raw_url=url, format="insomnia")
          return out
      
      
      def _parse_opencollection(obj: Dict, source: str) -> List[Dict]:
          out: List[Dict] = []
          requests = obj.get("requests")
          if isinstance(requests, list):
              for r in requests:
                  if not isinstance(r, dict):
                      continue
                  method = str(r.get("method", "GET")).upper()
                  path = str(r.get("path") or r.get("url") or "/")
                  _append_endpoint(out, method, path, source, format="opencollection")
          return out
      
      
      def _parse_wsdl(text: str, source: str) -> List[Dict]:
          out: List[Dict] = []
          try:
              root = ET.fromstring(text.encode("utf-8"))
          except Exception:
              return out
          ns = {
              "wsdl": "http://schemas.xmlsoap.org/wsdl/",
              "soap": "http://schemas.xmlsoap.org/wsdl/soap/",
          }
          soap_address = root.find(".//soap:address", ns)
          location = soap_address.attrib.get("location") if soap_address is not None else None
          for op in root.findall(".//wsdl:operation", ns):
              name = op.attrib.get("name")
              if not name:
                  continue
              path = f"/soap/{name}"
              _append_endpoint(out, "POST", path, source, raw_url=location, format="wsdl", operation=name)
          return out
      
      
      def _parse_bruno(text: str, source: str) -> List[Dict]:
          out: List[Dict] = []
          method = None
          url = None
          for line in text.splitlines():
              s = line.strip()
              block = re.match(r"^(get|post|put|patch|delete|head|options)\s*\{", s, re.IGNORECASE)
              if block:
                  method = block.group(1).upper()
                  continue
              m = re.match(r"method:\s*([A-Za-z]+)", s, re.IGNORECASE)
              if m:
                  method = m.group(1).upper()
                  continue
              u = re.match(r"url:\s*(.+)$", s, re.IGNORECASE)
              if u:
                  url = u.group(1).strip()
          if method and url:
              m = re.match(r"https?://[^/]+(.*)", url)
              path = m.group(1) if m and m.group(1) else (url if url.startswith("/") else f"/{url}")
              _append_endpoint(out, method, path, source, raw_url=url, format="bruno")
          return out
      
      
      def _load_structured(path: Path, raw: str) -> Optional[Dict]:
          if path.suffix.lower() == ".json":
              try:
                  return json.loads(raw)
              except Exception:
                  return None
          if path.suffix.lower() in {".yaml", ".yml"} and yaml is not None:
              try:
                  obj = yaml.safe_load(raw)
                  return obj if isinstance(obj, dict) else None
              except Exception:
                  return None
          return None
      
      
      def parse_file(path: Path, raw: str) -> List[Dict]:
          lower_name = path.name.lower()
          ext = path.suffix.lower()
      
          if ext == ".wsdl" or (ext == ".xml" and "<definitions" in raw and "wsdl" in raw.lower()):
              return _parse_wsdl(raw, str(path))
          if ext == ".bru":
              return _parse_bruno(raw, str(path))
          if ext in {".curl", ".sh", ".txt"}:
              return _parse_curl(raw, str(path))
      
          obj = _load_structured(path, raw)
          if obj is not None:
              if "openapi" in obj or "swagger" in obj:
                  return _parse_openapi(obj, str(path))
              if lower_name.endswith(".postman_collection.json") or "info" in obj and "item" in obj:
                  return _parse_postman(obj, str(path))
              if "resources" in obj:
                  return _parse_insomnia(obj, str(path))
              if lower_name.endswith(".opencollection.json") or "requests" in obj:
                  return _parse_opencollection(obj, str(path))
              return []
      
          return _parse_curl(raw, str(path))
      
      
      def parse_zip(path: Path) -> List[Dict]:
          out: List[Dict] = []
          with zipfile.ZipFile(path) as zf:
              for info in zf.infolist():
                  if info.is_dir():
                      continue
                  p = Path(info.filename)
                  raw = _safe_text(zf.read(info))
                  if p.suffix.lower() == ".zip":
                      continue
                  out.extend(parse_file(p, raw))
          return out
      
      
      def parse_input(path: Path) -> List[Dict]:
          if path.is_dir():
              out: List[Dict] = []
              for f in path.rglob("*"):
                  if f.is_file():
                      raw = f.read_text(encoding="utf-8", errors="ignore")
                      out.extend(parse_file(f, raw))
              return out
          if path.suffix.lower() == ".zip":
              return parse_zip(path)
          raw = path.read_text(encoding="utf-8", errors="ignore")
          return parse_file(path, raw)
      
      
      def dedupe(endpoints: List[Dict]) -> List[Dict]:
          seen = set()
          out = []
          for e in endpoints:
              key = (e.get("method"), e.get("path"), e.get("source"))
              if key in seen:
                  continue
              seen.add(key)
              out.append(e)
          return out
      
      
      def main() -> None:
          parser = argparse.ArgumentParser(description="Parse API sources into normalized endpoint inventory")
          parser.add_argument("--input", required=True, type=Path, help="Input file/folder/zip path")
          parser.add_argument("--output", type=Path, help="Output JSON path")
          args = parser.parse_args()
      
          endpoints = dedupe(parse_input(args.input))
          payload = {
              "input": str(args.input),
              "count": len(endpoints),
              "endpoints": endpoints,
          }
      
          if args.output:
              args.output.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
              print(str(args.output))
          else:
              print(json.dumps(payload, ensure_ascii=False, indent=2))
      
      
      if __name__ == "__main__":
          main()
      
    • run.sh 647 B
      #!/usr/bin/env bash
      set -euo pipefail
      
      SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
      ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
      INPUT_PATH="${1:-$ROOT_DIR/examples}"
      TMP_JSON="$ROOT_DIR/.tmp.normalized.json"
      OUT_JAVA="$SCRIPT_DIR/templates/restassured/src/test/java/com/example/api/GeneratedApiTest.java"
      
      python3 "$SCRIPT_DIR/parse_api_sources.py" --input "$INPUT_PATH" --output "$TMP_JSON"
      python3 "$SCRIPT_DIR/generate_restassured_tests.py" --input "$TMP_JSON" --output "$OUT_JAVA"
      
      if ! command -v mvn >/dev/null 2>&1; then
        echo "mvn not found. Test class generated at: $OUT_JAVA"
        exit 0
      fi
      
      cd "$SCRIPT_DIR/templates/restassured"
      mvn -q test
      
  • README.md 814 B
    # api-test-restassure (EN)
    
    ## Skill Overview
    
    Need API outputs that should land in REST Assured based automation; The project is Java-based or already uses REST Assured.
    
    ## How to Use
    
    1. Open `SKILL.md` in this folder and confirm this skill fits your task.
    2. In your AI tool, call `@skill api-test-restassure`, then add your real project context and goal.
    3. If you need a specific output format (table, checklist, report), include it directly in your request.
    
    ## One-Click Install Script
    
    Run from the repository root:
    
    ### macOS / Linux
    
    ```bash
    bash ./scripts/install-skills-mac.sh --tool codex --lang en --skill api-test-restassure
    ```
    
    ### Windows PowerShell
    
    ```powershell
    powershell -ExecutionPolicy Bypass -File .\scripts\install-skills-windows.ps1 -Tool codex -Lang en -Skill api-test-restassure
    ```
    
  • SKILL.md 2.6 KB
    ---
    name: api-test-restassure
    description: Use this skill when you need to parse multi-format API definitions and generate Rest Assured Java test classes; triggers include Rest Assured, RestAssured, and Java API automation.
    ---
    
    # api-test-restassure (EN)
    
    **Chinese version:** See the corresponding Chinese skill.
    
    ## When to Use
    
    - Need API outputs that should land in REST Assured based automation.
    - The project is Java-based or already uses REST Assured.
    
    ## Workflow
    
    1. Read and follow the main prompt listed under Progressive disclosure (coverage, structure, quality bar).
    2. Add only project context that changes the result: scope, environment, constraints, risks, dependencies, expected deliverable.
    3. If input is incomplete, return a usable first draft and explicitly mark assumptions and gaps.
    4. Default to Markdown; switch formats only when the user asks.
    
    ## Core Constraints
    
    - Prioritize by risk / business impact — do not treat everything equally.
    - Separate confirmed facts from current assumptions.
    - Do not invent endpoints, fields, environments, or root causes the user did not provide.
    - Use placeholders or env-var semantics for auth/secrets; never hardcode real credentials.
    - Keep output executable: concrete scenarios, clear priority, clear next steps.
    
    ## Progressive Disclosure
    
    - Before producing output, read and follow `prompts/api-test-restassure.md` (minimum coverage, output structure, quality bar).
    - When a ready-made template fits: use matching files under `output-templates/`.
    - When the user wants examples or alignment with existing assets: read relevant `examples/`.
    - For deep framework/troubleshoot/schema notes: read only the relevant file(s) under `references/`, do not load the whole directory.
    - For format conversion or helper checks: prefer existing `scripts/` over reinventing.
    - For evaluating/regressing this skill: use `evals/` with skill-up.
    
    ## Pre-delivery Checklist
    
    - [ ] Followed the main prompt's output structure
    - [ ] Minimum coverage focus: suite structure, common setup, auth handling, priority endpoints, positive scenarios, negative and boundary scenarios, assertion focus, test data strategy, ... (details in main prompt)
    - [ ] Covered the minimum checklist, or explained omissions
    - [ ] High-risk items have explicit priority
    - [ ] Did not invent details the user did not provide
    - [ ] Assumptions and gaps are marked
    
    ## Common Pitfalls
    
    - Do not pretend completeness when scope/context is missing.
    - Do not treat every item as equally important.
    - Do not skip assumptions and information gaps.
    - Do not dump generic theory unrelated to the current toolchain.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related