test(adr-115): diagnostic dump + wider subscription on state-test failure

After 4 surgical fixes the state_messages_published_on_snapshot_broadcast
test still reports 'expected ON state, got []' on CI — and we can't
tell whether the publisher is publishing nothing, or publishing the
wrong topic, or publishing to a session the subscriber lost.

Two changes to surface what's actually happening:

1. Widen subscription from `homeassistant/binary_sensor/+/presence/state`
   to `homeassistant/#`. Now the captured-message dump shows every
   topic the publisher emitted under the homeassistant prefix —
   discovery configs, availability heartbeats, state messages,
   anything else. A narrow filter was hiding which side of the
   pipeline was broken.

2. Add stderr `[diag]` lines that dump every captured (retain, topic,
   payload-prefix) on test failure. CI runs `--nocapture` so the lines
   land in the workflow log. From the next failed-CI log we'll know
   whether:
     - publisher isn't emitting state at all (no /state topics in dump)
     - publisher is emitting to a different topic shape (typo in
        topic format string)
     - subscriber connected to a stale session and missed messages
        (would see discovery + no state but dump would have count > 0)
     - subscriber is connecting after publisher disconnected (count = 0
        even after widening)

This is a debugging commit, not a production fix — once we know the
exact failure mode from the next CI log we can ship a real fix.

Refs PR #778, issue #776.

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
ruv 2026-05-23 15:58:33 -04:00
parent 967cede74d
commit 636ca7b52f
1 changed files with 23 additions and 7 deletions

View File

@ -261,11 +261,13 @@ async fn privacy_mode_suppresses_biometric_discovery() {
async fn state_messages_published_on_snapshot_broadcast() {
let Some(port) = should_run() else { return; };
let (sub, mut sub_loop) = subscribe_client(
port,
&["homeassistant/binary_sensor/+/presence/state"],
)
.await;
// Subscribe to the entire homeassistant tree so the diagnostic
// capture shows EVERYTHING the publisher is doing, not just
// the narrow presence/state filter — narrow filters can hide
// ordering issues (e.g., if the publisher is publishing only
// discovery and not state, a narrow filter on state can't tell
// us that).
let (sub, mut sub_loop) = subscribe_client(port, &["homeassistant/#"]).await;
let cfg = make_cfg(port, false, "state");
let builder = make_builder("inttest3");
@ -306,6 +308,19 @@ async fn state_messages_published_on_snapshot_broadcast() {
let msgs = collect_published(&mut sub_loop, Duration::from_secs(8)).await;
let _ = sub.disconnect().await;
// Diagnostic: dump every captured topic so we can see what (if
// anything) the subscriber received. CI runs with --nocapture, so
// this lands in the workflow log when the test fails.
eprintln!("[diag] subscriber captured {} messages:", msgs.len());
for (t, p, retain) in &msgs {
eprintln!(
"[diag] retain={} topic={} payload={}",
retain,
t,
String::from_utf8_lossy(p).chars().take(80).collect::<String>(),
);
}
let presence_states: Vec<String> = msgs
.iter()
.filter(|(t, _, _)| t.contains("/inttest3/presence/state"))
@ -314,8 +329,9 @@ async fn state_messages_published_on_snapshot_broadcast() {
assert!(
presence_states.iter().any(|p| p == "ON"),
"expected ON state, got {:?}",
presence_states
"expected ON state, got {:?} (of {} total captured)",
presence_states,
msgs.len(),
);
assert!(
presence_states.iter().any(|p| p == "OFF"),