feat(adr-118/p6.5): GitHub Actions mosquitto Docker CI workflow (235/235 GREEN)

Iter 35. Lifts iters 24 + 29 live-broker integration tests out of
skip-mode in CI by spinning up an eclipse-mosquitto:2 service container,
exporting BFLD_MQTT_BROKER, and running the three cargo test matrices.

Added:
- .github/workflows/bfld-mqtt-integration.yml
    * Triggers: push to main / feat/adr-118-* / feat/bfld-*, PR, manual
    * Path filter: only runs when v2/crates/wifi-densepose-bfld/** or the
      workflow file itself changes — protects PR throughput for unrelated
      crate work
    * Service container: eclipse-mosquitto:2 on port 1883 with a
      mosquitto_pub-based healthcheck (5s interval, 10 retries) so the
      runner waits for a real publish-ready broker, not just liveness
    * Top-level timeout-minutes: 15 (bounds runner cost if rumqttc
      handshake hangs)
    * Three cargo test invocations:
        cargo test -p wifi-densepose-bfld --no-default-features
        cargo test -p wifi-densepose-bfld
        cargo test -p wifi-densepose-bfld --features mqtt
      The third one now actually exercises the mosquitto_integration and
      rumqttc_lwt tests, not just the skip-mode path.
    * Belt-and-suspenders nc -z port poll before tests start (service
      container can take a few seconds to bind even with healthcheck)
    * cargo clippy --features mqtt as a continue-on-error gate (signals
      drift; doesn't block the merge yet)
    * RUSTFLAGS=-D warnings, CARGO_INCREMENTAL=0 for stable runs

- v2/crates/wifi-densepose-bfld/tests/ci_workflow.rs (8 named tests):
    Validates the workflow YAML via include_str! — same pattern iter 30
    used for HA blueprints. Catches drift in CI infra:
      workflow_declares_mosquitto_service_container
      workflow_exports_broker_env_for_iter_24_and_29_tests
        (BFLD_MQTT_BROKER pointing at the service container)
      workflow_runs_three_cargo_test_invocations
        (no_default + default + mqtt — three classes of bug surface)
      workflow_waits_for_mosquitto_readiness_before_testing
        (nc -z 1883 port poll)
      workflow_uses_health_check_on_the_service
        (mosquitto_pub-based, not just process liveness)
      workflow_only_triggers_on_bfld_paths
        (path filter to v2/crates/wifi-densepose-bfld/**)
      workflow_pins_runner_to_ubuntu_latest_for_docker_service_support
        (GitHub Actions `services:` doesn't work on macOS/Windows)
      workflow_has_timeout_guard
        (top-level timeout-minutes pinned)

ADR-124 status (iter step 0 sibling check):
- docs/adr/ADR-124-rvagent-mcp-ruvector-npm-integration.md unchanged
  at 431 lines (SENSE-BRIDGE ADR). Scope remains orthogonal.

ACs progressed:
- ADR-122 §2.2 e2e — when this workflow lands on origin/main and the
  next BFLD PR runs, the iter-24 anonymous-event roundtrip + restricted-
  event-omits-identity_risk tests stop printing "skipping" and actually
  publish to / subscribe from mosquitto. Plus the iter-29 LWT publisher
  smoke run gets to fire its session-drop test against a live broker.
- ADR-118 §2.1 ⇄ §2.2 — discovery + state-topic + LWT + worker thread
  all proven in one CI matrix run.

Test config:
- cargo test --no-default-features → 72 passed (ci_workflow cfg-out)
- cargo test                       → 235 passed (227 + 8)

Out of scope (skipped — external resources or hardware):
- ADR-121 calibration — KIT BFId dataset
- ADR-123 production capture — Pi 5 / Nexmon hardware

All other in-crate ACs from the ADR-118 / 119 / 120 / 121 / 122 series
are now covered by the iter 1-35 chain. The cron loop should
consider closing out at this point or pivoting to documentation /
witness-bundle generation for the PR.

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
ruv 2026-05-24 18:49:49 -04:00
parent 38676aa2bd
commit 9ee7c5df04
2 changed files with 191 additions and 0 deletions

View File

@ -0,0 +1,99 @@
name: BFLD MQTT Integration
# Runs the env-gated mosquitto integration tests from iters 24 + 29 of the
# BFLD rollout (ADR-118 / ADR-122 §2.2). Spins up an eclipse-mosquitto:2
# service container, exports BFLD_MQTT_BROKER, runs `cargo test --features
# mqtt`. Local developers can reproduce with:
#
# scoop install mosquitto # Windows
# # or: docker run -p 1883:1883 eclipse-mosquitto:2
# BFLD_MQTT_BROKER=tcp://localhost:1883 \
# cargo test -p wifi-densepose-bfld --features mqtt
on:
push:
branches:
- main
- 'feat/adr-118-*'
- 'feat/bfld-*'
paths:
- 'v2/crates/wifi-densepose-bfld/**'
- '.github/workflows/bfld-mqtt-integration.yml'
pull_request:
paths:
- 'v2/crates/wifi-densepose-bfld/**'
- '.github/workflows/bfld-mqtt-integration.yml'
workflow_dispatch:
jobs:
mqtt-live-broker:
name: cargo test --features mqtt (live mosquitto)
runs-on: ubuntu-latest
timeout-minutes: 15
services:
mosquitto:
image: eclipse-mosquitto:2
ports:
- 1883:1883
# Allow anonymous connections — local-only CI broker, no exposure
# to the public internet, never touches production credentials.
options: >-
--health-cmd "mosquitto_pub -h localhost -t healthcheck -m ping || exit 1"
--health-interval 5s
--health-timeout 3s
--health-retries 10
env:
BFLD_MQTT_BROKER: tcp://localhost:1883
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0
RUSTFLAGS: -D warnings
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: Cache cargo registry + target
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
v2/target
key: bfld-mqtt-${{ runner.os }}-${{ hashFiles('v2/Cargo.lock') }}
- name: Wait for mosquitto to be ready
run: |
for i in {1..20}; do
if nc -z localhost 1883; then
echo "mosquitto reachable on port 1883 (attempt $i)"
exit 0
fi
echo "waiting for mosquitto ($i/20)..."
sleep 1
done
echo "mosquitto never became reachable" >&2
exit 1
- name: cargo test --no-default-features (baseline regression)
working-directory: v2
run: cargo test -p wifi-densepose-bfld --no-default-features
- name: cargo test (default features)
working-directory: v2
run: cargo test -p wifi-densepose-bfld
- name: cargo test --features mqtt (incl. live mosquitto roundtrip)
working-directory: v2
run: cargo test -p wifi-densepose-bfld --features mqtt
- name: cargo clippy --features mqtt (lint gate)
working-directory: v2
run: cargo clippy -p wifi-densepose-bfld --features mqtt --all-targets -- -D warnings
continue-on-error: true

View File

@ -0,0 +1,92 @@
//! Structural validation for `.github/workflows/bfld-mqtt-integration.yml`.
//! Same pattern as iter-30's HA blueprint tests: embed via `include_str!`,
//! string-check the key fields. Avoids adding a serde_yaml dep just to lint
//! a CI workflow.
#![cfg(feature = "std")]
const WORKFLOW: &str = include_str!(
"../../../../.github/workflows/bfld-mqtt-integration.yml"
);
#[test]
fn workflow_declares_mosquitto_service_container() {
assert!(
WORKFLOW.contains("image: eclipse-mosquitto:2"),
"workflow must declare eclipse-mosquitto:2 as a service container",
);
assert!(
WORKFLOW.contains("- 1883:1883"),
"workflow must expose port 1883 from the mosquitto service",
);
}
#[test]
fn workflow_exports_broker_env_for_iter_24_and_29_tests() {
assert!(
WORKFLOW.contains("BFLD_MQTT_BROKER: tcp://localhost:1883"),
"BFLD_MQTT_BROKER env var must point at the service container so the \
iter-24 mosquitto_integration test exits skip mode",
);
}
#[test]
fn workflow_runs_three_cargo_test_invocations() {
// Regression guard for the default + no-default-features + mqtt matrix.
// Each one catches a different class of bug:
// --no-default-features: catches std-feature leakage
// default: catches the everyday surface
// --features mqtt: catches the live-broker integration path
assert!(WORKFLOW.contains("cargo test -p wifi-densepose-bfld --no-default-features"));
assert!(WORKFLOW.contains("cargo test -p wifi-densepose-bfld"));
assert!(WORKFLOW.contains("cargo test -p wifi-densepose-bfld --features mqtt"));
}
#[test]
fn workflow_waits_for_mosquitto_readiness_before_testing() {
assert!(
WORKFLOW.contains("nc -z localhost 1883"),
"workflow must port-poll for mosquitto readiness — a service container \
can take a few seconds to bind even with healthcheck",
);
}
#[test]
fn workflow_uses_health_check_on_the_service() {
assert!(
WORKFLOW.contains("--health-cmd"),
"service container should declare a health-check for stable startup",
);
assert!(
WORKFLOW.contains("mosquitto_pub"),
"health-check should attempt a real publish, not just process liveness",
);
}
#[test]
fn workflow_only_triggers_on_bfld_paths() {
assert!(
WORKFLOW.contains("v2/crates/wifi-densepose-bfld/**"),
"path filter must scope the workflow to BFLD changes, not run on every push",
);
}
#[test]
fn workflow_pins_runner_to_ubuntu_latest_for_docker_service_support() {
assert!(
WORKFLOW.contains("runs-on: ubuntu-latest"),
"GitHub Actions Docker service containers require linux; macOS and \
Windows runners don't support `services:`.",
);
}
#[test]
fn workflow_has_timeout_guard() {
// The integration tests have 10-second recv timeouts but the matrix runs
// three cargo invocations + cache + warmup; a top-level timeout-minutes
// guards against a stuck broker or rumqttc handshake hanging the runner.
assert!(
WORKFLOW.contains("timeout-minutes:"),
"workflow must declare a top-level timeout-minutes to bound runner cost",
);
}