docs(test): record fixture ownership and timing contracts

Document the private test listener protocol, bounded observable readiness,
connection ownership and controlled timing assertions so new fixtures do
not reintroduce the same races. Replace advice to rerun transient failures
with instructions to preserve evidence and diagnose the underlying cause.

The listener environment is test-only rather than deployment configuration;
normal service configuration and the NixOS module are deliberately unchanged.
Validation: checked guidance against the implementation and targeted test
commands; formatting and whitespace checks pass.

Assisted-by: Codex (GPT-6)
This commit is contained in:
DanConwayDev
2026-09-12 14:49:09 +00:00
parent 59a37b660a
commit cac1256fb5
2 changed files with 47 additions and 2 deletions
+6 -2
View File
@@ -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
+41
View File
@@ -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.