Files
routewatch/internal/server/prefix_originchanges_internal_test.go
T
sneak 22783eb425
check / check (push) Failing after 15m27s
Fix the second review of the origin changes
Stop now counts the origin changes inside a running write, in a new
writingOriginChanges field, and adds them to the count it logs. The prune test
checks which rows survive, including a prefix with rows on both sides of 72
hours. The prefix page tests cover 5,000 and 5,001 rows and a row's time and
peer count. New tests cover the dropped-rows warning and the dropped count's
reset. The prune floor is named minOriginChangesPerPrefix. The README, TODO,
package doc and doc comments are corrected to match.

Model: deepseek-v4-pro
2026-11-05 05:08:27 +00:00

220 lines
6.3 KiB
Go

package server
import (
"net/http"
"net/http/httptest"
"net/netip"
"strings"
"testing"
"git.eeqj.de/sneak/routewatch/internal/config"
"git.eeqj.de/sneak/routewatch/internal/database"
"git.eeqj.de/sneak/routewatch/internal/logger"
"git.eeqj.de/sneak/routewatch/internal/metrics"
"git.eeqj.de/sneak/routewatch/internal/ristypes"
"git.eeqj.de/sneak/routewatch/internal/routetable"
"git.eeqj.de/sneak/routewatch/internal/streamer"
)
// newPrefixDetailServer returns a server whose routing table holds testPrefixV4
// with origin AS 64500, and whose database holds the given origin changes.
func newPrefixDetailServer(
t *testing.T, changes []routetable.OriginChange,
) *Server {
t.Helper()
db, err := database.New(&config.Config{StateDir: t.TempDir()}, logger.New())
if err != nil {
t.Fatalf("database.New: %v", err)
}
t.Cleanup(func() { _ = db.Close() })
table := emptyTable()
announce(table, testPrefixV4, 64500, testPeer(1), testRouteTime(0))
err = db.WriteOriginChanges(changes)
if err != nil {
t.Fatalf("WriteOriginChanges: %v", err)
}
return New(db, table, streamer.New(logger.New(), metrics.New()),
logger.New(), &config.Config{})
}
// TestPrefixDetailShowsOriginChanges checks that the prefix page shows the
// origin changes newest first, with the announced and withdrawn rows and the
// AS link.
func TestPrefixDetailShowsOriginChanges(t *testing.T) {
t.Parallel()
prefix := netip.MustParsePrefix(testPrefixV4)
s := newPrefixDetailServer(t, []routetable.OriginChange{
{Prefix: prefix, OriginASN: 64500, Announced: true, PeerCount: 1,
Time: testRouteTime(1)},
{Prefix: prefix, OriginASN: 64500, Announced: false, PeerCount: 0,
Time: testRouteTime(2)},
})
rec := httptest.NewRecorder()
s.router.ServeHTTP(rec, httptest.NewRequestWithContext(
t.Context(), http.MethodGet, "/prefix/"+testPrefixV4, nil))
if rec.Code != http.StatusOK {
t.Fatalf("status %d, want %d; body %s", rec.Code, http.StatusOK, rec.Body)
}
page := rec.Body.String()
withdrawn := strings.Index(page, "Withdrawn")
announced := strings.Index(page, "Announced")
if withdrawn < 0 || announced < 0 {
t.Fatalf("page does not show the announced and withdrawn rows")
}
// The withdrawn row is newer, so it comes first.
if withdrawn > announced {
t.Errorf("the withdrawn row is not shown before the announced row")
}
if !strings.Contains(page, `href="/as/64500"`) {
t.Errorf("page does not link to AS64500")
}
}
// TestPrefixDetailShowsOriginZeroAsNoKnownOrigin checks that the origin
// changes table shows AS 0 as no known origin, without a link to AS0.
func TestPrefixDetailShowsOriginZeroAsNoKnownOrigin(t *testing.T) {
t.Parallel()
prefix := netip.MustParsePrefix(testPrefixV4)
s := newPrefixDetailServer(t, []routetable.OriginChange{
{Prefix: prefix, OriginASN: ristypes.NoOrigin, Announced: true,
PeerCount: 1, Time: testRouteTime(1)},
})
rec := httptest.NewRecorder()
s.router.ServeHTTP(rec, httptest.NewRequestWithContext(
t.Context(), http.MethodGet, "/prefix/"+testPrefixV4, nil))
if rec.Code != http.StatusOK {
t.Fatalf("status %d, want %d; body %s", rec.Code, http.StatusOK, rec.Body)
}
page := rec.Body.String()
if !strings.Contains(page, "No known origin") {
t.Errorf("page does not say the origin change has no known origin")
}
if strings.Contains(page, `href="/as/0"`) {
t.Errorf("page links to AS0")
}
}
// TestPrefixDetailSaysWhenMoreThan5000Changes checks that the prefix page says
// so when a prefix has more than 5,000 origin changes.
func TestPrefixDetailSaysWhenMoreThan5000Changes(t *testing.T) {
t.Parallel()
prefix := netip.MustParsePrefix(testPrefixV4)
changes := make([]routetable.OriginChange, 0, 5001)
for i := range 5001 {
changes = append(changes, routetable.OriginChange{
Prefix: prefix, OriginASN: 64500, Announced: true, PeerCount: 1,
Time: testRouteTime(i),
})
}
s := newPrefixDetailServer(t, changes)
rec := httptest.NewRecorder()
s.router.ServeHTTP(rec, httptest.NewRequestWithContext(
t.Context(), http.MethodGet, "/prefix/"+testPrefixV4, nil))
if rec.Code != http.StatusOK {
t.Fatalf("status %d, want %d; body %s", rec.Code, http.StatusOK, rec.Body)
}
page := rec.Body.String()
if !strings.Contains(page, "more than 5000 changes") {
t.Errorf("page does not say there are more than 5000 changes")
}
if got := strings.Count(page, "<td>Announced</td>"); got != 5000 {
t.Errorf("page shows %d change rows, want 5000", got)
}
}
// TestPrefixDetailShowsNoNoteAtExactly5000Changes checks that a prefix with
// exactly 5,000 origin changes shows all 5,000 and no note.
func TestPrefixDetailShowsNoNoteAtExactly5000Changes(t *testing.T) {
t.Parallel()
prefix := netip.MustParsePrefix(testPrefixV4)
changes := make([]routetable.OriginChange, 0, 5000)
for i := range 5000 {
changes = append(changes, routetable.OriginChange{
Prefix: prefix, OriginASN: 64500, Announced: true, PeerCount: 1,
Time: testRouteTime(i),
})
}
s := newPrefixDetailServer(t, changes)
rec := httptest.NewRecorder()
s.router.ServeHTTP(rec, httptest.NewRequestWithContext(
t.Context(), http.MethodGet, "/prefix/"+testPrefixV4, nil))
if rec.Code != http.StatusOK {
t.Fatalf("status %d, want %d; body %s", rec.Code, http.StatusOK, rec.Body)
}
page := rec.Body.String()
if strings.Contains(page, "more than") {
t.Errorf("page says there are more changes, but a prefix has 5000")
}
if got := strings.Count(page, "<td>Announced</td>"); got != 5000 {
t.Errorf("page shows %d change rows, want 5000", got)
}
}
// TestPrefixDetailShowsOriginChangeTimeAndPeerCount checks that a row shows
// its time and its peer count.
func TestPrefixDetailShowsOriginChangeTimeAndPeerCount(t *testing.T) {
t.Parallel()
prefix := netip.MustParsePrefix(testPrefixV4)
s := newPrefixDetailServer(t, []routetable.OriginChange{
{Prefix: prefix, OriginASN: 64500, Announced: true, PeerCount: 7,
Time: testRouteTime(5)},
})
rec := httptest.NewRecorder()
s.router.ServeHTTP(rec, httptest.NewRequestWithContext(
t.Context(), http.MethodGet, "/prefix/"+testPrefixV4, nil))
if rec.Code != http.StatusOK {
t.Fatalf("status %d, want %d; body %s", rec.Code, http.StatusOK, rec.Body)
}
page := rec.Body.String()
if !strings.Contains(page, "2026-01-02 03:09:05") {
t.Errorf("page does not show the origin change's time")
}
if !strings.Contains(page, "<td>7</td>") {
t.Errorf("page does not show the origin change's peer count")
}
}