test(e2e): give a soulfind that dies on startup a fresh port

free_port() hands out a port that is free when asked, not when soulfind
binds it, and soulfind exits when the port has gone. The library
harness also gave soulfind only five seconds and did not notice a child
that had died, so either read as "no soulfind server" and failed the
test. It now waits ten seconds like the CLI harness, never takes a
child that has exited as up, and retries on a fresh port; the CLI
harness gets the same retry.

A listener that takes the port first can still answer before soulfind
has got as far as binding, so the two library tests that bind listeners
outside the server gate now take it too: while a soulfind starts, no
other test in that binary binds anything.
This commit is contained in:
Michel de Graaf
2026-09-10 15:17:54 +02:00
parent 7ffb85a6ad
commit 2a343dd4b9
2 changed files with 59 additions and 32 deletions
+11 -7
View File
@@ -60,12 +60,16 @@ pub struct Soulfind {
}
impl Soulfind {
/// soulfind exits if its port is taken between `free_port()` handing it
/// out and soulfind binding it, so a start that fails gets a fresh port.
pub fn start() -> Option<Self> {
let port = free_port()?;
let db = std::env::temp_dir().join(format!("soulfind-{port}.db"));
let _ = std::fs::remove_file(&db);
let child = Self::spawn(port, &db)?;
Some(Self { child, db, port })
(0..3).find_map(|_| {
let port = free_port()?;
let db = std::env::temp_dir().join(format!("soulfind-{port}.db"));
let _ = std::fs::remove_file(&db);
let child = Self::spawn(port, &db)?;
Some(Self { child, db, port })
})
}
fn spawn(port: u16, db: &Path) -> Option<Child> {
@@ -78,8 +82,8 @@ impl Soulfind {
let deadline = Instant::now() + Duration::from_secs(10);
while Instant::now() < deadline {
// Whoever answers on the port has to be this child: one that
// lost the port and died would otherwise pass as up.
// A child that has exited is never taken as up: soulfind exits
// when its port is gone, and whoever took it may answer instead.
if child.try_wait().ok().flatten().is_some() {
return None;
}