fix(audio-rooms): retry mintToken on transport hiccup + harness /health warmup
The remaining interop failures all root in the same window: a stale
keep-alive pool entry from one test class is reused on the FIRST POST
of the next test class, the connection RSTs as the request body
writes, and OkHttp's built-in retryOnConnectionFailure won't retry a
POST after any byte of the body has gone out. Same situation hits a
phone client whose Wi-Fi hands off mid-mint.
Two fixes, both production-shaped:
- OkHttpNestsClient.mintToken now wraps execute() in
executeWithTransportRetry(): one retry on SocketException /
EOFException / generic IOException. Request builders are
immutable, so the second pass opens a fresh connection cleanly.
HTTP error status codes (4xx / 5xx) and malformed responses are
NOT retried — they go to the caller as before.
- NostrNestsHarness now polls GET /health until it returns 200
after the port-probe succeeds. moq-auth's Node runtime opens its
listen socket before the request handlers are wired, so a POST
that arrives in that window can RST. Waiting for /health proves
the request pipeline is live, eliminating the SocketException
that hit the first test of every test class run after
AuthEndpoints.
Symptoms fixed:
- NostrNestsAuthFailureInteropTest.missing_authorization_header_is_rejected_401
-> SocketException
- NostrNestsRoundTripInteropTest.production_speaker_broadcasts_to_production_listener_via_real_relay
-> "Failed to reach http://127.0.0.1:8090/auth"
- NostrNestsMultiPeerInteropTest.* (3 tests, same root cause)
This commit is contained in:
+43
@@ -154,6 +154,13 @@ class NostrNestsHarness private constructor(
|
||||
try {
|
||||
waitForPort("127.0.0.1", AUTH_HOST_PORT, PORT_READY_TIMEOUT_MS)
|
||||
waitForPort("127.0.0.1", MOQ_HOST_PORT, PORT_READY_TIMEOUT_MS)
|
||||
// moq-auth's Node runtime opens the listen socket
|
||||
// before its handlers are wired, so the first POST that
|
||||
// arrives in the gap can RST-on-write. Wait for /health
|
||||
// to actually return 200 — that proves the request
|
||||
// pipeline is live and avoids the SocketException that
|
||||
// otherwise hits the first test of the second class.
|
||||
waitForHealth("http://127.0.0.1:$AUTH_HOST_PORT/health", PORT_READY_TIMEOUT_MS)
|
||||
} catch (t: Throwable) {
|
||||
// If readiness probe fails, tear down so we don't leak the
|
||||
// stack into the next test run.
|
||||
@@ -300,5 +307,41 @@ class NostrNestsHarness private constructor(
|
||||
lastError,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Poll [healthUrl] until it returns 200. moq-auth opens its
|
||||
* listen socket before its handlers are ready; without this,
|
||||
* the first POST that arrives during that window gets a TCP
|
||||
* RST. Uses HttpURLConnection so we don't pull a heavy HTTP
|
||||
* client into the harness.
|
||||
*/
|
||||
private fun waitForHealth(
|
||||
healthUrl: String,
|
||||
timeoutMs: Long,
|
||||
) {
|
||||
val deadline = System.currentTimeMillis() + timeoutMs
|
||||
var lastError: Throwable? = null
|
||||
val url = java.net.URI(healthUrl).toURL()
|
||||
while (System.currentTimeMillis() < deadline) {
|
||||
try {
|
||||
val conn = url.openConnection() as java.net.HttpURLConnection
|
||||
conn.connectTimeout = 1_000
|
||||
conn.readTimeout = 1_000
|
||||
conn.requestMethod = "GET"
|
||||
try {
|
||||
if (conn.responseCode == 200) return
|
||||
} finally {
|
||||
conn.disconnect()
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
lastError = t
|
||||
}
|
||||
Thread.sleep(PORT_PROBE_INTERVAL_MS)
|
||||
}
|
||||
throw IllegalStateException(
|
||||
"$healthUrl did not return 200 within ${timeoutMs}ms",
|
||||
lastError,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user