Skip to content

Commit dcc05e4

Browse files
fix(live): Passed/Slow/Failed buckets reconcile with Tested (3-way partition)
The Current Batch panel showed Passed/Failed that didn't sum to Tested (e.g. 7 tested → 5 passed / 0 failed) because "passed"/"failed" were computed from three inconsistent definitions that overwrote each other: cold-refresh & per-result used SLA (≥10 Mbps); batch:end used the server's passed=testedNodes(total)/failed=hardErrors; cbRender derived passed=SLA, failed=errored — leaving connected-but-slow (<10 Mbps) nodes counted in neither bucket. Now cbRender is the single source of truth and partitions the tested (non-skipped) rows into three mutually-exclusive buckets that always reconcile: Failed (error / no reading), Passed SLA (≥10 Mbps), Slow (connected <10 Mbps). Added the Slow tile + relabeled Passed → "Passed SLA" so the threshold meaning is explicit. The dead accumulator paths (cbApplyNodeResult / cbApplyBatchEnd / cold-refresh / replay) no longer compute their own divergent counts — they defer to cbRender.
1 parent 816ddb2 commit dcc05e4

1 file changed

Lines changed: 46 additions & 30 deletions

File tree

live.html

Lines changed: 46 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@
279279
}
280280
.cb-snap-item { color: var(--text-dim); }
281281
.cb-snap-pass { color: var(--green); font-weight: 700; }
282+
.cb-snap-slow { color: var(--yellow); font-weight: 700; }
282283
.cb-snap-fail { color: var(--red); font-weight: 700; }
283284
.cb-complete-msg {
284285
font-family: var(--font-display);
@@ -768,7 +769,8 @@ <h1 class="live-page-title">
768769
</div>
769770
<div class="cb-snapshot-row">
770771
<span class="cb-snap-item">Batch size: <strong id="cbSnapshot"></strong></span>
771-
<span class="cb-snap-item">Passed: <strong class="cb-snap-pass" id="cbPassed">0</strong></span>
772+
<span class="cb-snap-item">Passed SLA: <strong class="cb-snap-pass" id="cbPassed">0</strong></span>
773+
<span class="cb-snap-item">Slow: <strong class="cb-snap-slow" id="cbSlow">0</strong></span>
772774
<span class="cb-snap-item">Failed: <strong class="cb-snap-fail" id="cbFailed">0</strong></span>
773775
</div>
774776
<div class="cb-complete-msg" id="cbCompleteMsg"></div>
@@ -1040,11 +1042,11 @@ <h1 class="live-page-title">
10401042
testedAt: r2.testedAt || null,
10411043
});
10421044
}
1043-
// Sync the current-batch counter to the seeded rows so the
1044-
// Tested tile shows real progress on a cold refresh.
1045-
_cb.tested = resultsArr.length;
1046-
_cb.passed = resultsArr.filter(x => Number(x.actualMbps) >= 10).length;
1047-
_cb.failed = _cb.tested - _cb.passed;
1045+
// Sync the current-batch counter to the seeded rows so the Tested
1046+
// tile shows real progress on a cold refresh, then let cbRender derive
1047+
// the Passed SLA / Slow / Failed partition from the rows.
1048+
_cb.tested = resultsArr.length;
1049+
cbRender();
10481050
}
10491051
applyHeaderStatsFromState();
10501052
renderLiveStats();
@@ -1504,6 +1506,7 @@ <h1 class="live-page-title">
15041506
iteration: null,
15051507
tested: 0,
15061508
passed: 0,
1509+
slow: 0,
15071510
failed: 0,
15081511
gapEndsAt: null,
15091512
gapTimer: null,
@@ -1518,7 +1521,7 @@ <h1 class="live-page-title">
15181521
const startRaw = data.startedAt || data.started_at;
15191522
_cb.startedAt = startRaw ? new Date(startRaw).getTime() : Date.now();
15201523
_cb.iteration = data.iteration ?? null;
1521-
_cb.tested = 0; _cb.passed = 0; _cb.failed = 0;
1524+
_cb.tested = 0; _cb.passed = 0; _cb.slow = 0; _cb.failed = 0;
15221525
_cb.gapEndsAt = null;
15231526
if (_cb.gapTimer) { clearInterval(_cb.gapTimer); _cb.gapTimer = null; }
15241527
const dot = _cbEl('cbDot');
@@ -1528,21 +1531,19 @@ <h1 class="live-page-title">
15281531
cbRender();
15291532
}
15301533

