Compat: a copy's own listeners aren't ours to bind

The first run against INBUXA's store failed all eight tests identically,
before reading a single record: the copy carries that server's listeners on
25, 443, 465, 587, 110, 143, 993 and 995, and nothing in a test run is
root, so each one failed with "Permission denied (os error 13)".

The builder now remembers the listeners it adds, and under NO_INSERT drops
build errors for any it didn't. Every other error still stands, including a
bind failing on one of its own, so this can't hide the case where the
harness's own port is taken.

The copy isn't edited for this: its listeners are simply not what a compat
run needs, and it reaches the server over the compat- ones instead. Checked
that a NO_INSERT run still boots and that scim_tests, which takes the
ordinary path, still passes.
This commit is contained in:
2026-09-19 20:58:49 -07:00
parent 1da75e986e
commit 01c5503a19
2 changed files with 45 additions and 11 deletions
+10
View File
@@ -99,6 +99,16 @@ copy of INBUXA's data any more. The script copies from the source for each
test and removes the copy afterwards, so the source stays clean; take it test and removes the copy afterwards, so the source stays clean; take it
from a stopped server or a snapshot, never from under a running one. from a stopped server or a snapshot, never from under a running one.
**Why the copy's own listeners are ignored.** A real server listens on 25,
443, 993 and the rest. Nothing in a test run is root, so every one of those
fails to bind and the run died before reading any data, with eight
`Permission denied (os error 13)` errors and nothing about what to do.
Under `NO_INSERT` the harness now drops build errors belonging to listeners
it did not add itself, and keeps every other error, including a bind that
fails on one of its own (`tests/src/utils/server.rs`). The copy is not
edited to achieve this; its listeners are simply not what a compat run
needs.
**Why `compat-` listeners appear in the copy.** The harness needs listeners **Why `compat-` listeners appear in the copy.** The harness needs listeners
on its own ports, and the registry keys listeners by name. A real server on its own ports, and the registry keys listeners by name. A real server
has its own, and a production listener called `jmap` or `imap` collided has its own, and a production listener called `jmap` or `imap` collided
+35 -11
View File
@@ -83,6 +83,9 @@ pub struct TestServer {
pub struct TestServerBuilder { pub struct TestServerBuilder {
bootstrap: Bootstrap, bootstrap: Bootstrap,
temp_dir: TempDir, temp_dir: TempDir,
// inbuxa: the listeners this builder added, so a compat run can tell
// them from the ones that came with a copy of someone else's store.
own_listeners: Vec<Id>,
http_listener_port: u16, http_listener_port: u16,
reset: bool, reset: bool,
logging_enabled: bool, logging_enabled: bool,
@@ -126,6 +129,7 @@ impl TestServerBuilder {
) )
.await, .await,
http_listener_port: 8899, http_listener_port: 8899,
own_listeners: Vec::new(),
temp_dir, temp_dir,
reset, reset,
logging_enabled: false, logging_enabled: false,
@@ -212,17 +216,19 @@ impl TestServerBuilder {
} else { } else {
name.to_string() name.to_string()
}; };
self.insert_object(NetworkListener { let id = self
bind: Map::new(vec![ .insert_object(NetworkListener {
SocketAddr::from_str(&format!("0.0.0.0:{port}")).unwrap(), bind: Map::new(vec![
]), SocketAddr::from_str(&format!("0.0.0.0:{port}")).unwrap(),
name, ]),
protocol, name,
use_tls: true, protocol,
tls_implicit, use_tls: true,
..Default::default() tls_implicit,
}) ..Default::default()
.await; })
.await;
self.own_listeners.push(id);
self self
} }
@@ -390,6 +396,24 @@ impl TestServerBuilder {
.parse_tcp_acceptors(&mut self.bootstrap, inner.clone()) .parse_tcp_acceptors(&mut self.bootstrap, inner.clone())
.await; .await;
// inbuxa: a compat run opens a copy of a real server's store, which
// carries that server's listeners: 25, 443, 993 and the rest. Nothing
// here runs as root, so every one of them fails to bind and the run
// dies before it reads any data. Those are the copy's, not ours, and
// the tests reach the server over the compat- listeners added above,
// so drop their errors and keep every other one — including a bind
// that fails on a listener this builder added.
if std::env::var("NO_INSERT").is_ok() {
let own = &self.own_listeners;
self.bootstrap.errors.retain(|error| match error {
registry::types::error::Error::Build { object_id, .. } => {
object_id.object() != ObjectType::NetworkListener
|| own.contains(&object_id.id())
}
_ => true,
});
}
// Start services // Start services
self.bootstrap.assert_no_errors(); self.bootstrap.assert_no_errors();
if !self.disable_services { if !self.disable_services {