diff --git a/AGENTS.md b/AGENTS.md index 869c99a..4645e9c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -121,10 +121,14 @@ nix develop -c cargo test -p grasp-audit --lib specific_test_name -- --nocapture ### Troubleshooting **Buffer Size Errors:** -If you see mpsc channel buffer size panics on first test run, this is usually transient. Simply run the tests again. +Capture the failing test and panic before retrying. Diagnose the fixture or +capacity assumption; a successful rerun alone does not establish correctness. **Port Conflicts:** -Both `TestRelay` and `test-ngit-relay.sh` use random ports to avoid conflicts. If you see port errors, ensure no stale processes are running. +`TestRelay` transfers an owned loopback listener into the subprocess and +retains it across restarts. Do not release a reservation and rebind its port. +See [test fixture guidance](docs/how-to/test-fixtures.md) for readiness, +shutdown, and timing rules. Audit external server scripts separately. ## Code Patterns diff --git a/docs/how-to/test-fixtures.md b/docs/how-to/test-fixtures.md new file mode 100644 index 0000000..1f693d3 --- /dev/null +++ b/docs/how-to/test-fixtures.md @@ -0,0 +1,41 @@ +# Reliable test fixtures + +Run tests through the repository development shell. Keep parallel checks +enabled: each fixture owns its socket, tasks, subprocesses and temporary data. + +## Socket ownership + +Allocate loopback port zero and transfer the bound listener into the server. +Do not bind, inspect the port, drop the socket and bind that port again. +Subprocess fixtures pass `NGIT_TEST_LISTENER_FD` with `NGIT_TEST=1` on Unix. +The binary advertises this private protocol through +`--internal-test-listener-support`; it is not deployment configuration and is +not exposed through the NixOS module or example service environment. + +`TestRelay` retains a parent copy across same-address restarts. Offline relay +scenarios use `UnavailableEndpoint`, which accepts and closes connections +while retaining the socket, then transfers that socket to the recovered +server. This prevents another parallel test from taking the offline address. + +## Observable readiness and shutdown + +Wait for HTTP readiness, connected-state metrics, or the expected event with +a bounded deadline. A successful TCP connection, a connection attempt counter, +and an arbitrary grace period do not prove that a server is ready. + +Connection tasks belong to their fixture's accept loop. Stopping the fixture +cancels and joins them; dropping it cancels the owner. Git fixture subprocesses +are cancelled with their request, and all three pipes are driven concurrently. + +## Time and streaming assertions + +Age private cache timestamps explicitly and bracket wall-clock timestamps +before and after the operation. Streaming tests coordinate fake Git with an +owned loopback gate, collect bytes independently of HTTP frame boundaries, +and retain assertions about terminal flush and post-push promotion ordering. +Fixed sleeps remain appropriate only when elapsed time is itself under test; +polling an observable condition must always have a bounded deadline. + +Targeted regressions include `fixture_lifecycle`, `relay_identity`, +`git_response_streaming`, and the shared Git-server tests in `sync`. +Full package validation remains necessary after these scoped checks.