mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-11-11 17:37:40 +00:00
test(nodes): region fallback places never-zero-hop nodes by fewest hops (#1879)
Tests plus a stub GetNodeFallbackRegionSets; fails on assertions: the fallback still puts a node in every region that heard it.
This commit is contained in:
1 parent
80bad40740
commit
a66a7f36ee
3 files changed
+92
No files matched your search
@@ -27,6 +27,10 @@ import (
|
||||
|
||||
const nodeHomeRegionsRecomputerDefaultInterval = 5 * time.Minute
|
||||
|
||||
// regionFallbackMaxHops bounds the fallback for nodes never heard zero-hop: a
|
||||
// node whose closest hearing took more hops than this is in no region.
|
||||
const regionFallbackMaxHops = 3
|
||||
|
||||
var (
|
||||
nodeHomeRegionsRecompStartedMu sync.Mutex
|
||||
nodeHomeRegionsRecompStarted bool
|
||||
@@ -129,3 +133,10 @@ func enrichNodeHomeRegions(node map[string]interface{}, regions []string) {
|
||||
}
|
||||
node["home_regions"] = regions
|
||||
}
|
||||
|
||||
// GetNodeFallbackRegionSets returns the current fallback snapshot (pubkey →
|
||||
// sorted regions, empty for a node in no region), or an empty map before the
|
||||
// first compute. Read-only by convention.
|
||||
func (s *PacketStore) GetNodeFallbackRegionSets() map[string][]string {
|
||||
return map[string][]string{}
|
||||
}
|
||||
@@ -135,3 +135,81 @@ func TestHandleNodesHomeRegions(t *testing.T) {
|
||||
t.Fatalf("expected both seeded nodes in the response, saw %d", seen)
|
||||
}
|
||||
}
|
||||
|
||||
// mkPathAdvert is a relayed flood advert heard by each observer over the given
|
||||
// path (JSON array of hop hashes, "[]" for a direct hearing).
|
||||
func mkPathAdvert(id int, pubkey string, hearings map[string]string) *StoreTx {
|
||||
tx := mkHomeRegionAdvert(id, pubkey, homeRelayedFlood)
|
||||
i := 0
|
||||
for oid, path := range hearings {
|
||||
tx.Observations = append(tx.Observations, &StoreObs{
|
||||
ID: id*10 + i, TransmissionID: id, ObserverID: oid, PathJSON: path,
|
||||
})
|
||||
i++
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
// A node never heard zero-hop used to be in every region that heard any of its
|
||||
// adverts. Adverts flood the whole mesh, so on a statewide deployment that put
|
||||
// most nodes in every region. It now goes in the regions that heard it in the
|
||||
// fewest hops, and in none when even that is over regionFallbackMaxHops.
|
||||
func TestRegionFallbackUsesFewestHops(t *testing.T) {
|
||||
ps := newRegionTestStore(t, map[string]string{"o-aus": "AUS", "o-sat": "SAT", "o-hou": "HOU"})
|
||||
addToStore(ps,
|
||||
// One hop from SAT, relayed four hops to AUS: SAT only.
|
||||
mkPathAdvert(1, "pk_near", map[string]string{"o-sat": `["aa"]`, "o-aus": `["aa","bb","cc","dd"]`}),
|
||||
// Tied fewest hops: both.
|
||||
mkPathAdvert(2, "pk_tie", map[string]string{"o-sat": `["aa","bb"]`, "o-hou": `["cc","dd"]`, "o-aus": `["aa","bb","cc"]`}),
|
||||
// Exactly regionFallbackMaxHops is still placed.
|
||||
mkPathAdvert(3, "pk_cap", map[string]string{"o-aus": `["aa","bb","cc"]`}),
|
||||
// Closest hearing beyond the cap: no region.
|
||||
mkPathAdvert(4, "pk_gulf", map[string]string{"o-aus": `["aa","bb","cc","dd"]`, "o-hou": `["aa","bb","cc","dd","ee","ff"]`}),
|
||||
// A later advert heard closer counts.
|
||||
mkPathAdvert(5, "pk_gulf2", map[string]string{"o-hou": `["aa","bb","cc","dd","ee"]`}),
|
||||
mkPathAdvert(6, "pk_gulf2", map[string]string{"o-hou": `["aa"]`}),
|
||||
)
|
||||
ps.recomputeNodeHomeRegionSets()
|
||||
|
||||
cases := []struct {
|
||||
region string
|
||||
want []string
|
||||
}{
|
||||
{"SAT", []string{"pk_near", "pk_tie"}},
|
||||
{"AUS", []string{"pk_cap"}},
|
||||
{"HOU", []string{"pk_gulf2", "pk_tie"}},
|
||||
}
|
||||
for _, c := range cases {
|
||||
got, ok := ps.RegionNodePubkeys(c.region)
|
||||
if !ok {
|
||||
t.Fatalf("region %q: ok=false", c.region)
|
||||
}
|
||||
if g := sortedKeys(got); !reflect.DeepEqual(g, c.want) {
|
||||
t.Errorf("region %q: got %v, want %v", c.region, g, c.want)
|
||||
}
|
||||
}
|
||||
|
||||
want := map[string][]string{
|
||||
"pk_near": {"SAT"},
|
||||
"pk_tie": {"HOU", "SAT"},
|
||||
"pk_cap": {"AUS"},
|
||||
"pk_gulf": {},
|
||||
"pk_gulf2": {"HOU"},
|
||||
}
|
||||
if got := ps.GetNodeFallbackRegionSets(); !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("fallback region sets = %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// Zero-hop home regions still win: a node with any zero-hop hearing is not in
|
||||
// the fallback snapshot at all.
|
||||
func TestRegionFallbackSkipsZeroHopNodes(t *testing.T) {
|
||||
ps := newHomeRegionStore(t)
|
||||
fb := ps.GetNodeFallbackRegionSets()
|
||||
if _, has := fb["pk_aus"]; has {
|
||||
t.Errorf("pk_aus has zero-hop home regions but is in the fallback: %v", fb)
|
||||
}
|
||||
if !reflect.DeepEqual(fb["pk_far"], []string{"SAT"}) {
|
||||
t.Errorf("pk_far fallback = %v, want [SAT]", fb["pk_far"])
|
||||
}
|
||||
}
|
||||
@@ -448,6 +448,9 @@ type PacketStore struct {
|
||||
// #1879: pubkey → regions whose observers heard it zero-hop, for the
|
||||
// /api/nodes region filter. Written by the node-home-regions recomputer.
|
||||
nodeHomeRegionSets atomic.Pointer[map[string][]string]
|
||||
// #1879: pubkey → regions that heard it in the fewest hops, for nodes
|
||||
// never heard zero-hop; empty when even that is over regionFallbackMaxHops.
|
||||
nodeFallbackRegionSets atomic.Pointer[map[string][]string]
|
||||
|
||||
// Coverage + Redundancy axes (issue #672 axes 3 & 4 of 4): atomic
|
||||
// snapshots of pubkey → 0..1 score over the current neighbor graph.
|
||||
|
||||
Reference in new issue
Block a user