mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-09-27 07:09:45 +00:00
fix: View Path airtime estimate omits fields on 0-relay (direct) packets
Caught on real stg data: a directly-received packet has relays=0, and while the plain int AirtimeRelayCount's omitempty correctly dropped it from JSON, the *float64 EstimatedAirtimeMs still encoded as a bare "estimatedAirtimeMs":0 (a non-nil pointer isn't "empty" to omitempty even when it points at zero). The frontend's typeof-number check then rendered "~0ms estimated airtime (undefined relays)". Now both fields are omitted together whenever there's nothing to relay. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
e9e828c7e9
commit
e81f89da1e
@@ -112,6 +112,65 @@ func TestHandlePacketPath_Airtime(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestHandlePacketPath_Airtime_ZeroRelays confirms the fields are omitted
|
||||
// -- not a bare "estimatedAirtimeMs":0 with no accompanying relay count --
|
||||
// for a directly-received packet (no resolved_path relays at all). This
|
||||
// caught a real bug on stg: the *float64 EstimatedAirtimeMs survived JSON
|
||||
// encoding as 0 (a non-nil pointer isn't "empty"), while the plain int
|
||||
// AirtimeRelayCount's omitempty dropped it at exactly 0, leaving the
|
||||
// frontend a number with nothing to pair it with.
|
||||
func TestHandlePacketPath_Airtime_ZeroRelays(t *testing.T) {
|
||||
srv, router := setupTestServer(t)
|
||||
if _, err := srv.db.conn.Exec(`DELETE FROM transmissions`); err != nil {
|
||||
t.Fatalf("clear transmissions: %v", err)
|
||||
}
|
||||
if _, err := srv.db.conn.Exec(`DELETE FROM observations`); err != nil {
|
||||
t.Fatalf("clear observations: %v", err)
|
||||
}
|
||||
|
||||
txRes, err := srv.db.conn.Exec(`INSERT INTO transmissions (raw_hex, hash, first_seen, route_type, payload_type, decoded_json, channel_hash)
|
||||
VALUES ('AABBCCDDEE', 'airtimepath00003', '2026-01-15T10:00:00Z', 1, 5,
|
||||
'{"type":"CHAN","channel":"#ping","text":"ping","sender":"Eve"}', '#ping')`)
|
||||
if err != nil {
|
||||
t.Fatalf("insert tx: %v", err)
|
||||
}
|
||||
txID, _ := txRes.LastInsertId()
|
||||
obsRes, err := srv.db.conn.Exec(`INSERT INTO observers (id, name, iata) VALUES (?,?,?)`, "airtimeobs1", "ObsOne", "SJC")
|
||||
if err != nil {
|
||||
t.Fatalf("insert observer: %v", err)
|
||||
}
|
||||
obsIdx, _ := obsRes.LastInsertId()
|
||||
if _, err := srv.db.conn.Exec(
|
||||
`INSERT INTO observations (transmission_id, observer_idx, snr, rssi, path_json, resolved_path, timestamp) VALUES (?,?,?,?,?,?,?)`,
|
||||
txID, obsIdx, 9.0, -88.0, `[]`, `[]`, 1736935200,
|
||||
); err != nil {
|
||||
t.Fatalf("insert observation: %v", err)
|
||||
}
|
||||
if err := srv.store.Load(); err != nil {
|
||||
t.Fatalf("reload store: %v", err)
|
||||
}
|
||||
if !srv.store.WaitIndexesReady(5 * time.Second) {
|
||||
t.Fatal("background indexes never became ready after reload")
|
||||
}
|
||||
|
||||
req := httptest.NewRequest("GET", "/api/packets/airtimepath00003/path", nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
if w.Code != 200 {
|
||||
t.Fatalf("status=%d body=%s", w.Code, w.Body.String())
|
||||
}
|
||||
var raw map[string]interface{}
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &raw); err != nil {
|
||||
t.Fatalf("decode: %v", err)
|
||||
}
|
||||
if _, present := raw["estimatedAirtimeMs"]; present {
|
||||
t.Errorf("estimatedAirtimeMs = %v, want absent for a direct reception with 0 relays", raw["estimatedAirtimeMs"])
|
||||
}
|
||||
if _, present := raw["airtimeRelayCount"]; present {
|
||||
t.Errorf("airtimeRelayCount = %v, want absent for a direct reception with 0 relays", raw["airtimeRelayCount"])
|
||||
}
|
||||
}
|
||||
|
||||
// TestHandlePacketPath_Airtime_StoreUnavailable confirms the field is
|
||||
// simply omitted -- not a guessed zero -- when the in-memory store has no
|
||||
// record of this transmission's ID (e.g. DB-only mode, or an old packet
|
||||
|
||||
@@ -3286,7 +3286,12 @@ func (s *Server) annotatePacketPathAirtime(resp *PacketPathResponse) {
|
||||
return
|
||||
}
|
||||
total, relays, ok := s.store.AirtimeAndRelayCountForTransmission(resp.TxID)
|
||||
if !ok {
|
||||
if !ok || relays == 0 {
|
||||
// relays == 0 means a direct reception with nothing to relay --
|
||||
// there's no meaningful airtime estimate to show (and 0 would
|
||||
// otherwise survive JSON encoding as a bare "estimatedAirtimeMs":0
|
||||
// while the omitempty int AirtimeRelayCount vanishes alongside it,
|
||||
// leaving the frontend with a number but no relay count to pair it with).
|
||||
return
|
||||
}
|
||||
ms := float64(total.Microseconds()) / 1000.0
|
||||
|
||||
Reference in New Issue
Block a user