Files
routewatch/internal/server/prefix_originchanges_internal_test.go
T
sneak f455c25dd4
check / check (push) Successful in 2m33s
Reword the testUncarriedASN comment (review finding)
The comment on testUncarriedASN now says only what the identifier means.

Model: deepseek-v4-pro
2026-11-05 05:53:34 +00:00

231 lines
6.9 KiB
Go

package server
import (
"fmt"
"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"
)
// testUncarriedASN is an AS the test's routing table does not carry for the prefix.
const testUncarriedASN = 64501
// 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. The rows use an AS the routing table does not carry for the
// prefix, so the link comes from the origin changes table.
func TestPrefixDetailShowsOriginChanges(t *testing.T) {
t.Parallel()
prefix := netip.MustParsePrefix(testPrefixV4)
s := newPrefixDetailServer(t, []routetable.OriginChange{
{Prefix: prefix, OriginASN: testUncarriedASN, Announced: true,
PeerCount: 1, Time: testRouteTime(1)},
{Prefix: prefix, OriginASN: testUncarriedASN, 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/64501"`) {
t.Errorf("page does not link to AS64501")
}
}
// 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")
}
}
// TestPrefixDetailSaysWhenThereAreMoreChangesThanItShows checks that the
// prefix page says so when a prefix has more origin changes than the page's
// limit.
func TestPrefixDetailSaysWhenThereAreMoreChangesThanItShows(t *testing.T) {
t.Parallel()
prefix := netip.MustParsePrefix(testPrefixV4)
changes := make([]routetable.OriginChange, 0, maxOriginChangesOnPage+1)
for i := range maxOriginChangesOnPage + 1 {
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()
note := fmt.Sprintf("more than %d changes", maxOriginChangesOnPage)
if !strings.Contains(page, note) {
t.Errorf("page does not say there are more than %d changes",
maxOriginChangesOnPage)
}
if got := strings.Count(page, "<td>Announced</td>"); got != maxOriginChangesOnPage {
t.Errorf("page shows %d change rows, want %d", got,
maxOriginChangesOnPage)
}
}
// TestPrefixDetailShowsNoNoteAtTheLimit checks that a prefix with exactly the
// page's limit of origin changes shows all of them and no note.
func TestPrefixDetailShowsNoNoteAtTheLimit(t *testing.T) {
t.Parallel()
prefix := netip.MustParsePrefix(testPrefixV4)
changes := make([]routetable.OriginChange, 0, maxOriginChangesOnPage)
for i := range maxOriginChangesOnPage {
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 %d",
maxOriginChangesOnPage)
}
if got := strings.Count(page, "<td>Announced</td>"); got != maxOriginChangesOnPage {
t.Errorf("page shows %d change rows, want %d", got,
maxOriginChangesOnPage)
}
}
// 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")
}
}