Files
MrAlders0n 7d759d0a0f perf: known_routes retention, digest identity, and a working batched reconfirm (#98)
* fix: cascade channel_messages on packet delete

* rebuild known_routes keyed on (iata, path_key)

* add route retention delete, batch reconfirm, path_key upsert

* compute route path_key in store, add DeleteOldRoutes

* add routes retention config

* wire route retention into cleanup, batch reconfirm

* prune routes in reconfirm task, not cleanup

* expand routes retention docs in example config
2026-08-12 09:30:03 -04:00

115 lines
3.4 KiB
Go

// Copyright 2026 Beacon Contributors
// SPDX-License-Identifier: AGPL-3.0-or-later
package db
import (
"bytes"
"context"
"encoding/hex"
"testing"
"time"
sqlc "github.com/MeshCore-Beacon/beacon-server/db/sqlc"
mockdb "github.com/MeshCore-Beacon/beacon-server/db/sqlc/mock"
"github.com/MeshCore-Beacon/beacon-server/internal/api"
"github.com/google/uuid"
"go.uber.org/mock/gomock"
)
func TestRoutePathKey_MatchesPostgresDigest(t *testing.T) {
// Golden vector: Postgres computes
// decode(md5(array_to_string(node_ids, ',')), 'hex')
// over lowercase-hyphenated UUIDs. Migration 024 backfilled with that
// expression; this pins the Go side to the identical bytes.
a := uuid.MustParse("00000000-0000-0000-0000-000000000001")
b := uuid.MustParse("00000000-0000-0000-0000-000000000002")
got := hex.EncodeToString(routePathKey([]uuid.UUID{a, b}))
want := "f097439148601d9f3291c474f82fa64c"
if got != want {
t.Errorf("routePathKey = %s, want %s", got, want)
}
}
func TestUpsertKnownRoute_ComputesPathKey(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mock := mockdb.NewMockQuerier(ctrl)
store := &Store{q: mock}
a := uuid.MustParse("00000000-0000-0000-0000-000000000001")
b := uuid.MustParse("00000000-0000-0000-0000-000000000002")
wantKey, _ := hex.DecodeString("f097439148601d9f3291c474f82fa64c")
mock.EXPECT().UpsertKnownRoute(gomock.Any(), gomock.Cond(func(p sqlc.UpsertKnownRouteParams) bool {
return bytes.Equal(p.PathKey, wantKey)
})).Return(nil)
if err := store.UpsertKnownRoute(context.Background(), []uuid.UUID{a, b}, [][]byte{{0x37}, {0xd8}}, "PRG", 2); err != nil {
t.Fatal(err)
}
}
func TestDeleteOldRoutes_PassesCutoffs(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mock := mockdb.NewMockQuerier(ctrl)
store := &Store{q: mock}
retention := time.Date(2026, 7, 23, 0, 0, 0, 0, time.UTC)
grace := time.Date(2026, 7, 30, 0, 0, 0, 0, time.UTC)
mock.EXPECT().DeleteOldRoutes(gomock.Any(), gomock.Cond(func(p sqlc.DeleteOldRoutesParams) bool {
return p.LastSeen.Time.Equal(retention) && p.ObservationCount == 3 && p.LastSeen_2.Time.Equal(grace)
})).Return(nil)
if err := store.DeleteOldRoutes(context.Background(), retention, 3, grace); err != nil {
t.Fatal(err)
}
}
func TestExtractFromNode_Found(t *testing.T) {
a, b, c := uuid.New(), uuid.New(), uuid.New()
hops := []api.RouteHop{
{NodeID: a},
{NodeID: b},
{NodeID: c},
}
result := extractFromNode(hops, b)
if len(result) != 2 {
t.Fatalf("expected 2 hops, got %d", len(result))
}
if result[0].NodeID != b {
t.Errorf("expected first hop to be b, got %s", result[0].NodeID)
}
if result[1].NodeID != c {
t.Errorf("expected second hop to be c, got %s", result[1].NodeID)
}
}
func TestExtractFromNode_FirstNode(t *testing.T) {
a, b := uuid.New(), uuid.New()
hops := []api.RouteHop{{NodeID: a}, {NodeID: b}}
result := extractFromNode(hops, a)
if len(result) != 2 {
t.Fatalf("expected 2 hops, got %d", len(result))
}
}
func TestExtractFromNode_NotFound(t *testing.T) {
a, b := uuid.New(), uuid.New()
hops := []api.RouteHop{{NodeID: a}}
result := extractFromNode(hops, b)
// not found returns full slice
if len(result) != 1 {
t.Fatalf("expected full slice returned, got %d hops", len(result))
}
}
func TestExtractFromNode_Empty(t *testing.T) {
result := extractFromNode(nil, uuid.New())
if len(result) != 0 {
t.Errorf("expected empty result for nil hops")
}
}