# Tests Deterministic and hermetic. Integration tests stand up a local mock OIDC/OAuth2 server on an ephemeral port; no traffic to a real Gitea. ```bash just test # full suite python3 -m unittest discover -t . -s tests -p 'test_*.py' -v bash tests/test_forge_auth_integration.sh bash tests/test_setup_args.sh bash tests/test_setup_deploy_flag.sh bash tests/test_doctor.sh bash tests/test_next_steps.sh ``` ## Inventory - **`test_forge_auth.py`**: `scripts/forge_auth.py` unit tests: PKCE pair generation, HMAC state signing + CSRF rejection, `ForgeAuthConfig.from_env` validation (loopback-only redirect, missing port, missing env vars, `FORGE_GITEA_USERNAME` propagation), `build_authorize_url` with `prompt=login` and `login_hint`, `build_gitea_logout_url`, `AuthFile` read/write/merge/`has_live_gitea_token`, `auth_store_path` precedence, `run_logout`, `main()` dispatcher. - **`test_git_credential_forge.py`**: `scripts/git-credential-forge.py` unit tests: credential protocol I/O, host/scheme/port matching, live-token fast-path, pass-through for missing store or non-matching host, expired-token refresh, refresh-failure handling, `store` / `erase` no-ops, `main()` dispatcher. - **`test_forge_auth_integration.py`**: end-to-end Python integration tests against `tests/mock_oidc_server.py`: full PKCE flow, gateway-required schema on disk, idempotent re-login, refresh-token rotation with server-side revocation, logout preserving gateway-bearer fields. - **`test_forge_auth_integration.sh`**: shell end-to-end: drives `forge_auth.py login` against the mock server, installs the credential helper into a sandboxed `$HOME`, and exercises `git credential fill`. Covers URL matching, `github.com` non-leakage, rotated-token pickup, `just logout` teardown, and the username-mismatch guard (login fails, auth file untouched, Gitea logout URL surfaced, authorise URL carries `prompt=login` + `login_hint`). - **`test_setup_args.sh`**: `scripts/setup.sh` coverage: argument parsing, `--help`, `--headless` wiring to `forge_login.sh --no-browser`, the `--headless + FORGE_SETUP_YES=1` hang guard, live-token reuse, silent-refresh rescue, `prompt_choice` non-tty stdout isolation. - **`test_setup_deploy_flag.sh`**: `scripts/setup.sh --deploy` coverage: `--deploy` is parsed and surfaced in `--help`, `setup.sh` with `--deploy` swaps the manifest/recipe pair to `operator_setup_steps.json` / `operator-setup`, non-interactive runs either hand off or print the `just run-next-steps --manifest operator_setup_steps.json --recipe operator-setup` follow-up hint, and `next_steps.sh --run --manifest ... --recipe operator-setup` execs the expected recipe in the orchestrator checkout. - **`test_doctor.sh`**: `scripts/doctor.sh`: miss-path under a sandboxed PATH, asserts every `[miss]` line is followed by a `fix:` line and the consolidated block is emitted. - **`test_next_steps.sh`**: `scripts/next_steps.sh` contract: operator manifest / recipe is the default, `--manifest contributor_setup_steps.json --recipe contributor-setup` swaps to the contributor runner, missing manifest warns and points at the orchestrator README, `--headless` / `--yes` / `--skip-optional` / `--only ID` forward verbatim to the selected `just `. Fixtures use generic step ids (the real manifests ship with the orchestrator). - **`mock_oidc_server.py`**: test fixture implementing `/.well-known/openid-configuration`, `/login/oauth/authorize`, `/login/oauth/access_token`, `/login/oauth/userinfo`. PKCE verification on `authorization_code`; rotation + revocation on `refresh_token`. ## Adding tests - Python: drop `test_*.py` in this directory, use `unittest`, stdlib only. - Shell: executable, deterministic, non-interactive. Use ephemeral ports via `python3 -c 'import socket; ...'` and sandbox `$HOME` with `mktemp -d`. Stub external binaries (e.g. `just`, `git`) by dropping a script into a temp `fakebin/` and prepending it to `PATH`.