1531-
function cbApplyNodeResult(data) {
1532-
_cb.tested++;
1533-
const passed = data.passed != null
1534-
? !!data.passed
1535-
: (data.actual_mbps != null && Number(data.actual_mbps) >= 10) || (data.actualMbps != null && Number(data.actualMbps) >= 10);
1536-
if (passed) _cb.passed++;
1537-
else _cb.failed++;
1534+
function cbApplyNodeResult() {
1535+
// The row is already upserted into resultsArr by the caller; cbRender is the
1536+
// single source of truth — it recomputes Tested + the Passed SLA / Slow /
1537+
// Failed partition from resultsArr. Don't accumulate counts here (that path
1538+
// used a 2-bucket SLA-only definition that disagreed with cbRender).
15381539
cbRender();
15391540
}
15401541

1541-
function cbApplyBatchEnd(data) {
1542-
data = data || {};
1543-
if (data.passed != null) _cb.passed = data.passed;
1544-
if (data.failed != null) _cb.failed = data.failed;
1545-
_cb.tested = _cb.passed + _cb.failed;
1542+
function cbApplyBatchEnd() {
1543+
// Don't trust the server's batch:end passed/failed — they use a different
1544+
// definition (passed = total testedNodes, failed = hard errors only) that
1545+
// conflicts with cbRender's resultsArr-derived partition. Just stop the
1546+
// pulse and let cbRender recompute from the authoritative rows.
15461547
const dot = _cbEl('cbDot');
15471548
if (dot) dot.className = 'pulse-dot';
15481549
cbRender();
@@ -1627,11 +1628,27 @@ <h1 class="live-page-title">
16271628
}
16281629
}
16291630

1630-
// Pass/Fail derived from resultsArr (matches admin's recompute logic).
1631-
const passed = arr.filter(r => Number(r.actualMbps) >= 10).length;
1632-
const failed = arr.filter(r => !r.skipped && r.errorCode !== 'TEST_RUN_SKIP' && (r.error || r.errorCode || r.actualMbps == null)).length;
1631+
// Outcome buckets — mutually exclusive, derived from resultsArr (the single
1632+
// source of truth). They PARTITION the tested (non-skipped) nodes so that
1633+
// Passed SLA + Slow + Failed always reconciles. Priority: a hard error or no
1634+
// speed reading is Failed; otherwise ≥10 Mbps is Passed SLA; a connected
1635+
// sub-10 reading is Slow. Skipped / TEST RUN rows aren't outcomes — they're
1636+
// surfaced in the stat tiles, not here. (This replaced a 2-bucket scheme
1637+
// where slow-but-connected nodes fell through both filters, e.g. 7 tested
1638+
// showing 5 passed / 0 failed with the 2 slow nodes uncounted.)
1639+
let passed = 0, slow = 0, failed = 0;
1640+
for (const r of arr) {
1641+
if (r.skipped || r.errorCode === 'TEST_RUN_SKIP') continue;
1642+
const m = r.actualMbps != null ? Number(r.actualMbps) : null;
1643+
if (m == null || r.error || r.errorCode) failed++;
1644+
else if (m >= 10) passed++;
1645+
else slow++;
1646+
}
1647+
_cb.passed = passed; _cb.slow = slow; _cb.failed = failed;
16331648
const passEl = _cbEl('cbPassed');
16341649
if (passEl) passEl.textContent = passed;
1650+
const slowEl = _cbEl('cbSlow');
1651+
if (slowEl) slowEl.textContent = slow;
16351652
const failEl = _cbEl('cbFailed');
16361653
if (failEl) failEl.textContent = failed;
16371654

@@ -2158,11 +2175,11 @@ <h1 class="live-page-title">
21582175
testRun: err === 'TEST_RUN_SKIP',
21592176
testedAt: tested,
21602177
});
2161-
cbApplyNodeResult({ passed: resultMbps != null && resultMbps >= 10 });
2178+
cbApplyNodeResult();
21622179
break;
21632180
}
21642181
case 'batch:end': {
2165-
cbApplyBatchEnd(d);
2182+
cbApplyBatchEnd();
21662183
const next = d.next_in_ms ? Math.round(d.next_in_ms / 1000) : null;
21672184
const passed = d.passed != null ? d.passed : _cb.passed;
21682185
const failed = d.failed != null ? d.failed : _cb.failed;
@@ -2230,7 +2247,7 @@ <h1 class="live-page-title">
22302247
resultsArr.length = 0;
22312248
const tbody = document.getElementById('resultsBody');
22322249
if (tbody) tbody.innerHTML = '';
2233-
_cb.tested = 0; _cb.passed = 0; _cb.failed = 0;
2250+
_cb.tested = 0; _cb.passed = 0; _cb.slow = 0; _cb.failed = 0;
22342251
}
22352252
}
22362253
if (Array.isArray(d.results) && d.results.length) {
@@ -2254,11 +2271,10 @@ <h1 class="live-page-title">
22542271
testedAt: r.testedAt || null,
22552272
});
22562273
}
2257-
// Sync the current-batch counter to the replayed rows so the
2258-
// Tested tile + cb panel reflect actual progress on first paint.
2259-
_cb.tested = resultsArr.length;
2260-
_cb.passed = resultsArr.filter(x => Number(x.actualMbps) >= 10).length;
2261-
_cb.failed = _cb.tested - _cb.passed;
2274+
// Sync the current-batch counter to the replayed rows so the Tested
2275+
// tile + cb panel reflect actual progress on first paint; cbRender
2276+
// (below) derives the Passed SLA / Slow / Failed partition.
2277+
_cb.tested = resultsArr.length;
22622278
}
22632279
applyHeaderStatsFromState();
22642280
renderLiveStats();

0 commit comments

Comments
 (0)