check / check (push) Waiting to run
The routing table in memory now holds every route, so the two per-peer route tables go, with every write, delete, count and read that served them. The prefix handler applies each update to the table, writes the table's changes every second, and stores each new prefix with its first-seen time. The peer delete, its retry on the next flush and the updates held for it go too. The types and errors of the server's answers move to internal/server. The image's memory settings fit a full table of about 1 GB: GOMEMLIMIT of 2.5 GiB, a 512 MiB page cache for the writing connection, and SQLite heap limits of 768 MiB and 1 GiB. The README describes the new table, its database copy and the memory budget. Model: opus-5-5
69 lines
2.0 KiB
Go
69 lines
2.0 KiB
Go
package routewatch
|
|
|
|
import (
|
|
"maps"
|
|
"slices"
|
|
"testing"
|
|
|
|
"git.eeqj.de/sneak/routewatch/internal/logger"
|
|
)
|
|
|
|
// TestASHandlerStoresTheEarliestAndLatestTimesOfANewAS handles two updates
|
|
// whose path carries an AS the database does not hold yet, the second 5
|
|
// seconds after the first, in one batch into a real database. The AS is stored
|
|
// first seen at the time of the first update and last seen at the time of the
|
|
// second.
|
|
func TestASHandlerStoresTheEarliestAndLatestTimesOfANewAS(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
db := newTestDatabase(t)
|
|
|
|
// Built without NewASHandler's flush goroutine, so the test flushes itself.
|
|
h := &ASHandler{db: db, logger: logger.New()}
|
|
|
|
first := peerAnnouncement(testPeerIP, testV4Prefix)
|
|
second := peerAnnouncement(testPeerIP, testV4Prefix)
|
|
second.ParsedTimestamp = first.ParsedTimestamp.Add(secondUpdateAfter)
|
|
|
|
h.HandleMessage(first)
|
|
h.HandleMessage(second)
|
|
h.flushBatch()
|
|
|
|
asn, _, err := db.GetAS(t.Context(), testASNA)
|
|
if err != nil {
|
|
t.Fatalf("GetAS(%d): %v", testASNA, err)
|
|
}
|
|
|
|
if !asn.FirstSeen.Equal(first.ParsedTimestamp) {
|
|
t.Errorf("first_seen of AS%d = %s, want %s", testASNA,
|
|
asn.FirstSeen, first.ParsedTimestamp)
|
|
}
|
|
|
|
if !asn.LastSeen.Equal(second.ParsedTimestamp) {
|
|
t.Errorf("last_seen of AS%d = %s, want %s", testASNA,
|
|
asn.LastSeen, second.ParsedTimestamp)
|
|
}
|
|
}
|
|
|
|
// TestASHandlerRecordsNothingFromAPathWithASZero handles an update whose AS
|
|
// path holds AS 0 between two other ASes. RFC 7607 makes such a path
|
|
// malformed, so neither AS is recorded.
|
|
func TestASHandlerRecordsNothingFromAPathWithASZero(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
store := newMockStore()
|
|
|
|
// Built without NewASHandler's flush goroutine, so the test flushes itself.
|
|
h := &ASHandler{db: store, logger: logger.New()}
|
|
|
|
msg := peerAnnouncement(testPeerIP, testV4Prefix)
|
|
msg.Path = decodePath(t, testASNA, 0, testASNB)
|
|
h.HandleMessage(msg)
|
|
h.flushBatch()
|
|
|
|
if len(store.ASNs) > 0 {
|
|
t.Errorf("ASes recorded = %v, want none",
|
|
slices.Sorted(maps.Keys(store.ASNs)))
|
|
}
|
|
}
|