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
+60
View File
@@ -0,0 +1,60 @@
# Opt-in Gitea Integration Tests (#66)
Real-Gitea integration tests for the shared API client
(`gitea_auth.api_request` / `api_get_all`). **Skipped by default** — the unit
suite (`pytest tests/ -q`) stays fast, mocked, and network-free.
## What they prove
Against a real, disposable Gitea instance:
- issue listing + pagination (multi-page walk, overall `limit`)
- PR listing through the same paginated client
- targeted label add/remove (one label removed, others untouched)
- permission denial fails closed (bad token → clear `401` error, token never echoed)
- real Gitea error payloads surface as safe, redacted `RuntimeError`s
## Environment variables
| Variable | Meaning |
|---|---|
| `GITEA_INTEGRATION=1` | opt-in switch — the suite is skipped without it |
| `GITEA_INTEGRATION_URL` | base URL (default `http://localhost:3003`) |
| `GITEA_INTEGRATION_TOKEN` | API token for the **local test** instance |
Never point these at a production Gitea and never use production tokens. The
token is used only in the `Authorization` header; tests assert it does not
appear in any error output.
## Quick start (Docker)
```bash
tests/integration/gitea-integration up
export GITEA_INTEGRATION_TOKEN="$(tests/integration/gitea-integration token)"
GITEA_INTEGRATION=1 ./venv/bin/python -m pytest tests/integration/ -q
tests/integration/gitea-integration down
```
- Image is **pinned** (`gitea/gitea:1.22.6` in `docker-compose.yml`); bump the
pin deliberately, never `:latest`.
- `up` waits until `/api/v1/version` answers (60s timeout).
- `token` idempotently creates a TEST-ONLY admin (`inttest-admin`) inside the
container and prints a fresh API token to stdout — nothing is written to disk.
- `down` removes the container **and its volume** (full teardown).
An existing local Gitea works too: set `GITEA_INTEGRATION_URL` and a token for
any account allowed to create/delete its own repos.
## Seed data and cleanup
- Each session creates one uniquely-named repo (`inttest-<8 hex>`), seeds
issues/labels/one PR inside it, and deletes the repo on teardown
(best-effort; a leaked repo is disposable and obvious).
- Nothing outside the seed repo is touched; the suite never mutates
pre-existing repos, users, or instance settings.
## Relationship to the unit suite
Unit tests remain the default and must stay mocked. Add an integration test
only for behavior that genuinely depends on a real server (pagination
metadata, permission semantics, live error payloads).