diff --git a/public/nodes.js b/public/nodes.js index d1322b31..05f98a72 100644 --- a/public/nodes.js +++ b/public/nodes.js @@ -781,7 +781,7 @@ ${observers.length || relayObserverCount ? `
${(() => { const regions = [...new Set(observers.map(o => o.iata).filter(Boolean))]; return regions.length ? `
Regions: ${regions.map(r => '' + escapeHtml(r) + '').join(' ')}
` : ''; })()}

Heard By — direct (${observers.length} observer${observers.length === 1 ? '' : 's'})

- ${observers.length ? '' : '
No observer is within radio range of this node.
'} + ${observers.length ? '' : '
No observation proves a direct reception here, which is not the same as being out of range.
'} ${observers.length ? ` @@ -1756,7 +1756,7 @@ ${observers.length || relayObserverCount ? `
${(() => { const regions = [...new Set(observers.map(o => o.iata).filter(Boolean))]; return regions.length ? `
Regions: ${regions.join(', ')}
` : ''; })()}

Heard By — direct (${observers.length} observer${observers.length === 1 ? '' : 's'})

- ${observers.length ? '' : '
No observer is within radio range of this node.
'} + ${observers.length ? '' : '
No observation proves a direct reception here, which is not the same as being out of range.
'}
${observers.map(o => { const stats = [`${o.packetCount} pkts`]; diff --git a/tests/unit/test-direct-rf-heard-by.js b/tests/unit/test-direct-rf-heard-by.js index 28a219b4..c308f71b 100644 --- a/tests/unit/test-direct-rf-heard-by.js +++ b/tests/unit/test-direct-rf-heard-by.js @@ -66,8 +66,19 @@ test('relayed observers are reported as a count, without signal numbers', () => test('a node nobody hears directly still renders the card, with an empty state', () => { const html = renderCard([], 35, esc); assert.ok(/Heard By — direct \(0 observers\)/.test(html), 'expected a zero direct heading'); - assert.ok(/No observer is within radio range of this node\./.test(html), + assert.ok(/No observation proves a direct reception here/.test(html), 'expected the empty state line'); + // The empty state must not assert that nobody is in range. It cannot know + // that: direct-routed traffic carries no sender (firmware Mesh.cpp calls + // removeSelfFromPath before retransmitting) and ambiguous relay hops are + // left unattributed, so a node can be heard by several observers and still + // have no attributable direct reception. Measured on a production instance + // when this card shipped: 16 of 40 sampled repeaters showed the empty state + // while the same card counted relay observers two lines below. + assert.ok(!/No observer is within radio range/i.test(html), + 'the empty state must not assert that nobody is in range'); + assert.ok(/not the same as being out of range/i.test(html), + 'the empty state must say what it cannot conclude, not only what it found'); assert.ok(/Seen via relay by 35 observers/.test(html), 'expected the relay count'); assert.ok(!/observer-sort-table/.test(html), 'an empty direct list must not render a table header with no rows');
Observer