test(mail): stateful IMAP mock harness + fix a flaky webhook capture test #384

Merged
KofTwentyTwo merged 3 commits from story/imap-mock-harness into dev 2026-08-14 19:09:51 +00:00
3 Commits
Author SHA1 Message Date
james.maes a747b3a32e Merge remote-tracking branch 'origin/dev' into story/imap-mock-harness
# Conflicts:
#	crates/nuncio-filter/src/test_tracing.rs
#	crates/nuncio-filter/src/webhook.rs
2026-08-14 13:42:20 -05:00
james.maes 83636efcdb fix(filter): stop the webhook log-capture tests failing at random
The two webhook telemetry tests installed a hand-rolled tracing::Subscriber
with set_default and asserted on what it recorded. Roughly three runs in ten
they captured nothing at all and failed.

Callsite Interest is cached process-globally. A raw Subscriber installed
thread-locally does not participate in the way tracing_subscriber's registry
handles that cache, so whichever sibling test reached the dispatch callsites
first decided whether these two could ever see them -- an order dependency,
and therefore a coin flip under a parallel test harness.

Two narrower fixes were tried and measured, and both made it worse:
rebuild_interest_cache() after set_default failed five runs in six, because
the rebuild is itself global and concurrent capture tests clobber each other;
returning Interest::sometimes() from register_callsite failed nine in ten,
because callsite registration consults the global dispatcher and never asks a
thread-local subscriber at all.

Adopt the registry-backed capture helper nuncio-cal and nuncio-contacts
already use, which does not exhibit the problem. The two tests become plain
#[test] driving a current-thread runtime built inside the capture scope, since
a #[tokio::test] creates its runtime outside it. Assertions now match on level
and named fields rather than substrings of a formatted line, so "the host was
logged" no longer passes because the host happened to appear in some other
field.

Measured: 0 failures in 12 stress runs, against 3 in 10 before.
2026-08-10 14:14:41 -05:00
james.maes ac017ba3c4 test(mail): add a stateful multi-connection IMAP server for offline tests
The IMAP client had no mock. wiremock is HTTP-only, so JMAP and the DAV
protocols have one and IMAP does not; what stood in for it was a scripted
responder rewritten inline in each test, holding one connection and no
mailbox state.

That shape cannot express the property the sync work needs to assert: two
clients observing each other's writes through shared server state. It also
cannot reproduce the response forms that decide whether a mutation actually
applied -- COPYUID, VANISHED, and a tagged [MODIFIED ...] -- so a client that
reports success for a no-op cannot currently be caught by a test.

The server binds a real loopback listener, shares mailbox state across every
accepted connection, and speaks IMAP4rev1 plus the parts of RFC 7162, RFC 4315,
and RFC 6851 the engine exercises. Capability advertisement is selectable,
because the sync ladder branches on it and a suite that only ever runs against
a QRESYNC server proves nothing about the path Gmail and Exchange take.

Behind a `test-support` feature so it stays out of production builds while
remaining available to dependent crates' integration suites.

Covers: shared state across connections; a move of an already-moved UID
returning OK with no COPYUID; UNCHANGEDSINCE reporting MODIFIED in the tagged
response; QRESYNC SELECT reporting VANISHED; QRESYNC refused without ENABLE and
on a CONDSTORE-only server; VANISHED versus EXPUNGE on delete.
2026-08-10 13:57:12 -05:00