원클릭으로
qa-testing
Execute scenario-based QA testing with browser automation, database validation, and automatic ticket creation for failures.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Execute scenario-based QA testing with browser automation, database validation, and automatic ticket creation for failures.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
Run E2E tests for Auth9 portal using Playwright with hybrid testing strategy.
Run tests, check logs, and troubleshoot Auth9 services in Docker and Kubernetes environments.
Run performance benchmarks for auth9-core API using hey load testing tool.
Reset Auth9 local Docker development environment to a clean state.
Rust coding conventions and patterns for auth9-core development.
Run tests and check coverage for Auth9 backend, frontend, and SDK with mock-based testing patterns.
SOC 직업 분류 기준
| name | qa-testing |
| description | Execute scenario-based QA testing with browser automation, database validation, and automatic ticket creation for failures. |
Execute scenario-based manual QA testing for Auth9 using Playwright browser automation with Docker environment validation.
./scripts/reset-docker.sh (avoid hard-coding in docs/skills)To prevent repository-root pollution during QA:
DATE=$(date +%F)
EVIDENCE_DIR="artifacts/qa/${DATE}"
mkdir -p "$EVIDENCE_DIR"
$EVIDENCE_DIR (or its subfolders).*.png, *.jpg, *.json, ad-hoc logs) to repository root.scripts/qa/, not root.artifacts/qa/{date}/tickets/{ticket_filename_without_ext}/artifacts/qa/{date}/ immediately.QA testing against auth9-core API requires a Bearer token. Do NOT explore the codebase to figure out how to get a token. Use the helper tools below directly.
# Generate admin JWT token (valid 1 hour, RS256 signed)
TOKEN=$(.claude/skills/tools/gen-admin-token.sh)
# Use in curl requests
curl -s http://localhost:8080/api/v1/tenants \
-H "Authorization: Bearer $TOKEN"
For repeated API calls, use the wrapper script that auto-injects the token:
# GET request
.claude/skills/tools/qa-api-test.sh GET /api/v1/tenants
# POST with JSON body
.claude/skills/tools/qa-api-test.sh POST /api/v1/users '{"email":"test@example.com","password":"Pass123!"}' # pragma: allowlist secret
# PUT with JSON body
.claude/skills/tools/qa-api-test.sh PUT /api/v1/tenants/{id}/password-policy '{"min_length":12}'
746ceba8-3ddf-4a8b-b021-a1337b7a1a35 (admin@auth9.local).claude/skills/tools/jwt_private_clean.key)http://localhost:8080node + jsonwebtoken npm package (already in project root gen_token.js)Use hey for concurrent request testing:
hey -n 20 -c 20 -m POST \
-H "Content-Type: application/json" \
-d '{"email": "test@example.com"}' \
http://localhost:8080/api/v1/auth/forgot-password
Note: Rate limiting is active on some endpoints (e.g., forgot-password: 5 req/min, token: 10 req/min).
All test data (emails, URLs, domains) must comply with docs/testing/test-domain-policy.md. Summary:
@example.com, @test.com, @auth9.localhttps://*.example.com/...*.example.com (e.g. acme.example.com)evil.com, attacker.com, attacker.exampleexample.comtest-enterprise.com, acme.com, corp.com, etc.scripts/qa/)IMPORTANT: A collection of reusable QA test scripts already exists in scripts/qa/. Before writing any new test script, always check scripts/qa/ first for an existing script that covers the same or similar scenario.
ls scripts/qa/ or Glob: scripts/qa/* to find existing scripts. If a matching script exists, use it directly (or adapt it) instead of creating a new one.scripts/qa/: When a new test script is needed, always place it under scripts/qa/ — never in the project root or other ad-hoc locations.test-{feature}.{js,mjs,py,sh} or {feature}_test.py.IMPORTANT: This skill is strictly for testing and reporting. NEVER attempt to fix, patch, or modify any source code during QA testing. If a test fails, create a ticket immediately and move on to the next scenario.
1. Confirm QA document with user
2. List all test scenarios
3. For each scenario:
a. Execute test in browser
b. If error → Check Docker logs
c. Validate database state
d. If FAIL → Immediately create ticket in docs/ticket/ (DO NOT defer)
e. Report scenario result (PASS/FAIL) before moving to next
4. Report final summary to user
5. Run workspace hygiene check (no QA artifacts in repository root)
Ticket creation rule: Create the ticket the moment a scenario is confirmed as FAIL — before starting the next scenario. This ensures no failure is lost if the session is interrupted, and gives the user real-time visibility into issues as they surface.
CRITICAL: Always confirm with user which QA document to test.
Glob: docs/qa/**/*.md
Modules: tenant/, user/, rbac/, service/, invitation/, session/, webhook/, auth/
Exclude docs/qa/README.md (just an index).
docs/qa/README.md, list modules, ask user to chooseExtract from confirmed document:
token_type and tenant_id fieldsid valuesUUID(), configure missing service). Do NOT skip gates and proceed — this is the primary cause of false-positive tickets.Use playwright-cli commands via Bash:
# 1. Open browser and navigate
playwright-cli open http://localhost:3000
# 2. Snapshot to get page structure and element refs
playwright-cli snapshot
# 3. Login (if needed):
playwright-cli fill e1 "admin"
playwright-cli fill e2 "SecurePass123!"
playwright-cli click e3
# 4. Execute test steps:
# - snapshot before each interaction
playwright-cli snapshot
# - click, type, fill for interactions
playwright-cli click e5
playwright-cli type "some text"
# - snapshot to verify results
playwright-cli snapshot
# 5. Close browser when done
playwright-cli close
If screenshots are required for evidence, always save into $EVIDENCE_DIR:
# Example only; keep filenames scenario-oriented
playwright-cli screenshot "$EVIDENCE_DIR/scenario-01-login.png"
Rules:
playwright-cli snapshot before interactions to get element refsIf step fails:
docker logs auth9-core --tail 50
docker logs auth9-portal --tail 50
docker logs auth9-oidc --tail 50
After each scenario:
mysql -h 127.0.0.1 -P 4000 -u root auth9 -e "SELECT ..."
Compare actual vs expected:
CRITICAL: Create the ticket RIGHT NOW, before moving to the next scenario. Do NOT accumulate failures for batch ticket creation later. Each failed scenario gets its own ticket written to docs/ticket/ immediately upon confirmation of failure.
Workflow per failure:
docs/ticket/ using the naming and structure belowdocs/ticket/{filename}.md"This ensures:
When attaching evidence in ticket markdown, use paths under artifacts/qa/{date}/... only.
For UIUX test documents: In addition to functional failures, visibility and accessibility issues MUST also result in ticket creation. This applies even when the underlying functionality works correctly. Examples include:
When creating a ticket for a UIUX visibility/accessibility issue, set:
Medium (default) — raise to High if the issue blocks a user action or violates WCAG 2.1 Level AFrontendFormat: {module}_{document}_scenario{N}_{YYMMDD_HHMMSS}.md
Example: docs/ticket/user_01-crud_scenario2_260203_143052.md
# Ticket: {Scenario Title}
**Created**: {YYYY-MM-DD HH:mm:ss}
**QA Document**: `docs/qa/{module}/{document}.md`
**Scenario**: #{number}
**Status**: FAILED
---
## 测试内容
{Brief description}
**Test Location**: {UI path or API endpoint}
---
## 预期结果
{Expected outcome}
**Expected Database State**:
```sql
{SQL queries}
{Initial state requirements}
UI Error:
{Error message}
Database State:
{Actual data}
Data Mismatch:
{Relevant log lines}
Root Cause: {Analysis} Severity: High / Medium / Low Related Components: Frontend / Backend / Database / Auth9-OIDC / Redis
Ticket generated by QA Testing Skill
## Step 4: Final Test Summary (report to user, don't save)
```markdown
✅ 测试完成!
📊 测试结果:
- 通过: 11/13 (84.6%)
- 失败: 2/13
🎫 创建的 Tickets:
1. docs/ticket/user_01-crud_scenario4_260203_143052.md
- Scenario #4: Update user profile
- Severity: High
-- User
SELECT id, email, display_name, mfa_enabled FROM users WHERE email = 'test@example.com';
SELECT tu.*, t.name FROM tenant_users tu JOIN tenants t ON t.id = tu.tenant_id WHERE tu.user_id = '{id}';
-- Tenant
SELECT id, name, slug, status FROM tenants WHERE slug = 'test-tenant';
SELECT * FROM services WHERE tenant_id = '{id}';
-- RBAC
SELECT r.name, utr.* FROM user_tenant_roles utr JOIN roles r ON r.id = utr.role_id WHERE utr.tenant_user_id = '{id}';
SELECT p.* FROM permissions p JOIN role_permissions rp ON rp.permission_id = p.id WHERE rp.role_id = '{id}';
reset-local-env skill if needed| Issue | Solution |
|---|---|
| Browser fails | curl http://localhost:3000, check auth9-portal logs |
| DB connection fails | docker ps | grep tidb, reconnect |
| Services not responding | docker ps, restart services, reset env |
| 403 "Identity token is only allowed for tenant selection and exchange" | Using Identity Token instead of Tenant Access Token. Run gate check: echo $TOKEN | cut -d. -f2 | base64 -d | jq .token_type — regenerate with gen-test-tokens.js tenant-owner |
| 500 ColumnDecode error on API call | Non-UUID id values in database (from manual INSERT). Run: SELECT id FROM {table} WHERE id NOT REGEXP '^[0-9a-f]{8}-' — delete and re-insert with UUID() |
| Feature appears missing or unconfigured | Environment prerequisite not met (e.g., IdP not configured, init not run). Check the scenario's 步骤 0 for required state verification |
# Get messages
curl http://localhost:8025/api/v1/messages
# Search
curl "http://localhost:8025/api/v1/search?query=to:test@example.com"
# Clear all
curl -X DELETE http://localhost:8025/api/v1/messages
Invitation flow: Clear mailpit → Send invitation → Get email → Extract link → Complete test
# Parse QR code
zbarimg --raw totp-qr.png
# Output: otpauth://totp/auth9:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=auth9
# Extract secret
SECRET=$(zbarimg --raw totp-qr.png | sed -n 's/.*secret=\([^&]*\).*/\1/p')
# Generate TOTP code
oathtool --totp -b "$SECRET"
API: POST /api/users/{id}/mfa/enable, POST /api/users/{id}/mfa/disable
IMPORTANT: See references/api-testing-cookbook.md for full recipes and common pitfalls.
auth9.TokenExchange (NOT auth9.token_exchange.TokenExchange)-cacert /certs/ca.crt -cert /certs/client.crt -key /certs/client.keyx-api-key: dev-grpc-api-key (required)grpcurl-docker.sh or Docker network-import-path /proto -proto auth9.protoservice_id: Use OAuth client_id string (e.g. auth9-portal), NOT service UUIDauth9-platform tenant: Use auth9-platform tenant ID, not demoTOKEN=$(.claude/skills/tools/gen-admin-token.sh)
PLATFORM_TENANT_ID=$(mysql -u root -h 127.0.0.1 -P 4000 auth9 -N -e \
"SELECT id FROM tenants WHERE slug = 'auth9-platform';")
.claude/skills/tools/grpcurl-docker.sh \
-cacert /certs/ca.crt -cert /certs/client.crt -key /certs/client.key \
-import-path /proto -proto auth9.proto \
-H "x-api-key: dev-grpc-api-key" \
-d "{\"identity_token\": \"$TOKEN\", \"tenant_id\": \"$PLATFORM_TENANT_ID\", \"service_id\": \"auth9-portal\"}" \
auth9-grpc-tls:50051 auth9.TokenExchange/ExchangeToken
IMPORTANT: See references/api-testing-cookbook.md for event type mapping and all recipes.
POST http://localhost:8080/api/v1/identity/eventsdev-webhook-secretx-webhook-signature: sha256=<HMAC-SHA256 hex> (legacy x-keycloak-signature also accepted)credentialType, authMethod, ipAddress)BODY='{"type":"LOGIN_ERROR","realmId":"auth9","userId":"00000000-0000-0000-0000-000000000001","error":"invalid_user_credentials","time":1704067200000,"details":{"username":"test","email":"test@example.com","credentialType":"otp"}}'
SECRET="dev-webhook-secret-change-in-production" # pragma: allowlist secret
SIG=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | cut -d' ' -f2)
curl -s -w "\nHTTP: %{http_code}" -X POST "http://localhost:8080/api/v1/identity/events" \
-H "Content-Type: application/json" \
-H "x-webhook-signature: sha256=$SIG" \
-d "$BODY"
auth9-oidc) — Keycloak has been removed.admin@auth9.local / SecurePass123!) for testing.Some QA documents have pre-built executable test scripts in scripts/qa/auto/. These scripts encode all API calls, assertions, and DB validations so you do NOT need to construct curl commands manually.
Check the QA document's corresponding entry in docs/qa/_manifest.yaml for a test_script field:
- id: integration/04-health-check
test_script: scripts/qa/auto/integration-04-health-check.sh
Or check if a matching script exists by convention:
# For docs/qa/auth/07-public-endpoints.md -> scripts/qa/auto/auth-07-public-endpoints.sh
ls scripts/qa/auto/auth-07-public-endpoints.sh
bash scripts/qa/auto/xxx.sh 2>/tmp/qa-stderr.log{"type":"scenario","id":1,"title":"...","status":"PASS",...} — scenario result{"type":"assert","status":"FAIL","label":"...","expected":"...","actual":"..."} — assertion detail{"type":"summary","total":5,"passed":4,"failed":1,...} — final summaryFollow the standard manual testing workflow described above (construct curl/browser steps from the QA doc).
Scripts use shared libraries in scripts/qa/lib/:
lib/assert.sh — assert_eq, assert_http_status, assert_json_field, assert_db, etc.lib/setup.sh — gen_admin_token, gen_tenant_token, api_get, api_post, db_query, etc.lib/runner.sh — scenario registration, timing, JSONL output, run_all execution