Files
beacon-server/db/paths_test.go
T
MrAlders0n ded8708fec Stats, adverts, traces and telemetry: bounded statements, same results (#243)
* feat(db): observer adverts on a partial index once observations carry their payload type

* fix(db): keep today's adverts statement unguarded and rate-limit the gate's read warning

* feat(background): batched, resumable payload-type backfill for untyped observations

* fix(background): cap each payload-type backfill statement at 50,000 rows

* fix(db): reject a non-positive backfill row cap

* perf(db): probe rollups per hour and IATA for region-sized stats lists

* fix(db): keep the stats probes inside each rollup table's hour range

* test(db): push the far-future until to year 9999 so a missing path-stats end bound times out

* perf(db): row-comparison trace cursor and a since-only telemetry statement

* test(db): pin the empty trace cursor tag and run the trace pagination tests in CI

* test(planguard): stats, adverts, traces and telemetry statements leave pending; diff cases

* test(planguard): window-bounded advert roles, short quiet page, per-observer telemetry cursors
2026-10-19 19:18:45 -04:00

73 lines
2.4 KiB
Go

// Copyright 2026 Beacon Contributors
// SPDX-License-Identifier: AGPL-3.0-or-later
package db
import (
"context"
"testing"
"time"
sqlc "github.com/MeshCore-Beacon/beacon-server/db/sqlc"
"github.com/jackc/pgx/v5/pgtype"
)
type pathBucketQuerier struct {
sqlc.Querier
row sqlc.GetPathStatsRow
}
func (q pathBucketQuerier) GetPathStats(context.Context, sqlc.GetPathStatsParams) ([]sqlc.GetPathStatsRow, error) {
return []sqlc.GetPathStatsRow{q.row}, nil
}
func (q pathBucketQuerier) GetPathStatsProbe(context.Context, sqlc.GetPathStatsProbeParams) ([]sqlc.GetPathStatsProbeRow, error) {
return []sqlc.GetPathStatsProbeRow{sqlc.GetPathStatsProbeRow(q.row)}, nil
}
func TestPathStatsRejectsInvalidDatabaseBucket(t *testing.T) {
for _, row := range []sqlc.GetPathStatsRow{
{HashBytes: 0}, {HashBytes: 4}, {HashBytes: 1, Entries: -1}, {HashBytes: 1, Entries: 64},
{HashBytes: 4, IsHourly: true},
} {
store := &Store{q: pathBucketQuerier{row: row}}
if _, err := store.GetPathStats(context.Background(), time.Time{}, time.Time{}, nil); err == nil {
t.Fatalf("invalid bucket accepted: %+v", row)
}
}
}
type pathRowsQuerier struct {
sqlc.Querier
rows []sqlc.GetPathStatsRow
}
func (q pathRowsQuerier) GetPathStats(context.Context, sqlc.GetPathStatsParams) ([]sqlc.GetPathStatsRow, error) {
return q.rows, nil
}
func (q pathRowsQuerier) GetPathStatsProbe(context.Context, sqlc.GetPathStatsProbeParams) ([]sqlc.GetPathStatsProbeRow, error) {
out := make([]sqlc.GetPathStatsProbeRow, len(q.rows))
for i, r := range q.rows {
out[i] = sqlc.GetPathStatsProbeRow(r)
}
return out, nil
}
// An hour's maxEntries is the largest across its category/width rows, not the last one seen.
func TestPathStatsHourlyMaxEntries(t *testing.T) {
hour := pgtype.Timestamptz{Time: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}
store := &Store{q: pathRowsQuerier{rows: []sqlc.GetPathStatsRow{
{IsHourly: true, Hour: hour, Category: 0, HashBytes: 1, Receptions: 3, MaxEntries: 9},
{IsHourly: true, Hour: hour, Category: 0, HashBytes: 2, Receptions: 1, MaxEntries: 5},
{IsHourly: true, Hour: hour, Category: 1, HashBytes: 0, Receptions: 2, MaxEntries: 0},
}}}
got, err := store.GetPathStats(context.Background(), time.Time{}, time.Time{}, nil)
if err != nil {
t.Fatal(err)
}
if len(got.Hourly) != 1 || got.Hourly[0].MaxEntries != 9 {
t.Errorf("hourly %+v, want one hour with maxEntries 9", got.Hourly)
}
}