test(bf-2aqf): hard-assert >=1 blob in TestFullE2EIntegration
Replace the minimum-count dodge in TestFullE2EIntegration with two hard assertions: AssertBlobObserved on the WatchDashboardWS blobCounts (captured concurrently during the run via a goroutine alongside AssertDuringRun) and AssertDetectionEventsObserved on the /api/events "detection" result. The "we verify the API responds correctly rather than asserting a minimum count" dodge comment is gone. 4-node/2-walker/30s config and AssertDuringRun kept. Expected RED until upstream bf-4q5w lands — deliberately not weakened. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
a8d0759c21
commit
3b00ccfbd4
1 changed files with 61 additions and 11 deletions
|
|
@ -903,7 +903,20 @@ func TestDashboardWebSocket(t *testing.T) {
|
|||
t.Logf("Received %d blob count updates", len(blobCounts))
|
||||
}
|
||||
|
||||
// TestFullE2EIntegration runs a comprehensive end-to-end test
|
||||
// TestFullE2EIntegration runs a comprehensive end-to-end test.
|
||||
//
|
||||
// It is the bf-5jeo verification capstone's full-stack check: the 4-node /
|
||||
// 2-walker / 30s simulator run must produce observable output on BOTH the
|
||||
// dashboard WebSocket feed (a tracked blob appeared) and the /api/events
|
||||
// "detection" stream (a detection event was persisted). Asserting neither — the
|
||||
// previous behavior — let detection regressions pass silently as long as the API
|
||||
// was merely reachable. The two hard assertions below close that dodge.
|
||||
//
|
||||
// NOTE: these assertions are deliberately strict and are expected to be RED until
|
||||
// the upstream fusion SetNodePosition wiring (bf-4q5w) lands, so that an empty
|
||||
// blob feed / detection-event list surfaces as a hard failure rather than silently
|
||||
// passing. That strictness is the entire point of the bf-5jeo capstone. Do NOT
|
||||
// weaken it (e.g. by re-accepting an empty feed or list) to make the test green.
|
||||
func TestFullE2EIntegration(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping e2e test in short mode")
|
||||
|
|
@ -925,28 +938,65 @@ func TestFullE2EIntegration(t *testing.T) {
|
|||
t.Fatalf("Failed to run simulator: %v", err)
|
||||
}
|
||||
|
||||
// Assert during run
|
||||
// Capture the dashboard WebSocket blob feed concurrently while the
|
||||
// simulation runs and AssertDuringRun polls the live state. WatchDashboardWS
|
||||
// blocks for `simDuration`, so it must run alongside AssertDuringRun rather
|
||||
// than after it (the simulator has finished by then).
|
||||
var blobCounts []int
|
||||
var wsErr error
|
||||
var wsWG sync.WaitGroup
|
||||
wsWG.Add(1)
|
||||
go func() {
|
||||
defer wsWG.Done()
|
||||
blobCounts, wsErr = h.WatchDashboardWS(ctx, simDuration)
|
||||
}()
|
||||
|
||||
// Assert during run (kept as-is; AssertDuringRun itself is hardened by bf-2330).
|
||||
if err := h.AssertDuringRun(ctx, simDuration, 4); err != nil {
|
||||
t.Fatalf("Assertion failed during run: %v", err)
|
||||
}
|
||||
|
||||
// Wait for simulator to complete
|
||||
// Collect the dashboard WS capture.
|
||||
wsWG.Wait()
|
||||
if wsErr != nil {
|
||||
t.Fatalf("Failed to watch dashboard WS: %v", wsErr)
|
||||
}
|
||||
|
||||
// Wait for simulator to complete (grace period for any in-flight detection
|
||||
// events to be persisted to the events table).
|
||||
time.Sleep(simDuration + 2*time.Second)
|
||||
|
||||
// Assert after run: verify the events API is functional.
|
||||
// Detection events are only generated when the fusion engine produces blobs
|
||||
// (requiring sufficient signal variation). We verify the API responds correctly
|
||||
// rather than asserting a minimum count.
|
||||
// Hard assertion #1: the dashboard WebSocket feed must have shown at least one
|
||||
// tracked blob during the run. A zero blob count means the fusion+tracking
|
||||
// loop localized no walker from the 4-node/2-walker CSI stream — a detection
|
||||
// regression, not a tolerated quiet-room condition. The helper (bf-2330) gates
|
||||
// nil/empty/zero; we wrap its error with the peak observed and likely upstream
|
||||
// cause so a RED failure is immediately actionable.
|
||||
if assertErr := AssertBlobObserved(blobCounts); assertErr != nil {
|
||||
t.Fatalf("expected >=1 blob in the dashboard WS feed during the run, "+
|
||||
"but none was observed (the fusion/tracking pipeline localized no "+
|
||||
"walker from the 4-node/2-walker CSI stream) [%v]", assertErr)
|
||||
}
|
||||
|
||||
// Hard assertion #2: the /api/events "detection" stream must surface at least
|
||||
// one detection event. Same rationale as above — an empty list is a detection
|
||||
// regression, not a tolerated condition.
|
||||
events, err := h.GetEvents(ctx, "detection", 100)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get events: %v", err)
|
||||
}
|
||||
|
||||
if events == nil {
|
||||
t.Fatal("Expected non-nil events response from API")
|
||||
if assertErr := AssertDetectionEventsObserved(events); assertErr != nil {
|
||||
observed := 0
|
||||
if events != nil {
|
||||
observed = len(events.Events)
|
||||
}
|
||||
t.Fatalf("expected >=1 detection event but observed %d; "+
|
||||
"the fusion/tracking pipeline produced no blobs (no walker was "+
|
||||
"localized from the 4-node/2-walker CSI stream) [%v]",
|
||||
observed, assertErr)
|
||||
}
|
||||
|
||||
t.Logf("✓ Full E2E integration test passed (events API functional, %d detection events)", len(events.Events))
|
||||
t.Logf("✓ Full E2E integration test passed (dashboard WS showed >=1 blob; %d detection events)", len(events.Events))
|
||||
}
|
||||
|
||||
// IO_1_FreshInstall_FirstBoot tests the fresh install / first boot scenario.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue