feat: opt-in Docker-based Gitea integration test suite (#66)

Adds tests/integration/: an optional real-Gitea suite, skipped by default,
enabled only by GITEA_INTEGRATION=1.

- docker-compose.yml pins gitea/gitea:1.22.6 (disposable container, port 3003)
- gitea-integration helper: up (wait-ready) / token (test-only admin, token to
  stdout only) / down (removes container + volume)
- conftest.py: session fixtures; unique disposable seed repo (inttest-<hex>)
  created via API and deleted on teardown
- test_gitea_live.py (6 tests, via shared api_request/api_get_all client):
  issue pagination multi-page walk + overall limit, PR listing, targeted label
  add/remove leaves other labels intact, bad-token 401 fails closed without
  echoing the credential, real 404 payload surfaces as safe redacted error
- README + developer-testing-guidelines section 8 updated (planned -> real)

Default pytest tests/ -q: 355 passed, 6 skipped (unit suite unchanged, no
network). Live verification: 6 passed against the pinned container. No
production credentials; token never logged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-02 16:11:48 -04:00
parent 02c0c2023b
commit 22347bd549
7 changed files with 327 additions and 8 deletions
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env bash
set -euo pipefail
# gitea-integration — manage the disposable Gitea container for the opt-in
# integration suite (#66). See tests/integration/README.md.
#
# tests/integration/gitea-integration up start pinned Gitea, wait ready
# tests/integration/gitea-integration token create test admin + print token
# tests/integration/gitea-integration down stop container, delete volume
#
# The admin credentials below are TEST-ONLY, for the throwaway local container
# started by this script. They are not secrets and must never be reused for a
# real instance. The generated API token is printed ONCE to stdout (for
# `export GITEA_INTEGRATION_TOKEN=$(...)`) and is never written to any file.
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
compose() { docker compose -f "$here/docker-compose.yml" "$@"; }
base_url="${GITEA_INTEGRATION_URL:-http://localhost:3003}"
admin_user="inttest-admin"
admin_pass="inttest-local-only-pass" # TEST-ONLY, disposable container
admin_email="inttest-admin@example.invalid"
case "${1:-}" in
up)
compose up -d
printf 'gitea-integration: waiting for %s ' "$base_url"
for _ in $(seq 1 60); do
if curl -fsS "$base_url/api/v1/version" >/dev/null 2>&1; then
printf '\ngitea-integration: ready\n'
exit 0
fi
printf '.'
sleep 1
done
printf '\ngitea-integration: Gitea did not become ready in 60s\n' >&2
exit 1
;;
token)
# Idempotent admin creation (ignore "already exists").
compose exec -T -u git gitea gitea admin user create \
--username "$admin_user" --password "$admin_pass" \
--email "$admin_email" --admin --must-change-password=false \
>/dev/null 2>&1 || true
# Unique token name per call; Gitea prints the token as the last field.
tok_name="inttest-$(date +%s)"
out="$(compose exec -T -u git gitea gitea admin user generate-access-token \
--username "$admin_user" --scopes all --token-name "$tok_name")"
printf '%s\n' "$out" | sed -n 's/.*successfully created[:!]* *//p' | tr -d '[:space:]'
printf '\n'
;;
down)
compose down -v
;;
*)
printf 'usage: tests/integration/gitea-integration up|token|down\n' >&2
exit 2
;;
esac