Files
beacon-server/db/packets_schema_integration_test.go
T
MrAlders0n 41baee9197 Packets: one statement per filter shape, exact pageCursor on packets, nodes and observers (2.1.0) (#241)
* feat(db): packet type domains, row-cursor indexes and the inverted-heard index

* feat(api): exact page cursors for packets, nodes and observers

* feat(db): normalise packet list filters before choosing a statement

* feat(db): one packets statement per filter shape with an exact row cursor

* fix(db): let the window statements decide since after until

* feat(db): exact keyset cursors for nodes and observers; observers page by last_seen

* feat(api): pageCursor on packets, nodes and observers; it wins over cursor

* test(planguard): packets statements and node/observer keysets leave pending; diff and storediff cases

* chore: bump version to 2.1.0

* test(db): turn off JIT in the keyset cursor tests
2026-10-19 15:59:55 -04:00

124 lines
3.8 KiB
Go

// Copyright 2026 Beacon Contributors
// SPDX-License-Identifier: AGPL-3.0-or-later
package db
import (
"context"
"errors"
"strings"
"testing"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
)
// packetsEndStateMigrations are the packet type domains, row-cursor indexes and the inverted-heard statistics.
var packetsEndStateMigrations = []string{
"025_packets_payload_type_domain.sql",
"026_validate_packets_payload_type_domain.sql",
"027_packets_route_type_domain.sql",
"028_validate_packets_route_type_domain.sql",
"029_packets_last_heard_hash.sql",
"030_packets_type_last_heard.sql",
"031_packets_scope_last_heard.sql",
"032_packets_heard_inverted.sql",
"033_packets_heard_inverted_stat.sql",
}
// packetsEndStateIndexes are the indexes packetsEndStateMigrations build.
var packetsEndStateIndexes = []string{
"idx_packets_last_heard_hash",
"idx_packets_type_last_heard",
"idx_packets_scope_last_heard",
"idx_packets_heard_inverted",
}
// applyPacketsEndState applies packetsEndStateMigrations inside tx.
func applyPacketsEndState(t *testing.T, ctx context.Context, tx pgx.Tx) {
t.Helper()
applyMigrationsTx(t, ctx, tx, packetsEndStateMigrations...)
}
// The domain checks are validated and the four packet indexes and the statistics object exist and are usable.
func TestPacketsDomainChecksPostgres(t *testing.T) {
ctx, pool := schemaPool(t)
if err := RunMigrations(ctx, pool); err != nil {
t.Fatal(err)
}
want := map[string]string{
"packets_payload_type_domain": "CHECK (((payload_type >= 0) AND (payload_type <= 15)))",
"packets_route_type_domain": "CHECK (((route_type >= 0) AND (route_type <= 3)))",
}
rows, err := pool.Query(ctx, `SELECT conname::text, convalidated, pg_get_constraintdef(oid)
FROM pg_constraint WHERE conrelid = 'packets'::regclass AND contype = 'c'`)
if err != nil {
t.Fatal(err)
}
got := map[string]string{}
for rows.Next() {
var name, def string
var validated bool
if err := rows.Scan(&name, &validated, &def); err != nil {
t.Fatal(err)
}
if !validated {
t.Errorf("%s not validated", name)
}
got[name] = def
}
if err := rows.Err(); err != nil {
t.Fatal(err)
}
if len(got) != len(want) {
t.Fatalf("packets checks = %v, want %v", got, want)
}
for name, def := range want {
if got[name] != def {
t.Errorf("%s = %q, want %q", name, got[name], def)
}
}
for _, idx := range packetsEndStateIndexes {
var usable bool
if err := pool.QueryRow(ctx, `SELECT i.indisvalid AND i.indisready FROM pg_index i
WHERE i.indexrelid = to_regclass($1)`, idx).Scan(&usable); err != nil {
t.Fatalf("%s: %v", idx, err)
}
if !usable {
t.Errorf("%s not valid and ready", idx)
}
}
var def string
if err := pool.QueryRow(ctx, "SELECT pg_get_indexdef('idx_packets_heard_inverted'::regclass)").Scan(&def); err != nil {
t.Fatal(err)
}
if !strings.HasSuffix(def, "WHERE ((first_heard_at > last_heard_at) IS TRUE)") {
t.Errorf("idx_packets_heard_inverted = %q", def)
}
var stats int
if err := pool.QueryRow(ctx, "SELECT count(*) FROM pg_statistic_ext WHERE stxname = 'packets_heard_inverted_stat' AND stxrelid = 'packets'::regclass").Scan(&stats); err != nil {
t.Fatal(err)
}
if stats != 1 {
t.Errorf("packets_heard_inverted_stat rows = %d, want 1", stats)
}
for _, tc := range []struct {
name string
payloadType, routeType int
}{
{"payload_type 16", 16, 0},
{"route_type 4", 0, 4},
} {
_, err := pool.Exec(ctx, `INSERT INTO packets (packet_hash, payload_type, payload_version, route_type, raw_payload, raw_header, first_heard_at, last_heard_at)
VALUES ('\x01', $1, 0, $2, '\x', '\x', now(), now())`, tc.payloadType, tc.routeType)
var pgErr *pgconn.PgError
if !errors.As(err, &pgErr) || pgErr.Code != "23514" {
t.Errorf("%s: err = %v, want SQLSTATE 23514", tc.name, err)
}
}
}