test(audio-rooms): -DnestsInteropExternal bypasses Docker
Adds a "bring your own stack" path to NostrNestsHarness so the interop
tests can run without a Docker daemon. With `-DnestsInteropExternal=true`:
- Skip the `docker compose up` that builds + boots
moq-auth + moq-relay
- Port-probe + /health-check the same 8090 / 4443 endpoints the
Docker path uses
- close() is a no-op — the caller owns the lifecycle
Useful in two situations:
1. Sandboxes / restricted CI without a Docker daemon (just run
`cargo install moq-relay` + `node moq-auth/dist/index.js`
yourself, then run gradle with the flag)
2. Fast iteration — the Docker path takes ~30 s to compile
moq-relay on first run; with this, you keep both processes
alive across many test invocations
Forward `nestsInteropExternal` (and `nestsInteropDebug`,
`nestsInteropMoqRev`) through the Test task so the property reaches
test workers; without that, the gate stays off in the worker JVM.
Also: `assertSpeakerReached` / `assertListenerReached` now log a "✘"
checkpoint with the rich state description before calling fail().
JUnit captures the assertion message in a separate section, but the
"standard output" tab is what most people read first when scanning
for a cause — so the chained-cause string now lands in both places.
This commit is contained in:
@@ -95,4 +95,7 @@ kotlin {
|
||||
tasks.withType<Test>().configureEach {
|
||||
System.getProperty("nestsInterop")?.let { systemProperty("nestsInterop", it) }
|
||||
System.getProperty("nestsInteropRev")?.let { systemProperty("nestsInteropRev", it) }
|
||||
System.getProperty("nestsInteropMoqRev")?.let { systemProperty("nestsInteropMoqRev", it) }
|
||||
System.getProperty("nestsInteropExternal")?.let { systemProperty("nestsInteropExternal", it) }
|
||||
System.getProperty("nestsInteropDebug")?.let { systemProperty("nestsInteropDebug", it) }
|
||||
}
|
||||
|
||||
@@ -167,6 +167,10 @@ object InteropDebug {
|
||||
else -> error("unsupported expectedClass=$expectedClass")
|
||||
}
|
||||
if (!ok) {
|
||||
// Land the rich state dump on stdout too — JUnit captures the
|
||||
// fail message separately, but the standard-output section is
|
||||
// what people glance at first when debugging.
|
||||
checkpoint(scope, "✘ speaker NOT @ $expectedClass — actual=${describe(actual)}")
|
||||
fail("[$scope] speaker did not reach $expectedClass — was ${describe(actual)}")
|
||||
}
|
||||
checkpoint(scope, "speaker @ $expectedClass — ${describe(actual)}")
|
||||
@@ -183,6 +187,7 @@ object InteropDebug {
|
||||
else -> error("unsupported expectedClass=$expectedClass")
|
||||
}
|
||||
if (!ok) {
|
||||
checkpoint(scope, "✘ listener NOT @ $expectedClass — actual=${describe(actual)}")
|
||||
fail("[$scope] listener did not reach $expectedClass — was ${describe(actual)}")
|
||||
}
|
||||
checkpoint(scope, "listener @ $expectedClass — ${describe(actual)}")
|
||||
|
||||
+56
-2
@@ -45,7 +45,7 @@ import java.nio.file.Path
|
||||
* across as many cases as possible.
|
||||
*/
|
||||
class NostrNestsHarness private constructor(
|
||||
private val workDir: File,
|
||||
private val workDir: File?,
|
||||
val authBaseUrl: String,
|
||||
val moqEndpoint: String,
|
||||
) : AutoCloseable {
|
||||
@@ -54,16 +54,37 @@ class NostrNestsHarness private constructor(
|
||||
override fun close() {
|
||||
if (stopped) return
|
||||
stopped = true
|
||||
runDocker(workDir, "down", "-v", "--remove-orphans")
|
||||
// External mode: caller owns the lifecycle of moq-auth +
|
||||
// moq-relay. Nothing for us to tear down.
|
||||
val dir = workDir ?: return
|
||||
runDocker(dir, "down", "-v", "--remove-orphans")
|
||||
}
|
||||
|
||||
companion object {
|
||||
/** System property gate. Tests should call [assumeNestsInterop] first. */
|
||||
const val ENABLE_PROPERTY = "nestsInterop"
|
||||
|
||||
/**
|
||||
* Bypass-Docker mode. When `-DnestsInteropExternal=true`, the
|
||||
* harness doesn't try to bring up containers — it assumes the
|
||||
* caller has already started `moq-auth` on [AUTH_HOST_PORT] and
|
||||
* `moq-relay` on [MOQ_HOST_PORT] (e.g. via
|
||||
* `nests/dev-config/start-dev.sh`). Useful on machines without a
|
||||
* Docker daemon (sandboxes, restricted CI), and for fast
|
||||
* iteration since each test no longer pays the
|
||||
* `docker compose up` / `docker compose down` cost.
|
||||
*
|
||||
* The harness still port-probes + health-checks both endpoints
|
||||
* before declaring "ready", so a half-started external stack
|
||||
* fails the same way a half-started Docker stack would.
|
||||
*/
|
||||
const val EXTERNAL_PROPERTY = "nestsInteropExternal"
|
||||
|
||||
/** Tests that depend on the harness call this in `@BeforeTest`. */
|
||||
fun isEnabled(): Boolean = System.getProperty(ENABLE_PROPERTY) == "true"
|
||||
|
||||
private fun isExternal(): Boolean = System.getProperty(EXTERNAL_PROPERTY) == "true"
|
||||
|
||||
/**
|
||||
* Pin the nostrnests revision to a known-good SHA. Bump deliberately
|
||||
* — drift on `main` should not silently change interop expectations.
|
||||
@@ -138,6 +159,8 @@ class NostrNestsHarness private constructor(
|
||||
"Call NostrNestsHarness.assumeNestsInterop() first to skip cleanly."
|
||||
}
|
||||
|
||||
if (isExternal()) return startExternal()
|
||||
|
||||
val workDir = ensureRepo()
|
||||
// The compose file `build:`s `./moq` — the upstream moq-rs
|
||||
// sources — but nostrnests does NOT ship that directory; it
|
||||
@@ -186,6 +209,37 @@ class NostrNestsHarness private constructor(
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* "External" path: caller has already started moq-auth +
|
||||
* moq-relay (e.g. via `nests/dev-config/start-dev.sh` or a
|
||||
* `cargo install moq-relay` build kicked off by hand). We just
|
||||
* port-probe + health-check, then return a harness whose
|
||||
* [close] is a no-op.
|
||||
*
|
||||
* Skip Docker entirely — useful on machines without a Docker
|
||||
* daemon, and dramatically faster on developer iteration.
|
||||
*/
|
||||
private fun startExternal(): NostrNestsHarness {
|
||||
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)
|
||||
waitForHealth("http://127.0.0.1:$AUTH_HOST_PORT/health", PORT_READY_TIMEOUT_MS)
|
||||
} catch (t: Throwable) {
|
||||
throw IllegalStateException(
|
||||
"external moq-auth / moq-relay not reachable on 127.0.0.1:" +
|
||||
"$AUTH_HOST_PORT and 127.0.0.1:$MOQ_HOST_PORT — " +
|
||||
"did you start the stack? See nests/dev-config/start-dev.sh.\n" +
|
||||
"Original error: ${t.message}",
|
||||
t,
|
||||
)
|
||||
}
|
||||
return NostrNestsHarness(
|
||||
workDir = null,
|
||||
authBaseUrl = "http://127.0.0.1:$AUTH_HOST_PORT",
|
||||
moqEndpoint = "https://127.0.0.1:$MOQ_HOST_PORT/",
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience for `@BeforeTest`: throws JUnit's `AssumptionViolated`
|
||||
* (via `org.junit.Assume`) when the property isn't set, which JUnit
|
||||
|
||||
Reference in New Issue
Block a user