mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-09-27 05:27:55 +00:00
fix: normalize configured_scope to #-prefixed form like default_scope
cwichura flagged on PR #1867 that nodes.configured_scope (from the /neighbors OTA report) was stored as the observer's raw scope string ("dk") while default_scope and every other scope display use the #-prefixed hashRegion form ("#dk"). Apply the same regions.Normalize already used for default_scope, with "*" passed through unprefixed since it's a protocol wildcard, not a region name.
This commit is contained in:
@@ -16,6 +16,7 @@ import (
|
||||
|
||||
"github.com/meshcore-analyzer/dbschema"
|
||||
"github.com/meshcore-analyzer/packetpath"
|
||||
"github.com/meshcore-analyzer/regions"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
@@ -1729,6 +1730,7 @@ func (s *Store) UpdateNodeConfiguredScope(pubkey, scope, reportedAt string) erro
|
||||
if pubkey == "" {
|
||||
return nil
|
||||
}
|
||||
scope = normalizeConfiguredScopeList(scope)
|
||||
// Last-write-wins: skip if the stored confirmation is newer-or-equal.
|
||||
if reportedAt != "" {
|
||||
var curAt sql.NullString
|
||||
@@ -1749,6 +1751,40 @@ func (s *Store) UpdateNodeConfiguredScope(pubkey, scope, reportedAt string) erro
|
||||
return err
|
||||
}
|
||||
|
||||
// normalizeConfiguredScopeList applies the same "#"-prefix normalization
|
||||
// default_scope already gets (regions.Normalize, via matchScope) to a
|
||||
// comma-separated /neighbors-report scope list, so both fields display
|
||||
// consistently (flagged by cwichura on PR #1867: configured_scope was
|
||||
// stored as the observer's raw OTA-query string -- "dk", not "#dk").
|
||||
//
|
||||
// "*" is passed through UNCHANGED: it's a MeshCore protocol wildcard
|
||||
// ("responds to any scope"), not a real hashRegion name, so "#"-prefixing
|
||||
// it would misleadingly present it as one. Empty input stays empty -- a
|
||||
// responded-with-no-scopes report is a valid, meaningful "none configured"
|
||||
// state (see UpdateNodeConfiguredScope's doc comment) and must not gain a
|
||||
// stray "#".
|
||||
func normalizeConfiguredScopeList(raw string) string {
|
||||
if raw == "" {
|
||||
return ""
|
||||
}
|
||||
parts := strings.Split(raw, ",")
|
||||
out := make([]string, 0, len(parts))
|
||||
for _, p := range parts {
|
||||
p = strings.TrimSpace(p)
|
||||
if p == "" {
|
||||
continue
|
||||
}
|
||||
if p == "*" {
|
||||
out = append(out, p)
|
||||
continue
|
||||
}
|
||||
if name, ok := regions.Normalize(p); ok {
|
||||
out = append(out, name)
|
||||
}
|
||||
}
|
||||
return strings.Join(out, ",")
|
||||
}
|
||||
|
||||
// RecordNaiveSkew is called when resolveRxTime() clamps a packet's envelope
|
||||
// timestamp because the observer is emitting a zone-less local-time string
|
||||
// off from UTC by more than 15 min (issue #1478). Stamps the observer's
|
||||
|
||||
@@ -77,13 +77,13 @@ func TestHandleNeighborsReportWritesSelfAndResponded(t *testing.T) {
|
||||
if sc, _ := configuredScope(t, store, originLower); !sc.Valid || sc.String != "*" {
|
||||
t.Errorf("self configured_scope = %v, want '*'", sc)
|
||||
}
|
||||
// responded neighbor written, lowercased.
|
||||
if sc, _ := configuredScope(t, store, respLower); !sc.Valid || sc.String != "de,eu" {
|
||||
t.Errorf("responded configured_scope = %v, want 'de,eu'", sc)
|
||||
// responded neighbor written, lowercased, and normalized to #-prefixed scope form.
|
||||
if sc, _ := configuredScope(t, store, respLower); !sc.Valid || sc.String != "#de,#eu" {
|
||||
t.Errorf("responded configured_scope = %v, want '#de,#eu'", sc)
|
||||
}
|
||||
// timeout neighbor untouched — prior confirmed value survives.
|
||||
if sc, _ := configuredScope(t, store, timeoutLower); !sc.Valid || sc.String != "eu" {
|
||||
t.Errorf("timeout node configured_scope = %v, want prior 'eu' (must not be cleared)", sc)
|
||||
if sc, _ := configuredScope(t, store, timeoutLower); !sc.Valid || sc.String != "#eu" {
|
||||
t.Errorf("timeout node configured_scope = %v, want prior '#eu' (must not be cleared)", sc)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,22 +139,22 @@ func TestUpdateNodeConfiguredScopeLastWriteWins(t *testing.T) {
|
||||
if err := store.UpdateNodeConfiguredScope(pk, "stale", "2026-07-24T00:00:00Z"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if sc, _ := configuredScope(t, store, pk); sc.String != "eu" {
|
||||
t.Errorf("configured_scope = %q, want 'eu' (older report must not overwrite)", sc.String)
|
||||
if sc, _ := configuredScope(t, store, pk); sc.String != "#eu" {
|
||||
t.Errorf("configured_scope = %q, want '#eu' (older report must not overwrite)", sc.String)
|
||||
}
|
||||
// Newer report updates.
|
||||
if err := store.UpdateNodeConfiguredScope(pk, "de", "2026-07-26T00:00:00Z"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if sc, _ := configuredScope(t, store, pk); sc.String != "de" {
|
||||
t.Errorf("configured_scope = %q, want 'de' (newer report should update)", sc.String)
|
||||
if sc, _ := configuredScope(t, store, pk); sc.String != "#de" {
|
||||
t.Errorf("configured_scope = %q, want '#de' (newer report should update)", sc.String)
|
||||
}
|
||||
// inactive_nodes mirrored.
|
||||
var inactive sql.NullString
|
||||
if err := store.db.QueryRow(`SELECT configured_scope FROM inactive_nodes WHERE public_key = ?`, pk).Scan(&inactive); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if inactive.String != "de" {
|
||||
t.Errorf("inactive_nodes.configured_scope = %q, want 'de'", inactive.String)
|
||||
if inactive.String != "#de" {
|
||||
t.Errorf("inactive_nodes.configured_scope = %q, want '#de'", inactive.String)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user