From 201fa2787d6b67aff03a4866f09ea33b099d5063 Mon Sep 17 00:00:00 2001 From: Kpa-clawbot Date: Tue, 21 Apr 2026 17:59:50 +0000 Subject: [PATCH] =?UTF-8?q?Revert=20"fix(#866):=20full-page=20obs-switch?= =?UTF-8?q?=20=E2=80=94=20update=20hex=20+=20path=20+=20direction=20per=20?= =?UTF-8?q?observation=20(#870)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 42ff5a291be9b2dc7b283b51df5f57a9016cd265. The fix for #866 made the obs-switch UX worse, not better: - Location goes blank when switching observations - Path stays stuck on 'direct' across observations - Hex bytes don't update - Decoded fields don't update #866 remains open. A correct fix will follow in a separate PR that starts from a clean slate (not a follow-up to this change). Reopens #866. --- cmd/server/routes.go | 3 - cmd/server/types.go | 3 - public/packets.js | 6 +- test-frontend-helpers.js | 138 --------------------------------------- 4 files changed, 3 insertions(+), 147 deletions(-) diff --git a/cmd/server/routes.go b/cmd/server/routes.go index 0a9fcad0..a05c99ca 100644 --- a/cmd/server/routes.go +++ b/cmd/server/routes.go @@ -2386,9 +2386,6 @@ func mapSliceToObservations(maps []map[string]interface{}) []ObservationResp { obs.SNR = m["snr"] obs.RSSI = m["rssi"] obs.PathJSON = m["path_json"] - obs.ResolvedPath = m["resolved_path"] - obs.Direction = m["direction"] - obs.RawHex = m["raw_hex"] obs.Timestamp = m["timestamp"] result = append(result, obs) } diff --git a/cmd/server/types.go b/cmd/server/types.go index 165ecffd..fc4cbd1d 100644 --- a/cmd/server/types.go +++ b/cmd/server/types.go @@ -277,9 +277,6 @@ type ObservationResp struct { SNR interface{} `json:"snr"` RSSI interface{} `json:"rssi"` PathJSON interface{} `json:"path_json"` - ResolvedPath interface{} `json:"resolved_path,omitempty"` - Direction interface{} `json:"direction,omitempty"` - RawHex interface{} `json:"raw_hex,omitempty"` Timestamp interface{} `json:"timestamp"` } diff --git a/public/packets.js b/public/packets.js index b644b354..fbbf9946 100644 --- a/public/packets.js +++ b/public/packets.js @@ -387,7 +387,7 @@ const obs = data.observations.find(o => String(o.id) === String(obsTarget)); if (obs) { expandedHashes.add(h); - const obsPacket = {...data.packet, observer_id: obs.observer_id, observer_name: obs.observer_name, snr: obs.snr, rssi: obs.rssi, path_json: obs.path_json, resolved_path: obs.resolved_path, direction: obs.direction, timestamp: obs.timestamp, first_seen: obs.timestamp}; + const obsPacket = {...data.packet, observer_id: obs.observer_id, observer_name: obs.observer_name, snr: obs.snr, rssi: obs.rssi, path_json: obs.path_json, resolved_path: obs.resolved_path, timestamp: obs.timestamp, first_seen: obs.timestamp}; clearParsedCache(obsPacket); selectPacket(obs.id, h, {packet: obsPacket, breakdown: data.breakdown, observations: data.observations}, obs.id); } else { @@ -1246,7 +1246,7 @@ const child = group?._children?.find(c => String(c.id) === String(value)); if (child) { const parentData = group._fetchedData; - const obsPacket = parentData ? {...parentData.packet, observer_id: child.observer_id, observer_name: child.observer_name, snr: child.snr, rssi: child.rssi, path_json: child.path_json, resolved_path: child.resolved_path, direction: child.direction, timestamp: child.timestamp, first_seen: child.timestamp} : child; + const obsPacket = parentData ? {...parentData.packet, observer_id: child.observer_id, observer_name: child.observer_name, snr: child.snr, rssi: child.rssi, path_json: child.path_json, resolved_path: child.resolved_path, timestamp: child.timestamp, first_seen: child.timestamp} : child; if (parentData) { clearParsedCache(obsPacket); } selectPacket(child.id, parentHash, {packet: obsPacket, breakdown: parentData?.breakdown, observations: parentData?.observations}, child.id); } @@ -1797,7 +1797,7 @@ panel.innerHTML = isMobileNow ? '' : '
' + PANEL_CLOSE_HTML; const content = document.createElement('div'); panel.appendChild(content); - await renderDetail(content, data, selectedObservationId); + await renderDetail(content, data); if (!isMobileNow) initPanelResize(); } catch (e) { panel.innerHTML = `
Error: ${e.message}
`; diff --git a/test-frontend-helpers.js b/test-frontend-helpers.js index 3c06ba9d..2610a7b5 100644 --- a/test-frontend-helpers.js +++ b/test-frontend-helpers.js @@ -6116,144 +6116,6 @@ console.log('\n=== analytics.js: renderCollisionsFromServer collision table ===' }); } -// ===== Issue #866: Full-page obs-switch — hex + path must update per observation ===== -{ - console.log('\n=== Issue #866: Full-page observation switch ==='); - - const ctx866 = makeSandbox(); - loadInCtx(ctx866, 'public/roles.js'); - loadInCtx(ctx866, 'public/app.js'); - loadInCtx(ctx866, 'public/packet-helpers.js'); - - test('#866: switching observation updates effectivePkt path_json', () => { - const pkt = { id: 1, hash: 'abc123', observer_id: 'obs-agg', path_json: '["A","B","C","D"]', raw_hex: '0484A1B1C1D1', route_type: 1, timestamp: '2026-01-01T00:00:00Z' }; - const obs1 = { id: 10, observer_id: 'obs-1', path_json: '["A","B"]', snr: 5, rssi: -80, timestamp: '2026-01-01T00:01:00Z' }; - const obs2 = { id: 20, observer_id: 'obs-2', path_json: '["A","B","C","D"]', snr: 8, rssi: -75, timestamp: '2026-01-01T00:02:00Z' }; - - // Simulate renderDetail logic: pick obs1 - const eff1 = ctx866.clearParsedCache({...pkt, ...obs1, _isObservation: true}); - const path1 = ctx866.getParsedPath(eff1); - assert.deepStrictEqual(path1, ['A', 'B']); - assert.strictEqual(eff1.observer_id, 'obs-1'); - assert.strictEqual(eff1.snr, 5); - - // Switch to obs2 - const eff2 = ctx866.clearParsedCache({...pkt, ...obs2, _isObservation: true}); - const path2 = ctx866.getParsedPath(eff2); - assert.deepStrictEqual(path2, ['A', 'B', 'C', 'D']); - assert.strictEqual(eff2.observer_id, 'obs-2'); - assert.strictEqual(eff2.snr, 8); - }); - - test('#866: effectivePkt preserves raw_hex from packet when obs has none', () => { - const pkt = { id: 1, hash: 'h1', raw_hex: '0482AABB', route_type: 1 }; - const obs = { id: 10, observer_id: 'obs-1', path_json: '["AA"]', snr: 3, rssi: -90, timestamp: '2026-01-01T00:00:00Z' }; - const eff = ctx866.clearParsedCache({...pkt, ...obs, _isObservation: true}); - // obs doesn't have raw_hex, so packet's raw_hex survives spread - assert.strictEqual(eff.raw_hex, '0482AABB'); - }); - - test('#866: effectivePkt uses obs raw_hex when available (API now returns it)', () => { - const pkt = { id: 1, hash: 'h1', raw_hex: '0482AABB', route_type: 1 }; - const obs = { id: 10, observer_id: 'obs-1', raw_hex: '0441CC', path_json: '["CC"]', snr: 3, rssi: -90, timestamp: '2026-01-01T00:00:00Z' }; - const eff = ctx866.clearParsedCache({...pkt, ...obs, _isObservation: true}); - // obs has raw_hex from API, should override - assert.strictEqual(eff.raw_hex, '0441CC'); - }); - - test('#866: direction field carried through observation spread', () => { - const pkt = { id: 1, hash: 'h1', direction: 'rx', route_type: 1 }; - const obs = { id: 10, observer_id: 'obs-1', direction: 'tx', path_json: '[]', timestamp: '2026-01-01T00:00:00Z' }; - const eff = {...pkt, ...obs, _isObservation: true}; - assert.strictEqual(eff.direction, 'tx'); - }); - - test('#866: resolved_path carried through observation spread', () => { - const pkt = { id: 1, hash: 'h1', resolved_path: '["aaa","bbb","ccc"]', route_type: 1 }; - const obs = { id: 10, observer_id: 'obs-1', resolved_path: '["aaa"]', path_json: '["AA"]', timestamp: '2026-01-01T00:00:00Z' }; - const eff = ctx866.clearParsedCache({...pkt, ...obs, _isObservation: true}); - const rp = ctx866.getResolvedPath(eff); - assert.deepStrictEqual(rp, ['aaa']); - }); - - test('#866: getPathLenOffset used for hop count cross-check', () => { - // Flood route: offset 1 - assert.strictEqual(ctx866.getPathLenOffset(1), 1); - assert.strictEqual(ctx866.getPathLenOffset(2), 1); - // Transport route: offset 5 - assert.strictEqual(ctx866.getPathLenOffset(0), 5); - assert.strictEqual(ctx866.getPathLenOffset(3), 5); - }); - - test('#866: URL hash should encode obs parameter for deep linking', () => { - // Simulate the URL construction pattern from renderDetail obs click - const pktHash = 'abc123def456'; - const obsId = '42'; - const url = `#/packets/${pktHash}?obs=${obsId}`; - assert.strictEqual(url, '#/packets/abc123def456?obs=42'); - // Parse back - const qIdx = url.indexOf('?'); - const qs = new URLSearchParams(url.substring(qIdx)); - assert.strictEqual(qs.get('obs'), '42'); - }); -} - -// ===== #872 — hop-display unreliable badge ===== -{ - console.log('\n--- #872: hop-display unreliable warning badge ---'); - - function makeHopDisplaySandbox() { - const sb = { - window: { addEventListener: () => {}, dispatchEvent: () => {} }, - document: { - readyState: 'complete', - createElement: () => ({ id: '', textContent: '', innerHTML: '' }), - head: { appendChild: () => {} }, - getElementById: () => null, - addEventListener: () => {}, - querySelectorAll: () => [], - querySelector: () => null, - }, - console, - Date, Math, Array, Object, String, Number, JSON, RegExp, Map, Set, - encodeURIComponent, parseInt, parseFloat, isNaN, Infinity, NaN, undefined, - setTimeout: () => {}, setInterval: () => {}, clearTimeout: () => {}, clearInterval: () => {}, - }; - sb.window.document = sb.document; - sb.self = sb.window; - sb.globalThis = sb.window; - const ctx = vm.createContext(sb); - const hopSrc = fs.readFileSync(__dirname + '/public/hop-display.js', 'utf8'); - vm.runInContext(hopSrc, ctx); - return ctx; - } - - const hopCtx = makeHopDisplaySandbox(); - - test('#872: unreliable hop renders warning badge, not strikethrough', () => { - const html = hopCtx.window.HopDisplay.renderHop('AABB', { - name: 'TestNode', pubkey: 'pk123', unreliable: true, - ambiguous: false, conflicts: [], globalFallback: false, - }, {}); - // Must contain unreliable warning badge button - assert.ok(html.includes('hop-unreliable-btn'), 'should have unreliable badge button'); - assert.ok(html.includes('⚠️'), 'should have ⚠️ icon'); - assert.ok(html.includes('Unreliable name resolution'), 'should have tooltip text'); - // Must NOT contain line-through in inline style (CSS class no longer has it) - assert.ok(!html.includes('line-through'), 'should not contain line-through'); - // Should still have hop-unreliable class for subtle styling - assert.ok(html.includes('hop-unreliable'), 'should have hop-unreliable class'); - }); - - test('#872: reliable hop does NOT render unreliable badge', () => { - const html = hopCtx.window.HopDisplay.renderHop('CCDD', { - name: 'GoodNode', pubkey: 'pk456', unreliable: false, - ambiguous: false, conflicts: [], globalFallback: false, - }, {}); - assert.ok(!html.includes('hop-unreliable-btn'), 'should not have unreliable badge'); - }); -} - // ===== SUMMARY ===== Promise.allSettled(pendingTests).then(() => { console.log(`\n${'═'.repeat(40)}`);