mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-07-17 22:01:54 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
201fa2787d |
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -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 ? '' : '<div class="panel-resize-handle" id="pktResizeHandle"></div>' + 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 = `<div class="text-muted">Error: ${e.message}</div>`;
|
||||
|
||||
@@ -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)}`);
|
||||
|
||||
Reference in New Issue
Block a user