check / check (push) Successful in 2m33s
The comment on testUncarriedASN now says only what the identifier means. Model: deepseek-v4-pro
231 lines
6.9 KiB
Go
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")
|
|
}
|
|
}
|