package routewatch import ( "maps" "slices" "testing" "git.eeqj.de/sneak/routewatch/internal/database" "git.eeqj.de/sneak/routewatch/internal/logger" "git.eeqj.de/sneak/routewatch/internal/ristypes" ) // pathOfASes returns an AS path of n different ASes: AS 1 to AS n. With n up to // asnBatchSize, none of them is testASNA to testASND. func pathOfASes(n int) ristypes.ASPath { sequence := make([]int, n) for i := range sequence { sequence[i] = i + 1 } return ristypes.ASPath{Sequence: sequence} } // TestASHandlerStoresTheEarliestAndLatestTimesOfANewAS handles two updates // whose path carries an AS the database does not hold yet, in one batch into a // real database. The second is 5 seconds after the first, and the handler gets // it first. 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(), batch: make(map[int]database.SeenTimes), } first := peerAnnouncement(testPeerIP, testV4Prefix) second := peerAnnouncement(testPeerIP, testV4Prefix) second.ParsedTimestamp = first.ParsedTimestamp.Add(secondUpdateAfter) h.HandleMessage(second) h.HandleMessage(first) 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(), batch: make(map[int]database.SeenTimes), } 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))) } }