292 lines
11 KiB
Bash
Executable File
292 lines
11 KiB
Bash
Executable File
#!/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?inProgress=true")
|
|
respond '{"sprints":[{"id":"s1","name":"Sprint 1","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","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"}}' 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
|
|
;;
|
|
"POST http://localhost:3000/api/sprints/close")
|
|
respond '{"success":true}' 200
|
|
;;
|
|
"GET http://localhost:3000/api/tasks"|\
|
|
"GET http://localhost:3000/api/tasks?scope=active-sprint")
|
|
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
|
|
task_csv_status_json=$("$ROOT_DIR/scripts/task.sh" list --status open,todo,in-progress --json)
|
|
if [[ "$(echo "$task_csv_status_json" | jq 'length')" -ne 1 ]]; then
|
|
echo "Expected comma-separated --status filter to match task list output" >&2
|
|
echo "$task_csv_status_json" >&2
|
|
exit 1
|
|
fi
|
|
"$ROOT_DIR/scripts/task.sh" get t1 >/dev/null
|
|
"$ROOT_DIR/scripts/task.sh" current-sprint >/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" --goal "Ship" --start-date "2026-02-24" --end-date "2026-03-03" >/dev/null
|
|
"$ROOT_DIR/scripts/sprint.sh" update "Sprint 1" --goal "Updated Goal" >/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" "2026-02-24" "2026-03-03" "Goal" >/dev/null
|
|
"$ROOT_DIR/scripts/gantt.sh" sprint update s1 goal "Updated Goal" >/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?inProgress=true"
|
|
assert_log_contains "GET http://localhost:3000/api/sprints/s1"
|
|
assert_log_contains "GET http://localhost:3000/api/sprints/current"
|
|
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=active-sprint"
|
|
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"
|