| name | be-testing |
| description | Backend testing patterns — API request construction, response verification, database state checks, error handling testing, and adaptive tool detection. |
| activation | Load when testing backend APIs or database state |
| allowed-tools | Bash(curl:*), Bash(httpie:*), Bash(http:*), Bash(psql:*), Bash(sqlite3:*), Bash(mysql:*), Bash(mongosh:*), Bash(redis-cli:*), Bash(command:*), Bash(echo:*), Bash(jq:*), Bash(grep:*), Bash(cat:./*), Bash(head:./*), Bash(tail:./*), Read, Write, Bash(mkdir:*) |
Backend Testing Patterns
Tool Detection
ALWAYS run this check first:
command -v curl >/dev/null 2>&1 && echo "OK: curl available" || echo "UNAVAILABLE: curl"
command -v http >/dev/null 2>&1 && echo "OK: httpie available" || echo "UNAVAILABLE: httpie"
command -v psql >/dev/null 2>&1 && echo "OK: psql available" || echo "UNAVAILABLE: psql"
command -v sqlite3 >/dev/null 2>&1 && echo "OK: sqlite3 available" || echo "UNAVAILABLE: sqlite3"
command -v mysql >/dev/null 2>&1 && echo "OK: mysql available" || echo "UNAVAILABLE: mysql"
command -v mongosh >/dev/null 2>&1 && echo "OK: mongosh available" || echo "UNAVAILABLE: mongosh"
command -v redis-cli >/dev/null 2>&1 && echo "OK: redis-cli available" || echo "UNAVAILABLE: redis-cli"
command -v jq >/dev/null 2>&1 && echo "OK: jq available" || echo "UNAVAILABLE: jq"
Use the first available tool from each category. If no HTTP client is available, mark all API scenarios as SKIP.
Database Server Access
In addition to CLI database clients, check if database tools are available. In OpenCode, database access may be provided via shell commands.
Priority order for DB access:
- Configured database CLI (psql, sqlite3, mysql — most common in OpenCode)
- Direct shell access to database (e.g., docker exec into a DB container)
- SKIP (no access available)
Note: OpenCode provides database access through CLI tools installed in the environment. Always prefer CLI clients when available.
Execution Workflow
For each BE scenario from the test plan:
- Read the scenario — understand method, endpoint, payload, expected response, DB checks
- Execute the request — send HTTP request with proper method, headers, body
- Verify response — check status code, response body structure, specific values
- Verify DB state (if DB Check specified) — run query, compare against expected
- Execute edge cases — run each edge case as a sub-test
- Record result — pass/fail with response details
API Testing Patterns
Request Construction (curl)
GET request:
curl -s -w "\n%{http_code}" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
"http://localhost:8000/api/resources"
POST request:
curl -s -w "\n%{http_code}" \
-X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "test", "email": "test@example.com"}' \
"http://localhost:8000/api/resources"
PUT request:
curl -s -w "\n%{http_code}" \
-X PUT \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "updated"}' \
"http://localhost:8000/api/resources/1"
DELETE request:
curl -s -w "\n%{http_code}" \
-X DELETE \
-H "Authorization: Bearer $TOKEN" \
"http://localhost:8000/api/resources/1"
PATCH request:
curl -s -w "\n%{http_code}" \
-X PATCH \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"status": "active"}' \
"http://localhost:8000/api/resources/1"
The -w "\n%{http_code}" flag appends the status code on a new line after the response body. Parse the last line as the status code.
Request Construction (httpie)
GET request:
http GET http://localhost:8000/api/resources \
Authorization:"Bearer $TOKEN" \
--print=hb
POST request:
http POST http://localhost:8000/api/resources \
Authorization:"Bearer $TOKEN" \
name=test email=test@example.com \
--print=hb
Use --print=hb to show headers and body (useful for debugging). Use --print=b for body only.
Response Verification
Check status code:
STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X GET "http://localhost:8000/api/resources")
if [ "$STATUS" = "200" ]; then echo "PASS: status 200"; else echo "FAIL: expected 200, got $STATUS"; fi
Check response body with jq:
RESPONSE=$(curl -s -H "Content-Type: application/json" "http://localhost:8000/api/resources")
echo "$RESPONSE" | jq -e '.id' > /dev/null 2>&1 && echo "PASS: id exists" || echo "FAIL: id missing"
echo "$RESPONSE" | jq -e '.status == "active"' > /dev/null 2>&1 && echo "PASS: status is active" || echo "FAIL: status mismatch"
echo "$RESPONSE" | jq -e '.items | length > 0' > /dev/null 2>&1 && echo "PASS: items not empty" || echo "FAIL: items empty"
Without jq (fallback with grep):
RESPONSE=$(curl -s "http://localhost:8000/api/resources")
echo "$RESPONSE" | grep -q '"status":"active"' && echo "PASS" || echo "FAIL"
Credential Safety Rules
- NEVER log full DATABASE_URL, DB_PASSWORD, or connection strings in reports.
- Mask passwords:
postgres://admin:***@host/db
- Prefer test/local DB connections. If production credentials detected, abort and mark SKIP.
Database Verification Patterns
PostgreSQL (psql)
psql -h localhost -U user -d dbname -t -A -c "SELECT COUNT(*) FROM resources WHERE name = 'test';"
psql -h localhost -U user -d dbname -t -A -c "SELECT status FROM resources WHERE id = 1;"
psql -h localhost -U user -d dbname -t -A -c "SELECT COUNT(*) FROM resources WHERE id = 1;"
psql -h localhost -U user -d dbname -t -A -c "SELECT COUNT(*) FROM orders WHERE user_id = 1 AND status = 'completed';"
Flags: -t (tuples only, no headers), -A (unaligned output, no padding).
SQLite
sqlite3 db.sqlite3 "SELECT COUNT(*) FROM resources WHERE name = 'test';"
sqlite3 db.sqlite3 "SELECT status FROM resources WHERE id = 1;"
MySQL
mysql -h localhost -u user -p$DB_PASS dbname -N -e "SELECT COUNT(*) FROM resources WHERE name = 'test';"
Flag: -N (skip column names).
Connection String Detection
If the test plan doesn't specify DB connection details, look for them in:
.env or .env.local files
docker-compose.yml (service ports, credentials)
- Framework config files (
settings.py, database.yml, config/database.php)
- Environment variables:
DATABASE_URL, DB_HOST, DB_NAME, DB_USER, DB_PASSWORD
Error Handling Test Patterns
Missing required field
curl -s -w "\n%{http_code}" -X POST \
-H "Content-Type: application/json" \
-d '{"name": "test"}' \
"http://localhost:8000/api/resources"
Unauthenticated request
curl -s -w "\n%{http_code}" -X GET \
"http://localhost:8000/api/resources"
Insufficient permissions
curl -s -w "\n%{http_code}" -X DELETE \
-H "Authorization: Bearer $REGULAR_USER_TOKEN" \
"http://localhost:8000/api/admin/users/1"
Resource not found
curl -s -w "\n%{http_code}" -X GET \
-H "Authorization: Bearer $TOKEN" \
"http://localhost:8000/api/resources/99999"
Duplicate creation
curl -s -X POST -H "Content-Type: application/json" \
-d '{"email": "test@example.com"}' \
"http://localhost:8000/api/users"
curl -s -w "\n%{http_code}" -X POST -H "Content-Type: application/json" \
-d '{"email": "test@example.com"}' \
"http://localhost:8000/api/users"
Result Format
For each scenario, return results in this format:
### BE-XX: <scenario name>
- **Status:** PASS / FAIL / SKIP
- **Request:** <METHOD> <URL>
- **Response status:** <actual status code>
- **Response body:** <relevant excerpt or full body if short>
- **DB check:** <PASS/FAIL/SKIP — actual value vs expected>
- **Details:** <what was verified / what went wrong>
- **Edge cases:**
- <edge case 1>: PASS / FAIL — <details>
- <edge case 2>: PASS / FAIL — <details>
Error Handling
- If no HTTP client is available: mark ALL API scenarios as SKIP with reason
- If DB client is unavailable: execute API scenarios but mark DB Checks as SKIP
- If a request times out (>30s): mark as FAIL with "timeout" note
- If a connection is refused: mark as FAIL with "connection refused — is the server running?"
- If response is not valid JSON when expected: mark as FAIL, include raw response body