manual-test
Skill0 starsChanged 51 days agoMITView on GitHub
Guide for manual testing backend services locally via curl OR Kafka event triggers, with direct DB/Redis verification. Polymorphic by stack: java-spring (Summer Framework X-Userinfo + Keycloak ROPC, plain Spring JWT), node, python, go, dotnet. Use this skill when: testing APIs locally, creating auth tokens, seeding test data, verifying callback webhooks, producing Kafka events to trigger consumers, or debugging API responses. Also trigger when user says: "manual test", "test locally", "curl test", "seed data", "produce event", or wants to verify a fix.
- Reads credentials
- Sends data out
7 other projects use a version of this.
Adopted by
Most recently taken up by
- hypequery/hypequerytheir own version, first seen Nov 8, 2026
- InfiniteRoomLabs/agent-opstheir own version, first seen Nov 1, 2026
- QAMarcos/claude-qa-flowtheir own version, first seen Nov 1, 2026
- kaeawc/auto-mobiletheir own version, first seen Oct 8, 2026
- sheshbabu/zentheir own version, first seen Oct 2, 2026
- tranquocthong/claude-spec-flowword for word, first seen Sep 22, 2026
Other versions7 projects use a version of this.
Word for word · 1
- tranquocthong/claude-spec-flow★ 0100% same
Edited · 0
None found.
Wrote their own version · 6
- sheshbabu/zen★ 1.2ksame name
- hypequery/hypequery★ 104same name
- kaeawc/auto-mobile★ 56same name
- QAMarcos/claude-qa-flow★ 12same name
- joris887/exosuit★ 9same name
- InfiniteRoomLabs/agent-ops★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
The file
We split this skill into the rules it gives an agent
12 rules. 0 are common, 7 are distinctive, and 5 are very specific to this project.
0
Common
Most of the 6 edited versions we read keep these.
7
Distinctive
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 18
Test backend services locally via curl OR by producing Kafka events, with direct DB/Redis verification.
- Very specific: line 55
Always verify code freshness before testing: Run scripts/api.sh PRIME (calls freshness-check.sh).
- Distinctive: line 56
Manual mode (default): ask user "App đang chạy commit X, repo ở Y, restart?" — DO NOT kill/restart without explicit yes.
- Very specific: line 58
Never: run ./gradlew bootRun / npm start / dotnet run without going through scripts/restart-service.sh (it confirms intent and shows the …
- Distinctive: line 61
Don't use your automated-test stub task for manual testing: If the repo has a stub task for automated/blackbox runs (on its own ports), …
- Very specific: line 68
Place checklists at: .claude/docs/manual-tests/features/<feature>/CHECKLIST.yaml
- Very specific: line 69
Test rigor — MUST follow
references/test-rigor.md: Every test MUST: - Distinctive: line 74
Before reporting PASS, run the sanity-fail probe (break one seed → that test must fail).
- Distinctive: line 75
SHOULD rules (realistic seed shape, no pre-set derived fields, isolation-row seed, negative-control sibling) apply for regression / depth.
- Distinctive: line 115
Run discovery before testing:
- Very specific: line 166
Heuristic: if /bo/... returns 401, go straight to Path B.
- Distinctive: line 184
Verify JWT roles before testing endpoint:
“Common” is measured: kept by at least 6 in 10 of the 6 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Manual Testing Backend Services
Test backend services locally via curl OR by producing Kafka events, with direct DB/Redis verification. Works across stacks (Java/Spring, Node, Python, Go, .NET) via per-stack discovery scripts.
Per-stack coverage is deepest for Java + Spring (especially Summer Framework) — the rest cover the common 80% and fall back to agent grep for anything not deterministically detectable. See references/stacks.md.
This file is the entry point. Detail lives in references/*.md (read on-demand) and runnable helpers in scripts/*.sh (Claude executes; user does nothing extra).
Lazy Mode (default path) — 5 commands
scripts/api.sh PRIME # detect stack + auth + port + creds + FRESHNESS check
# (reads PROJECT_CONTEXT.yaml if fresh — skips re-discovery)
scripts/test-user.sh # auto-pick + cache active test user (DB + Keycloak realm)
scripts/checklist-init.sh <feature> # REQUIRED if no CHECKLIST.yaml exists — generates scaffold
scripts/lint-checklist.sh <feature> # gate: refuses TODO/SELECT-1 markers + unresolved templates
scripts/run-checklist.sh <feature> [--tag smoke|regression] [--id TEST-ID]
# THE entry point for executing tests. Lints first, runs
# setup precondition SQL, fires each test, asserts body
# + status, runs verify SQL, prints PASS/FAIL/SKIP.
# Ad-hoc curl is NOT a substitute — add a test instead.PRIME reads .claude/docs/manual-tests/PROJECT_CONTEXT.yaml on subsequent sessions — skips re-discovery if < 7 days old. Force re-discovery with PRIME --refresh.
PRIME also runs freshness-check.sh — compares the running service's /actuator/info git commit against repo HEAD. If stale, it tells you how to restart:
scripts/api.sh RESTART # interactive: confirms before kill+restart
scripts/api.sh RESTART --auto # /manual-test auto mode: no prompt
scripts/api.sh FRESH # re-check freshness anytime
scripts/api.sh CONTEXT # print current PROJECT_CONTEXT.yaml
scripts/api.sh CONTEXT --refresh # force re-discover + rewrite contextCache lives in ~/.cache/manual-test/<project-hash>/. JWT TTL 240s (override JWT_TTL). On non-2xx, api.sh prints likely-cause hints from the troubleshooting matchers. Full cheatsheet: references/lazy-mode.md.
Hard Rules — NEVER violate
- Always verify code freshness before testing. Run
scripts/api.sh PRIME(callsfreshness-check.sh). If running service ≠ repo HEAD or working tree is dirty, the test result is meaningless against current code.- Manual mode (default): ask user "App đang chạy commit X, repo ở Y, restart?" — DO NOT kill/restart without explicit yes.
- Auto mode (
scripts/api.sh RESTART --autoOR/manual-test --auto): allowed to kill + restart without prompt. ONLY when user invoked auto explicitly. - Never run
./gradlew bootRun/npm start/dotnet runwithout going throughscripts/restart-service.sh(it confirms intent and shows the exact command first).
- Never delete
.envlines. Comment with#only — lines may be needed again. - Use
tr -d '\n'when base64-encoding tokens on macOS —base64wraps long output silently → 401. - Don't use your automated-test stub task for manual testing. If the repo has a stub task for automated/blackbox runs (on its own ports), those are for automated tests. Manual testing uses the real services + your manual stub project (see
references/ports.md). - Every setup mutation MUST be reversed in teardown — even if the test fails midway. See
references/seeding.md→ State Restoration. - Every test MUST be executed via
scripts/run-checklist.sh. Ad-hoc curl as the primary test method is FORBIDDEN — this is enforced by code, not convention.- CHECKLIST.yaml exists →
scripts/run-checklist.sh <feature> [--tag smoke]. - CHECKLIST.yaml missing →
scripts/checklist-init.sh <feature>→ fill TODOs →scripts/lint-checklist.sh <feature>→scripts/run-checklist.sh <feature>. - The runner lints first and refuses to execute if TODO markers, placeholder
SELECT 1 --, unresolved{paramName}templates, or missingverify:blocks are present. Tag a test[no-verify]to acknowledge an HTTP-only assertion is intentional. - "Just running one curl to check" still has to be a test in the YAML. If the curl is worth running, it's worth keeping as regression coverage.
- Place checklists at:
.claude/docs/manual-tests/features/<feature>/CHECKLIST.yaml
- CHECKLIST.yaml exists →
- Test rigor — MUST follow
references/test-rigor.md. Every test MUST:- Reproduce its precondition via the real entry point (user journey, not direct
INSERTof end states). When shortcutting, mirror every ambient write (audit_log,outbox_events,version, derived columns) and comment why. - Verify a side-effect / delta, not just HTTP status. Status 200 alone is never a PASS.
- Use a unique correlation ID per run (
${TEST_CORRELATION_ID}), not stableTEST-001. - Use
poll untilwith a timestamp filter for async, neverwait_ms+ single check. - Before reporting PASS, run the sanity-fail probe (break one seed → that test must fail). See test-rigor.md § Post-run audit.
- SHOULD rules (realistic seed shape, no pre-set derived fields, isolation-row seed, negative-control sibling) apply for regression / depth.
- Reproduce its precondition via the real entry point (user journey, not direct
Workflow Overview
-1. CHECKLIST → glob .claude/docs/manual-tests/features/*/CHECKLIST.yaml
→ found? load and proceed to step 0
→ missing? scripts/checklist-init.sh <feature> → review → confirm
0. PRIME → scripts/api.sh PRIME
→ PROJECT_CONTEXT.yaml fresh (<7d)? load from it (skip steps 1-2)
→ stale / missing? run steps 1-2, then write PROJECT_CONTEXT.yaml
1. DETECT-STACK → scripts/detect-stack.sh → java-spring | node | python | go | dotnet | unknown
2. DISCOVERY → scripts/detect-auth.sh → summer | jwt-basic | session | no-auth | unknown
scripts/warm-up.sh (health, DB list, endpoints — branches by stack)
scripts/db-creds.sh (auto-discover PG/Redis creds; yml for Spring, .env for others)
scripts/port-detect.sh (env override > yml > probe — mirrors Spring's real resolution)
2b. FRESHNESS → scripts/freshness-check.sh
→ fresh = proceed
→ stale = scripts/restart-service.sh (manual: confirm | auto: no prompt)
→ unknown = service exposes no /info, agent decides
3. AUTH → quick-probe → Path A / B / C (see Authentication below)
4. SEED → CHECKLIST.yaml setup blocks → scripts/db-query.sh
5. TRIGGER → CHECKLIST.yaml request blocks → curl OR scripts/produce-event.sh
6. VERIFY → CHECKLIST.yaml verify blocks → scripts/db-query.sh + Redis + consumer-lag
(references/results.md, references/kafka-events.md)
7. TEARDOWN → CHECKLIST.yaml teardown blocks → restore every mutationAgent-search fallback (important): Scripts cover the common 80% case deterministically. When a script returns unknown or guidance is missing for the detected stack, the script prints structured hints to stderr — Claude uses Grep / Glob to investigate manually. See references/stacks.md.
Trigger type depends on the feature:
| Feature shape | Trigger in CHECKLIST.yaml | Reference |
HTTP-exposed (@RestController, handler) | request.method + request.path | references/patterns.md |
Event-driven (@KafkaListener, KafkaReceiver) | request.kafka.topic + kafka.payload | references/kafka-events.md |
| Both (HTTP triggers internal Kafka emit) | HTTP request, then verify both DB + output topic | both files |
Discovery
Run discovery before testing:
scripts/warm-up.shPrints: health probes for known ports, all DB names, controller @RequestMapping + method paths.
Path discovery rule (critical): full endpoint path = class-level @RequestMapping + method-level mapping (@GetMapping, etc.). Don't infer from class name. Full detail + examples: references/troubleshooting.md.
Authentication — Decision Tree
First, classify the project:
scripts/detect-auth.sh
# → summer | jwt-basic | no-auth | unknown| Detect result | Path | Reference |
summer | Path A or B (decide via quick-probe below) | references/auth.md |
jwt-basic | Path C — standard Authorization: Bearer <token> | references/auth-jwt-basic.md |
no-auth | No auth needed | — |
unknown | Inspect SecurityFilterChain manually | — |
jwt-basic also covers custom Bearer schemes with no JWT library — a service that
reads the Authorization header itself and validates an opaque API key / static secret.
Library fingerprints miss those, so the detector falls back to grepping the source for
header-read + Bearer prefix. The wire form is identical (Authorization: Bearer <token>);
only how you MINT the token differs, and that is a lookup either way. X-Userinfo is
Summer/APISIX-only — never scaffold it for a project that did not detect as summer.
Summer projects — quick-probe Path A vs B
# 1. Build minimal X-Userinfo token
QUICK_TOKEN=$(echo -n '{"iat":1772680061,"exp":2088040061,"sub":"probe"}' | base64 | tr -d '\n')
# 2. Hit any real endpoint
curl -sS -o /dev/null -w "HTTP=%{http_code}\n" \
"http://localhost:PORT/api/v1/..." -H "X-Userinfo: $QUICK_TOKEN"| Probe result | Path |
| 400 / 404 / 200 (anything ≠ 401) | Path A — X-Userinfo direct. Build Mode A or Mode B token. |
401 com.unauthorized.access | Path B — X-Userinfo not trusted. Use Keycloak ROPC + Bearer JWT. |
Common cause of Path B: summer-platform ≥ 0.3.0 BO endpoints — the summer-apisix filter only trusts X-Userinfo behind a trusted upstream. Locally there is no upstream → 401.
Heuristic: if /bo/... returns 401, go straight to Path B. Don't waste time tuning Mode A token.
Path A — quick start
TOKEN=$(echo -n '{"iat":1772680061,"exp":2088040061,"sub":"USER_ID"}' | base64 | tr -d '\n')
curl -s http://localhost:PORT/api/v1/... -H "X-Userinfo: $TOKEN"End-user vs BO token formats, Mode A vs Mode B (resource_access vs role_groups), Redis seed for group-role: references/auth.md.
Path B — quick start
JWT=$(scripts/kc-ropc.sh <realm> <client> <user> <pass> [client-secret])
curl -s http://localhost:PORT/bo/api/v1/... -H "Authorization: Bearer $JWT"Verify JWT roles before testing endpoint:
scripts/decode-jwt.sh "$JWT"Keycloak realm/client discovery, JWT role verification, alternative-user search, UAT-dumped data pattern, role testing matrix: references/auth.md.
Path C — quick start (plain JWT, non-Summer)
# Option 1: app's own login endpoint
JWT=$(curl -sf -X POST http://localhost:PORT/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"...","password":"..."}' \
| sed -n 's/.*"\(access_token\|token\)":"\([^"]*\)".*/\2/p')
# Option 2: external IDP (Keycloak / Auth0 / etc.) — kc-ropc.sh works for any OIDC token endpoint
JWT=$(scripts/kc-ropc.sh <realm> <client> <user> <pass> [secret] [token-url])
curl -s http://localhost:PORT/api/v1/... -H "Authorization: Bearer $JWT"Login endpoint discovery, static-signed JWT minting, @PreAuthorize / hasRole claim mapping, gotchas: references/auth-jwt-basic.md.
Internal Endpoints
/internal/api/v1/... — no auth (permitAll). Used for service-to-service calls and webhooks.
Reference Index
Open the relevant file when you hit the topic — don't preload.
| Topic | File |
| Test rigor — 5 MUST + 5 SHOULD rules, BAD vs GOOD example | references/test-rigor.md |
| Lazy mode — 3 commands, auto-auth wrapper, gotcha matcher | references/lazy-mode.md |
| Per-stack discovery cheatsheet + agent-search fallback recipes | references/stacks.md |
CHECKLIST.yaml driven runs | references/checklist.md |
| Port map template (example layout — replace with your project's) | references/ports.md |
| Summer auth — Path A (X-Userinfo) + Path B (Keycloak ROPC) | references/auth.md |
| Plain JWT auth (any stack) — login endpoints, claim mapping, static signing | references/auth-jwt-basic.md |
| Kafka event-triggered testing — produce, consume, lag, DLT, replay | references/kafka-events.md |
| DB seed patterns, Redis, pg_notify, state restoration | references/seeding.md |
| 6 HTTP testing patterns (boundary, isolation, callback, config, fail-safe, restoration) | references/patterns.md |
| Response codes, stub failures, gotchas (date format, custom ID, path) | references/troubleshooting.md |
| Post-test verification, output format, pre-test checklist | references/results.md |
Helper Scripts
| Script | Purpose |
scripts/run-checklist.sh <feature> [--tag T] [--id ID] | Test executor — the ONLY supported way to run tests. Lints, resolves tokens, runs setup SQL, fires each test (HTTP), asserts status + body, runs verify SQL, prints pass/fail summary. Exit non-zero on any failure. Hard Rule #6 enforcement lives here. |
scripts/lint-checklist.sh <feature> | Pre-flight gate. Fails on TODO markers, placeholder SELECT 1 --, unresolved {paramName} path templates, missing verify: blocks (unless tagged no-verify). Always run before run-checklist.sh; the runner calls it implicitly too. |
scripts/api.sh PRIME [--refresh] | CONTEXT [--refresh] | RESTART | FRESH | REPEAT | STATE | CLEAR | METHOD PATH [...] | Discovery + freshness entry point. On PRIME: reads PROJECT_CONTEXT.yaml if fresh, else runs full discovery and writes context. METHOD PATH is for one-off probes during DISCOVERY only — NOT a substitute for run-checklist.sh. See references/lazy-mode.md. |
scripts/checklist-init.sh <feature> [--path /api/v1/prefix] | Generate CHECKLIST.yaml scaffold for a feature. Scans codebase for matching endpoints (Java: @GetMapping etc.; Summer: @Handler). Writes to .claude/docs/manual-tests/features/<feature>/CHECKLIST.yaml. Run when no checklist exists. |
scripts/test-user.sh | Auto-pick active test entity from DB + match Keycloak realm + cache password from realm export. |
scripts/freshness-check.sh [--port N] [--quiet] | Compare running service git commit vs repo HEAD. Exit 0=fresh / 1=stale / 2=unreachable. |
scripts/restart-service.sh [--auto] [--kill-only] [--port N] [--cmd 'cmd'] | Polymorphic kill + restart (lsof PID, SIGTERM/KILL, start by stack, wait health). Interactive by default; --auto skips prompt. |
scripts/context.sh (sourceable) | PROJECT_CONTEXT.yaml library: context_load/context_write/context_get/context_set/context_is_fresh. Used by api.sh PRIME. |
scripts/detect-stack.sh [project-root] | Classify stack: java-spring / node / python / go / dotnet / unknown |
scripts/detect-auth.sh [project-root] | Classify auth: summer / jwt-basic / session / no-auth / unknown (polymorphic by stack) |
scripts/port-detect.sh [project-root] | Resolve server port from yml/env/compose/probe. Used by api.sh PRIME. |
scripts/warm-up.sh | Health probes + DB list + endpoint discovery (branches by stack) |
scripts/state.sh (sourceable) | Session cache library: state_set/state_get/jwt_get/jwt_set. Used by api.sh + test-user.sh. |
scripts/db-creds.sh [--print] [project-root] | Discover PG + Redis creds from .env + application.yml. eval $(...) exports PG_HOST PG_PORT PG_DB PG_USER PG_PASS REDIS_HOST REDIS_PORT REDIS_PASS. |
scripts/db-query.sh "<sql>" [-d DB] [--host H] [--port P] [--user U] [--password W] [-f FILE] [-t] [-r root] | 1-shot psql with auto-discovered creds; each -- flag overrides one discovered field (multi-service). Use for seed / verify / inspect. |
scripts/decode-jwt.sh "$JWT" | Print JWT payload (handles macOS base64 padding) |
scripts/kc-ropc.sh <realm> <client> <user> <pass> [secret] [token-url] | Fetch Bearer JWT from Keycloak or any OIDC IDP |
scripts/produce-event.sh <topic> <payload-file|-> [-k KEY] [-H name=val] [-b BROKER] | Produce Kafka event (kcat or docker fallback) |
Templates
| File | Purpose |
templates/CHECKLIST.yaml | Boilerplate per-feature checklist — used by checklist-init.sh as base. Can also be copied manually to .claude/docs/manual-tests/features/<feature>/CHECKLIST.yaml. |
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

