mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-07-02 13:51:41 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5c2c71c6ee |
+72
-11
@@ -2145,6 +2145,13 @@ func setupRichTestDB(t *testing.T) *DB {
|
||||
db.conn.Exec(`INSERT INTO observations (transmission_id, observer_idx, snr, rssi, path_json, timestamp)
|
||||
VALUES (5, 1, 14.0, -88, '["aa"]', ?)`, recentEpoch)
|
||||
|
||||
// Extra packet sharing subpath "eeff,0011" with hash_with_path_02 above,
|
||||
// so that subpath has count>=2 and survives singleton pruning.
|
||||
db.conn.Exec(`INSERT INTO transmissions (raw_hex, hash, first_seen, route_type, payload_type, decoded_json)
|
||||
VALUES ('0140eeff0011', 'hash_shared_subpath', ?, 1, 4, '{"pubKey":"eeff001199887766","name":"TestShared","type":"ADVERT"}')`, recent)
|
||||
db.conn.Exec(`INSERT INTO observations (transmission_id, observer_idx, snr, rssi, path_json, timestamp)
|
||||
VALUES (6, 1, 9.0, -92, '["eeff","0011"]', ?)`, recentEpoch)
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
@@ -2276,14 +2283,11 @@ func TestSubpathPrecomputedIndex(t *testing.T) {
|
||||
t.Fatal("expected spTotalPaths > 0 after Load()")
|
||||
}
|
||||
|
||||
// The rich test DB has paths ["aa","bb"], ["aabb","ccdd"], and
|
||||
// ["eeff","0011","2233"]. That yields 5 unique raw subpaths.
|
||||
// The rich test DB has paths ["aa","bb"], ["aabb","ccdd"],
|
||||
// ["eeff","0011","2233"], and ["eeff","0011"]. After singleton pruning,
|
||||
// only subpaths with count>=2 survive. "eeff,0011" appears in two packets.
|
||||
expectedRaw := map[string]int{
|
||||
"aa,bb": 1,
|
||||
"aabb,ccdd": 1,
|
||||
"eeff,0011": 1,
|
||||
"0011,2233": 1,
|
||||
"eeff,0011,2233": 1,
|
||||
"eeff,0011": 2,
|
||||
}
|
||||
for key, want := range expectedRaw {
|
||||
got, ok := store.spIndex[key]
|
||||
@@ -2293,8 +2297,16 @@ func TestSubpathPrecomputedIndex(t *testing.T) {
|
||||
t.Errorf("spIndex[%q] = %d, want %d", key, got, want)
|
||||
}
|
||||
}
|
||||
if store.spTotalPaths != 3 {
|
||||
t.Errorf("spTotalPaths = %d, want 3", store.spTotalPaths)
|
||||
|
||||
// Singleton subpaths must have been pruned
|
||||
singletons := []string{"aa,bb", "aabb,ccdd", "0011,2233", "eeff,0011,2233"}
|
||||
for _, key := range singletons {
|
||||
if _, ok := store.spIndex[key]; ok {
|
||||
t.Errorf("expected singleton spIndex[%q] to be pruned", key)
|
||||
}
|
||||
}
|
||||
if store.spTotalPaths != 4 {
|
||||
t.Errorf("spTotalPaths = %d, want 4", store.spTotalPaths)
|
||||
}
|
||||
|
||||
// Fast-path (no region) and slow-path (with region) must return the
|
||||
@@ -2333,8 +2345,8 @@ func TestSubpathTxIndexPopulated(t *testing.T) {
|
||||
t.Fatal("expected non-nil detail for existing subpath")
|
||||
}
|
||||
matches, _ := detail["totalMatches"].(int)
|
||||
if matches != 1 {
|
||||
t.Errorf("totalMatches = %d, want 1", matches)
|
||||
if matches != 2 {
|
||||
t.Errorf("totalMatches = %d, want 2", matches)
|
||||
}
|
||||
|
||||
// Non-existent subpath should return 0 matches
|
||||
@@ -2382,6 +2394,55 @@ func TestSubpathDetailMixedCaseHops(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestSubpathSingletonDrop verifies that singleton entries are pruned from
|
||||
// spIndex while count>=2 entries are preserved.
|
||||
func TestSubpathSingletonDrop(t *testing.T) {
|
||||
db := setupRichTestDB(t)
|
||||
defer db.Close()
|
||||
store := NewPacketStore(db, nil)
|
||||
store.Load()
|
||||
|
||||
// "eeff,0011" appears in 2 packets — must survive singleton pruning
|
||||
if count, ok := store.spIndex["eeff,0011"]; !ok {
|
||||
t.Fatal("expected spIndex[\"eeff,0011\"] to survive singleton pruning")
|
||||
} else if count != 2 {
|
||||
t.Errorf("spIndex[\"eeff,0011\"] = %d, want 2", count)
|
||||
}
|
||||
|
||||
// All count==1 entries must be gone
|
||||
for key, count := range store.spIndex {
|
||||
if count < 2 {
|
||||
t.Errorf("spIndex[%q] = %d, singletons should have been pruned", key, count)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestSubpathEmptyDB verifies that the store loads successfully on a DB
|
||||
// with no transmissions (no subpaths at all).
|
||||
func TestSubpathEmptyDB(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
store := NewPacketStore(db, nil)
|
||||
store.Load()
|
||||
|
||||
if len(store.spIndex) != 0 {
|
||||
t.Errorf("expected empty spIndex on empty DB, got %d entries", len(store.spIndex))
|
||||
}
|
||||
if store.spTotalPaths != 0 {
|
||||
t.Errorf("expected spTotalPaths=0 on empty DB, got %d", store.spTotalPaths)
|
||||
}
|
||||
|
||||
// GetSubpathDetail should still work (return zero matches)
|
||||
detail := store.GetSubpathDetail([]string{"aa", "bb"})
|
||||
if detail == nil {
|
||||
t.Fatal("expected non-nil detail even on empty DB")
|
||||
}
|
||||
matches, _ := detail["totalMatches"].(int)
|
||||
if matches != 0 {
|
||||
t.Errorf("totalMatches on empty DB = %d, want 0", matches)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoreGetAnalyticsRFCacheHit(t *testing.T) {
|
||||
db := setupRichTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
@@ -546,9 +546,6 @@ func TestEstimateStoreTxBytes(t *testing.T) {
|
||||
manualCalc := int64(storeTxBaseBytes) + int64(len(tx.RawHex)+len(tx.Hash)+len(tx.DecodedJSON)+len(tx.PathJSON)) + int64(numIndexesPerTx*indexEntryBytes)
|
||||
manualCalc += perTxMapsBytes
|
||||
manualCalc += hops * perPathHopBytes
|
||||
if hops > 1 {
|
||||
manualCalc += (hops * (hops - 1) / 2) * perSubpathEntryBytes
|
||||
}
|
||||
if est != manualCalc {
|
||||
t.Fatalf("estimateStoreTxBytes = %d, want %d (manual calc)", est, manualCalc)
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ func TestEstimateStoreTxBytes_ReasonableValues(t *testing.T) {
|
||||
}
|
||||
|
||||
// TestEstimateStoreTxBytes_ManyHopsSubpaths verifies that packets with many
|
||||
// hops estimate significantly more due to O(path²) subpath index entries.
|
||||
// hops estimate more due to per-hop byPathHop index entries.
|
||||
func TestEstimateStoreTxBytes_ManyHopsSubpaths(t *testing.T) {
|
||||
tx2 := &StoreTx{
|
||||
Hash: "aabb",
|
||||
@@ -43,12 +43,14 @@ func TestEstimateStoreTxBytes_ManyHopsSubpaths(t *testing.T) {
|
||||
est2 := estimateStoreTxBytes(tx2)
|
||||
est10 := estimateStoreTxBytes(tx10)
|
||||
|
||||
// 10 hops → 45 subpath combos × 40 = 1800 bytes just for subpaths
|
||||
// 10 hops vs 2 hops → 8 extra byPathHop entries × perPathHopBytes
|
||||
if est10 <= est2 {
|
||||
t.Errorf("10-hop (%d) should estimate more than 2-hop (%d)", est10, est2)
|
||||
}
|
||||
if est10 < est2+1500 {
|
||||
t.Errorf("10-hop (%d) should estimate at least 1500 more than 2-hop (%d)", est10, est2)
|
||||
// spTxIndex eliminated in #791; cost difference is now linear (per-hop only)
|
||||
expectedDiff := int64(8) * perPathHopBytes // 8 extra hops
|
||||
if est10 < est2+expectedDiff {
|
||||
t.Errorf("10-hop (%d) should estimate at least %d more than 2-hop (%d)", est10, expectedDiff, est2)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user