342 lines
11 KiB
Bash
342 lines
11 KiB
Bash
#!/usr/bin/env bash
|
||
# =============================================================================
|
||
# Scrum4Me — API curl test script
|
||
# =============================================================================
|
||
# Usage:
|
||
# 1. Start the dev server: npm run dev
|
||
# 2. Log in as "lars" in the UI and create an API token (Settings → API Tokens)
|
||
# 3. Copy the token and set it below
|
||
# 4. Run: bash scripts/test-api.sh
|
||
#
|
||
# Prerequisites:
|
||
# - Database seeded: npx prisma db seed
|
||
# - Dev server running on localhost:3000
|
||
# - A valid API token for user "lars"
|
||
# - curl installed
|
||
# =============================================================================
|
||
|
||
TOKEN="" # Paste your API token here
|
||
BASE_URL="http://localhost:3000"
|
||
|
||
# IDs — fill these in after running GET /api/products
|
||
PRODUCT_ID=""
|
||
SPRINT_ID=""
|
||
STORY_ID=""
|
||
TASK_ID=""
|
||
|
||
# =============================================================================
|
||
# Helpers
|
||
# =============================================================================
|
||
|
||
PASS=0
|
||
FAIL=0
|
||
|
||
check() {
|
||
local label="$1"
|
||
local expected="$2"
|
||
local actual="$3"
|
||
|
||
if [ "$actual" = "$expected" ]; then
|
||
echo " PASS $label"
|
||
PASS=$((PASS + 1))
|
||
else
|
||
echo " FAIL $label (expected HTTP $expected, got $actual)"
|
||
FAIL=$((FAIL + 1))
|
||
fi
|
||
}
|
||
|
||
header() {
|
||
echo ""
|
||
echo "── $1 ──────────────────────────────────────────────────────────"
|
||
}
|
||
|
||
auth_header() {
|
||
echo "Authorization: Bearer $TOKEN"
|
||
}
|
||
|
||
# =============================================================================
|
||
# TC-P-09 GET /api/products — happy path
|
||
# =============================================================================
|
||
|
||
test_products() {
|
||
header "GET /api/products"
|
||
|
||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||
-H "$(auth_header)" \
|
||
"$BASE_URL/api/products")
|
||
check "TC-P-09 happy path (lars)" 200 "$status"
|
||
|
||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||
"$BASE_URL/api/products")
|
||
check "TC-P-01 no token → 401" 401 "$status"
|
||
|
||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||
-H "Authorization: Bearer invalid-token-xyz" \
|
||
"$BASE_URL/api/products")
|
||
check "TC-P-02 invalid token → 401" 401 "$status"
|
||
|
||
# Capture product list and extract first product ID for downstream tests
|
||
response=$(curl -s -H "$(auth_header)" "$BASE_URL/api/products")
|
||
echo " Response: $response" | head -c 300
|
||
echo ""
|
||
}
|
||
|
||
# =============================================================================
|
||
# TC-NS-08 GET /api/products/:id/next-story — happy path
|
||
# =============================================================================
|
||
|
||
test_next_story() {
|
||
header "GET /api/products/:id/next-story"
|
||
|
||
if [ -z "$PRODUCT_ID" ]; then
|
||
echo " SKIP PRODUCT_ID not set — fill in scripts/test-api.sh"
|
||
return
|
||
fi
|
||
|
||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||
-H "$(auth_header)" \
|
||
"$BASE_URL/api/products/$PRODUCT_ID/next-story")
|
||
check "TC-NS-08 happy path (lars)" "200 or 404" "$status"
|
||
|
||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||
"$BASE_URL/api/products/$PRODUCT_ID/next-story")
|
||
check "TC-NS-01 no token → 401" 401 "$status"
|
||
}
|
||
|
||
# =============================================================================
|
||
# TC-ST-09 GET /api/sprints/:id/tasks — happy path
|
||
# =============================================================================
|
||
|
||
test_sprint_tasks() {
|
||
header "GET /api/sprints/:id/tasks"
|
||
|
||
if [ -z "$SPRINT_ID" ]; then
|
||
echo " SKIP SPRINT_ID not set — fill in scripts/test-api.sh"
|
||
return
|
||
fi
|
||
|
||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||
-H "$(auth_header)" \
|
||
"$BASE_URL/api/sprints/$SPRINT_ID/tasks?limit=10")
|
||
check "TC-ST-09 happy path with limit=10" 200 "$status"
|
||
|
||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||
"$BASE_URL/api/sprints/$SPRINT_ID/tasks")
|
||
check "TC-ST-01 no token → 401" 401 "$status"
|
||
}
|
||
|
||
# =============================================================================
|
||
# TC-L-17–19 POST /api/stories/:id/log — all 3 log types
|
||
# =============================================================================
|
||
|
||
test_story_log() {
|
||
header "POST /api/stories/:id/log"
|
||
|
||
if [ -z "$STORY_ID" ]; then
|
||
echo " SKIP STORY_ID not set — fill in scripts/test-api.sh"
|
||
return
|
||
fi
|
||
|
||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||
-X POST \
|
||
-H "$(auth_header)" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"type":"IMPLEMENTATION_PLAN","content":"Aanpak: stap 1 implementeer, stap 2 test."}' \
|
||
"$BASE_URL/api/stories/$STORY_ID/log")
|
||
check "TC-L-17 IMPLEMENTATION_PLAN" 201 "$status"
|
||
|
||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||
-X POST \
|
||
-H "$(auth_header)" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"type":"TEST_RESULT","content":"Alle tests geslaagd.","status":"PASSED"}' \
|
||
"$BASE_URL/api/stories/$STORY_ID/log")
|
||
check "TC-L-18 TEST_RESULT PASSED" 201 "$status"
|
||
|
||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||
-X POST \
|
||
-H "$(auth_header)" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"type":"COMMIT","content":"feat: implementatie afgerond","commit_hash":"abc1234","commit_message":"feat: ST-XXX implementatie"}' \
|
||
"$BASE_URL/api/stories/$STORY_ID/log")
|
||
check "TC-L-19 COMMIT" 201 "$status"
|
||
|
||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||
-X POST \
|
||
-H "$(auth_header)" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"type":"UNKNOWN","content":"test"}' \
|
||
"$BASE_URL/api/stories/$STORY_ID/log")
|
||
check "TC-L-07 invalid type → 400" 400 "$status"
|
||
|
||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||
-X POST \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"type":"IMPLEMENTATION_PLAN","content":"test"}' \
|
||
"$BASE_URL/api/stories/$STORY_ID/log")
|
||
check "TC-L-01 no token → 401" 401 "$status"
|
||
}
|
||
|
||
# =============================================================================
|
||
# TC-RO-10 PATCH /api/stories/:id/tasks/reorder — happy path
|
||
# =============================================================================
|
||
|
||
test_reorder() {
|
||
header "PATCH /api/stories/:id/tasks/reorder"
|
||
|
||
if [ -z "$STORY_ID" ]; then
|
||
echo " SKIP STORY_ID not set — fill in scripts/test-api.sh"
|
||
return
|
||
fi
|
||
|
||
# Fetch current task IDs for the story first
|
||
task_ids=$(curl -s \
|
||
-H "$(auth_header)" \
|
||
"$BASE_URL/api/sprints/$SPRINT_ID/tasks?limit=10" \
|
||
| grep -o '"id":"[^"]*"' | sed 's/"id":"//;s/"//' | head -3 | tr '\n' ',' | sed 's/,$//')
|
||
|
||
if [ -z "$task_ids" ]; then
|
||
echo " SKIP no tasks found for reorder test"
|
||
return
|
||
fi
|
||
|
||
ids_json=$(echo "$task_ids" | awk -F',' '{for(i=1;i<=NF;i++) printf "\"%s\"%s", $i, (i<NF?",":"")}')
|
||
|
||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||
-X PATCH \
|
||
-H "$(auth_header)" \
|
||
-H "Content-Type: application/json" \
|
||
-d "{\"task_ids\":[$ids_json]}" \
|
||
"$BASE_URL/api/stories/$STORY_ID/tasks/reorder")
|
||
check "TC-RO-10 happy path" 200 "$status"
|
||
|
||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||
-X PATCH \
|
||
-H "$(auth_header)" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"task_ids":[]}' \
|
||
"$BASE_URL/api/stories/$STORY_ID/tasks/reorder")
|
||
check "TC-RO-06 empty task_ids → 400" 400 "$status"
|
||
|
||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||
-X PATCH \
|
||
-H "Content-Type: application/json" \
|
||
-d "{\"task_ids\":[$ids_json]}" \
|
||
"$BASE_URL/api/stories/$STORY_ID/tasks/reorder")
|
||
check "TC-RO-01 no token → 401" 401 "$status"
|
||
}
|
||
|
||
# =============================================================================
|
||
# TC-T-12–13 PATCH /api/tasks/:id — status updates
|
||
# =============================================================================
|
||
|
||
test_tasks() {
|
||
header "PATCH /api/tasks/:id"
|
||
|
||
if [ -z "$TASK_ID" ]; then
|
||
echo " SKIP TASK_ID not set — fill in scripts/test-api.sh"
|
||
return
|
||
fi
|
||
|
||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||
-X PATCH \
|
||
-H "$(auth_header)" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"status":"IN_PROGRESS"}' \
|
||
"$BASE_URL/api/tasks/$TASK_ID")
|
||
check "TC-T-12 status → IN_PROGRESS" 200 "$status"
|
||
|
||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||
-X PATCH \
|
||
-H "$(auth_header)" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"status":"DONE"}' \
|
||
"$BASE_URL/api/tasks/$TASK_ID")
|
||
check "TC-T-13 status → DONE" 200 "$status"
|
||
|
||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||
-X PATCH \
|
||
-H "$(auth_header)" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"status":"INVALID"}' \
|
||
"$BASE_URL/api/tasks/$TASK_ID")
|
||
check "TC-T-06 invalid status → 400" 400 "$status"
|
||
|
||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||
-X PATCH \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"status":"DONE"}' \
|
||
"$BASE_URL/api/tasks/$TASK_ID")
|
||
check "TC-T-01 no token → 401" 401 "$status"
|
||
}
|
||
|
||
# =============================================================================
|
||
# TC-TD-09–10 POST /api/todos — with and without product
|
||
# =============================================================================
|
||
|
||
test_todos() {
|
||
header "POST /api/todos"
|
||
|
||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||
-X POST \
|
||
-H "$(auth_header)" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"title":"Test todo zonder product"}' \
|
||
"$BASE_URL/api/todos")
|
||
check "TC-TD-09 happy path without product_id" 201 "$status"
|
||
|
||
if [ -n "$PRODUCT_ID" ]; then
|
||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||
-X POST \
|
||
-H "$(auth_header)" \
|
||
-H "Content-Type: application/json" \
|
||
-d "{\"title\":\"Test todo met product\",\"product_id\":\"$PRODUCT_ID\"}" \
|
||
"$BASE_URL/api/todos")
|
||
check "TC-TD-10 happy path with product_id" 201 "$status"
|
||
fi
|
||
|
||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||
-X POST \
|
||
-H "$(auth_header)" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"title":""}' \
|
||
"$BASE_URL/api/todos")
|
||
check "TC-TD-05 empty title → 400" 400 "$status"
|
||
|
||
status=$(curl -s -o /dev/null -w "%{http_code}" \
|
||
-X POST \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"title":"Test"}' \
|
||
"$BASE_URL/api/todos")
|
||
check "TC-TD-01 no token → 401" 401 "$status"
|
||
}
|
||
|
||
# =============================================================================
|
||
# Run all tests
|
||
# =============================================================================
|
||
|
||
echo "============================================================"
|
||
echo " Scrum4Me API Test Suite"
|
||
echo " Base URL: $BASE_URL"
|
||
echo "============================================================"
|
||
|
||
if [ -z "$TOKEN" ]; then
|
||
echo ""
|
||
echo "ERROR: TOKEN is not set. Edit scripts/test-api.sh and add your API token."
|
||
exit 1
|
||
fi
|
||
|
||
test_products
|
||
test_next_story
|
||
test_sprint_tasks
|
||
test_story_log
|
||
test_reorder
|
||
test_tasks
|
||
test_todos
|
||
|
||
echo ""
|
||
echo "============================================================"
|
||
echo " Results: $PASS passed, $FAIL failed"
|
||
echo "============================================================"
|
||
|
||
[ "$FAIL" -eq 0 ] && exit 0 || exit 1
|