#!/bin/bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" TMP_DIR="$(mktemp -d)" MOCK_BIN="$TMP_DIR/mockbin" MOCK_LOG="$TMP_DIR/mock-curl.log" cleanup() { rm -rf "$TMP_DIR" } trap cleanup EXIT mkdir -p "$MOCK_BIN" touch "$MOCK_LOG" cat > "$MOCK_BIN/curl" <<'MOCK_CURL' #!/bin/bash set -euo pipefail method="GET" url="" data="" cookie_out="" while [[ $# -gt 0 ]]; do case "$1" in -X) method="$2" shift 2 ;; --data|-d) data="$2" shift 2 ;; -c) cookie_out="$2" shift 2 ;; -b|-H|-w) shift 2 ;; -s|-sS) shift ;; http://*|https://*) url="$1" shift ;; *) shift ;; esac done if [[ -n "$cookie_out" ]]; then mkdir -p "$(dirname "$cookie_out")" touch "$cookie_out" fi echo "${method} ${url} ${data}" >> "${MOCK_CURL_LOG:?}" respond() { printf '%s\n%s\n' "$1" "$2" } case "${method} ${url}" in "GET http://localhost:3000/api/projects") respond '{"projects":[{"id":"p1","name":"Proj","description":"Demo","color":"#3b82f6"}]}' 200 ;; "GET http://localhost:3000/api/projects/p1") respond '{"project":{"id":"p1","name":"Proj","description":"Demo","color":"#3b82f6"}}' 200 ;; "POST http://localhost:3000/api/projects") respond '{"success":true,"project":{"id":"p2","name":"New Proj"}}' 200 ;; "PATCH http://localhost:3000/api/projects") respond '{"success":true}' 200 ;; "DELETE http://localhost:3000/api/projects") respond '{"success":true}' 200 ;; "GET http://localhost:3000/api/auth/users") respond '{"users":[{"id":"u1","name":"Max","email":"max@example.com"}]}' 200 ;; "POST http://localhost:3000/api/auth/login") respond '{"success":true}' 200 ;; "POST http://localhost:3000/api/auth/logout") respond '{"success":true}' 200 ;; "GET http://localhost:3000/api/auth/session") respond '{"authenticated":true}' 200 ;; "POST http://localhost:3000/api/auth/register") respond '{"success":true}' 200 ;; "POST http://localhost:3000/api/auth/forgot-password") respond '{"success":true}' 200 ;; "POST http://localhost:3000/api/auth/reset-password") respond '{"success":true}' 200 ;; "PATCH http://localhost:3000/api/auth/account") respond '{"success":true}' 200 ;; "GET http://localhost:3000/api/sprints"|\ "GET http://localhost:3000/api/sprints?status=active") respond '{"sprints":[{"id":"s1","name":"Sprint 1","projectId":"p1","status":"active","startDate":"2026-02-20","endDate":"2026-03-01"}]}' 200 ;; "GET http://localhost:3000/api/sprints/s1") respond '{"sprint":{"id":"s1","name":"Sprint 1","projectId":"p1","status":"active","startDate":"2026-02-20","endDate":"2026-03-01"}}' 200 ;; "POST http://localhost:3000/api/sprints") respond '{"success":true,"sprint":{"id":"s2","name":"Sprint 2","projectId":"p1"}}' 200 ;; "PATCH http://localhost:3000/api/sprints") respond '{"success":true}' 200 ;; "DELETE http://localhost:3000/api/sprints") respond '{"success":true}' 200 ;; "GET http://localhost:3000/api/sprints/current") respond '{"sprint":{"id":"s1","name":"Sprint 1"}}' 200 ;; "GET http://localhost:3000/api/sprints/current?projectId=p1") respond '{"sprint":{"id":"s1","name":"Sprint 1","projectId":"p1"}}' 200 ;; "POST http://localhost:3000/api/sprints/close") respond '{"success":true}' 200 ;; "GET http://localhost:3000/api/tasks"|\ "GET http://localhost:3000/api/tasks?scope=all") respond '{"tasks":[{"id":"t1","title":"Demo Task","status":"open","priority":"medium","type":"task","projectId":"p1","assigneeId":"u1","sprintId":"s1","comments":[],"tags":[],"attachments":[]}]}' 200 ;; "GET http://localhost:3000/api/tasks?taskId=t1&include=detail") respond '{"tasks":[{"id":"t1","title":"Demo Task","status":"open","priority":"medium","type":"task","projectId":"p1","assigneeId":"u1","sprintId":"s1","comments":[],"tags":[],"attachments":[]}]}' 200 ;; "POST http://localhost:3000/api/tasks") respond '{"success":true,"task":{"id":"t1"}}' 200 ;; "DELETE http://localhost:3000/api/tasks") respond '{"success":true}' 200 ;; "POST http://localhost:3000/api/tasks/natural") respond '{"success":true,"task":{"id":"t2","title":"Natural"}}' 200 ;; "GET http://localhost:3000/api/debug") respond '{"ok":true}' 200 ;; *) respond "{\"error\":\"Unhandled mock request: ${method} ${url}\"}" 500 ;; esac MOCK_CURL chmod +x "$MOCK_BIN/curl" assert_log_contains() { local expected="$1" if ! grep -F "$expected" "$MOCK_LOG" >/dev/null 2>&1; then echo "Expected mock curl log to contain: $expected" >&2 echo "Actual log:" >&2 cat "$MOCK_LOG" >&2 exit 1 fi } export HOME="$TMP_DIR/home" export PATH="$MOCK_BIN:$PATH" export MOCK_CURL_LOG="$MOCK_LOG" export API_URL="http://localhost:3000/api" ATTACH_FILE="$TMP_DIR/attachment.txt" BULK_FILE="$TMP_DIR/tasks.json" cat > "$ATTACH_FILE" <<'ATTACH_EOF' refactor-test ATTACH_EOF cat > "$BULK_FILE" <<'BULK_EOF' [ { "title": "Bulk task", "project": "Proj", "assignee": "Max", "sprint": "Sprint 1" } ] BULK_EOF "$ROOT_DIR/scripts/task.sh" list --json >/dev/null "$ROOT_DIR/scripts/task.sh" get t1 >/dev/null "$ROOT_DIR/scripts/task.sh" current-sprint >/dev/null "$ROOT_DIR/scripts/task.sh" current-sprint --project Proj >/dev/null "$ROOT_DIR/scripts/task.sh" create --title "API Task" --project "Proj" --assignee "Max" --sprint current --status todo --priority high >/dev/null "$ROOT_DIR/scripts/task.sh" update t1 --status done --tags "api,refactor" --add-comment "done" >/dev/null "$ROOT_DIR/scripts/task.sh" delete t1 >/dev/null "$ROOT_DIR/scripts/task.sh" bulk-create "$BULK_FILE" >/dev/null "$ROOT_DIR/scripts/project.sh" list --json >/dev/null "$ROOT_DIR/scripts/project.sh" get "Proj" >/dev/null "$ROOT_DIR/scripts/project.sh" create --name "New Proj" --description "Desc" --color "#ffffff" >/dev/null "$ROOT_DIR/scripts/project.sh" update "Proj" --name "Renamed Proj" >/dev/null "$ROOT_DIR/scripts/project.sh" delete "Proj" >/dev/null "$ROOT_DIR/scripts/sprint.sh" list --json >/dev/null "$ROOT_DIR/scripts/sprint.sh" list --active --json >/dev/null "$ROOT_DIR/scripts/sprint.sh" get "Sprint 1" >/dev/null "$ROOT_DIR/scripts/sprint.sh" create --name "Sprint 2" --project "Proj" --goal "Ship" --start-date "2026-02-24" --end-date "2026-03-03" >/dev/null "$ROOT_DIR/scripts/sprint.sh" update "Sprint 1" --status completed >/dev/null "$ROOT_DIR/scripts/sprint.sh" close "Sprint 1" >/dev/null "$ROOT_DIR/scripts/sprint.sh" delete "Sprint 1" >/dev/null "$ROOT_DIR/scripts/gantt.sh" task list open >/dev/null "$ROOT_DIR/scripts/gantt.sh" task get t1 >/dev/null "$ROOT_DIR/scripts/gantt.sh" task create "From wrapper" todo high p1 >/dev/null "$ROOT_DIR/scripts/gantt.sh" task natural "Create natural task" >/dev/null "$ROOT_DIR/scripts/gantt.sh" task update t1 status done >/dev/null "$ROOT_DIR/scripts/gantt.sh" task comment t1 "Looks good" >/dev/null "$ROOT_DIR/scripts/gantt.sh" task attach t1 "$ATTACH_FILE" >/dev/null "$ROOT_DIR/scripts/gantt.sh" task delete t1 >/dev/null "$ROOT_DIR/scripts/gantt.sh" project list >/dev/null "$ROOT_DIR/scripts/gantt.sh" project get p1 >/dev/null "$ROOT_DIR/scripts/gantt.sh" project create "Wrapper Project" "Desc" "#111111" >/dev/null "$ROOT_DIR/scripts/gantt.sh" project update p1 name "Renamed" >/dev/null "$ROOT_DIR/scripts/gantt.sh" project delete p1 >/dev/null "$ROOT_DIR/scripts/gantt.sh" sprint list >/dev/null "$ROOT_DIR/scripts/gantt.sh" sprint get s1 >/dev/null "$ROOT_DIR/scripts/gantt.sh" sprint create "Wrapper Sprint" p1 "2026-02-24" "2026-03-03" "Goal" >/dev/null "$ROOT_DIR/scripts/gantt.sh" sprint update s1 status completed >/dev/null "$ROOT_DIR/scripts/gantt.sh" sprint close s1 >/dev/null "$ROOT_DIR/scripts/gantt.sh" sprint delete s1 >/dev/null "$ROOT_DIR/scripts/gantt.sh" auth login "max@example.com" "secret" >/dev/null "$ROOT_DIR/scripts/gantt.sh" auth logout >/dev/null "$ROOT_DIR/scripts/gantt.sh" auth session >/dev/null "$ROOT_DIR/scripts/gantt.sh" auth register "new@example.com" "secret" "New User" >/dev/null "$ROOT_DIR/scripts/gantt.sh" auth forgot-password "new@example.com" >/dev/null "$ROOT_DIR/scripts/gantt.sh" auth reset-password "tok123" "newsecret" >/dev/null "$ROOT_DIR/scripts/gantt.sh" auth account name "Renamed User" >/dev/null "$ROOT_DIR/scripts/gantt.sh" auth users >/dev/null "$ROOT_DIR/scripts/gantt.sh" debug >/dev/null if [[ ! -f "$HOME/.config/gantt-board/cookies.txt" ]]; then echo "Expected cookie file to be created at $HOME/.config/gantt-board/cookies.txt" >&2 exit 1 fi assert_log_contains "GET http://localhost:3000/api/projects" assert_log_contains "POST http://localhost:3000/api/projects" assert_log_contains "PATCH http://localhost:3000/api/projects" assert_log_contains "DELETE http://localhost:3000/api/projects" assert_log_contains "GET http://localhost:3000/api/projects/p1" assert_log_contains "GET http://localhost:3000/api/sprints" assert_log_contains "GET http://localhost:3000/api/sprints?status=active" assert_log_contains "GET http://localhost:3000/api/sprints/s1" assert_log_contains "GET http://localhost:3000/api/sprints/current" assert_log_contains "GET http://localhost:3000/api/sprints/current?projectId=p1" assert_log_contains "POST http://localhost:3000/api/sprints" assert_log_contains "PATCH http://localhost:3000/api/sprints" assert_log_contains "POST http://localhost:3000/api/sprints/close" assert_log_contains "DELETE http://localhost:3000/api/sprints" assert_log_contains "GET http://localhost:3000/api/tasks" assert_log_contains "GET http://localhost:3000/api/tasks?scope=all" assert_log_contains "GET http://localhost:3000/api/tasks?taskId=t1&include=detail" assert_log_contains "POST http://localhost:3000/api/tasks" assert_log_contains "DELETE http://localhost:3000/api/tasks" assert_log_contains "POST http://localhost:3000/api/tasks/natural" assert_log_contains "POST http://localhost:3000/api/auth/login" assert_log_contains "POST http://localhost:3000/api/auth/logout" assert_log_contains "GET http://localhost:3000/api/auth/session" assert_log_contains "POST http://localhost:3000/api/auth/register" assert_log_contains "POST http://localhost:3000/api/auth/forgot-password" assert_log_contains "POST http://localhost:3000/api/auth/reset-password" assert_log_contains "PATCH http://localhost:3000/api/auth/account" assert_log_contains "GET http://localhost:3000/api/auth/users" assert_log_contains "GET http://localhost:3000/api/debug" if rg -n --hidden -S "rest/v1|SUPABASE_URL|SERVICE_KEY|ANON_KEY|qnatchrjlpehiijwtreh" "$ROOT_DIR/scripts" --glob "!scripts/tests/*" >/dev/null 2>&1; then echo "Direct Supabase references were found in scripts/" >&2 rg -n --hidden -S "rest/v1|SUPABASE_URL|SERVICE_KEY|ANON_KEY|qnatchrjlpehiijwtreh" "$ROOT_DIR/scripts" --glob "!scripts/tests/*" >&2 exit 1 fi echo "CLI API passthrough tests passed"