Record origin changes and show them on the prefix page (closes #161) #231
No files matched your search
@@ -145,7 +145,7 @@ and origin AS can have two kept routes, one or none.
|
|||||||
prefix's traffic to. RouteWatch records the peer itself as the next hop. The
|
prefix's traffic to. RouteWatch records the peer itself as the next hop. The
|
||||||
page also lists the prefix's origin changes, newest first. An origin change is
|
page also lists the prefix's origin changes, newest first. An origin change is
|
||||||
an AS starting to announce the prefix, when its first peer carries it, or
|
an AS starting to announce the prefix, when its first peer carries it, or
|
||||||
stopping, when its last peer no longer does. It shows at most the newest
|
stopping, when its last peer no longer does. The page shows at most the newest
|
||||||
5,000. Each row shows:
|
5,000. Each row shows:
|
||||||
- the time
|
- the time
|
||||||
- announced or withdrawn
|
- announced or withdrawn
|
||||||
@@ -577,6 +577,9 @@ What happens at each limit:
|
|||||||
and after the held-back messages of every peer whose session went down before
|
and after the held-back messages of every peer whose session went down before
|
||||||
it. It holds at most 150,000 such messages, about 150 MiB. At that number it
|
it. It holds at most 150,000 such messages, about 150 MiB. At that number it
|
||||||
takes nothing more off its queue until it has applied one of them.
|
takes nothing more off its queue until it has applied one of them.
|
||||||
|
- Waiting origin changes: the routing table keeps at most 100,000 origin changes
|
||||||
|
waiting to be written. Past that, it drops the oldest, and the prefix handler
|
||||||
|
logs how many. The prefix page never shows a dropped origin change.
|
||||||
|
|
||||||
To replace the image's Go soft limit, set `GOMEMLIMIT` in the environment, for
|
To replace the image's Go soft limit, set `GOMEMLIMIT` in the environment, for
|
||||||
example `-e GOMEMLIMIT=3GiB`. A limit close to the routing table's size makes
|
example `-e GOMEMLIMIT=3GiB`. A limit close to the routing table's size makes
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ func TestOriginChangesPrune(t *testing.T) {
|
|||||||
db := newRoutesTestDatabase(t)
|
db := newRoutesTestDatabase(t)
|
||||||
oldPrefix := netip.MustParsePrefix(tablePrefixV4)
|
oldPrefix := netip.MustParsePrefix(tablePrefixV4)
|
||||||
straddling := netip.MustParsePrefix(tablePrefixShortV4)
|
straddling := netip.MustParsePrefix(tablePrefixShortV4)
|
||||||
|
busy := netip.MustParsePrefix(tablePrefixBusyV4)
|
||||||
other := netip.MustParsePrefix("10.20.0.0/16")
|
other := netip.MustParsePrefix("10.20.0.0/16")
|
||||||
|
|
||||||
// Whole seconds, so the stored times round-trip exactly.
|
// Whole seconds, so the stored times round-trip exactly.
|
||||||
@@ -70,6 +71,12 @@ func TestOriginChangesPrune(t *testing.T) {
|
|||||||
// prune keeps all 200 newer and the newest 300 of the 400 older.
|
// prune keeps all 200 newer and the newest 300 of the 400 older.
|
||||||
changes = append(changes, rows(straddling, now.Add(-100*time.Hour), 400)...)
|
changes = append(changes, rows(straddling, now.Add(-100*time.Hour), 400)...)
|
||||||
changes = append(changes, rows(straddling, now.Add(-71*time.Hour), 200)...)
|
changes = append(changes, rows(straddling, now.Add(-71*time.Hour), 200)...)
|
||||||
|
// 100 rows older than 72 hours and minOriginChangesPerPrefix+100 newer
|
||||||
|
// rows for busy. The prune keeps all the newer rows and none of the older,
|
||||||
|
// because the newer rows alone are more than minOriginChangesPerPrefix.
|
||||||
|
changes = append(changes, rows(busy, now.Add(-100*time.Hour), 100)...)
|
||||||
|
changes = append(changes, rows(busy, now.Add(-71*time.Hour),
|
||||||
|
minOriginChangesPerPrefix+100)...)
|
||||||
// One row for other, which the prune leaves alone.
|
// One row for other, which the prune leaves alone.
|
||||||
changes = append(changes, rows(other, now.Add(-100*time.Hour), 1)...)
|
changes = append(changes, rows(other, now.Add(-100*time.Hour), 1)...)
|
||||||
|
|
||||||
@@ -83,44 +90,25 @@ func TestOriginChangesPrune(t *testing.T) {
|
|||||||
t.Fatalf("PruneOriginChanges: %v", err)
|
t.Fatalf("PruneOriginChanges: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
oldTimes := survivorTimes(t, db, oldPrefix, 1000)
|
|
||||||
if len(oldTimes) != 500 {
|
|
||||||
t.Errorf("GetOriginChanges(oldPrefix) = %d rows, want 500", len(oldTimes))
|
|
||||||
}
|
|
||||||
|
|
||||||
// The newest row survives, and the oldest survivor is the 100th oldest,
|
// The newest row survives, and the oldest survivor is the 100th oldest,
|
||||||
// so a prune that kept the oldest 500 instead would fail.
|
// so a prune that kept the oldest 500 instead would fail.
|
||||||
wantNewest := now.Add(-100 * time.Hour).Add(599 * time.Minute)
|
checkSurvivors(t, db, oldPrefix, 500,
|
||||||
if !oldTimes[0].Equal(wantNewest) {
|
now.Add(-100*time.Hour).Add(599*time.Minute),
|
||||||
t.Errorf("the newest surviving row's time = %v, want %v",
|
now.Add(-100*time.Hour).Add(100*time.Minute))
|
||||||
oldTimes[0], wantNewest)
|
|
||||||
}
|
|
||||||
|
|
||||||
wantOldest := now.Add(-100 * time.Hour).Add(100 * time.Minute)
|
|
||||||
if !oldTimes[499].Equal(wantOldest) {
|
|
||||||
t.Errorf("the oldest surviving row's time = %v, want %v",
|
|
||||||
oldTimes[499], wantOldest)
|
|
||||||
}
|
|
||||||
|
|
||||||
straddlingTimes := survivorTimes(t, db, straddling, 1000)
|
|
||||||
if len(straddlingTimes) != 500 {
|
|
||||||
t.Errorf("GetOriginChanges(straddling) = %d rows, want 500",
|
|
||||||
len(straddlingTimes))
|
|
||||||
}
|
|
||||||
|
|
||||||
// The newest of the newer-than-72-hours rows survives, and the oldest
|
// The newest of the newer-than-72-hours rows survives, and the oldest
|
||||||
// survivor is the 100th oldest of the older-than-72-hours rows.
|
// survivor is the 100th oldest of the older-than-72-hours rows.
|
||||||
wantNewest = now.Add(-71 * time.Hour).Add(199 * time.Minute)
|
checkSurvivors(t, db, straddling, 500,
|
||||||
if !straddlingTimes[0].Equal(wantNewest) {
|
now.Add(-71*time.Hour).Add(199*time.Minute),
|
||||||
t.Errorf("the newest surviving row's time = %v, want %v",
|
now.Add(-100*time.Hour).Add(100*time.Minute))
|
||||||
straddlingTimes[0], wantNewest)
|
|
||||||
}
|
|
||||||
|
|
||||||
wantOldest = now.Add(-100 * time.Hour).Add(100 * time.Minute)
|
// Every newer-than-72-hours row survives, so the oldest survivor is the
|
||||||
if !straddlingTimes[499].Equal(wantOldest) {
|
// first of them and the newest is minOriginChangesPerPrefix+99 minutes
|
||||||
t.Errorf("the oldest surviving row's time = %v, want %v",
|
// after it.
|
||||||
straddlingTimes[499], wantOldest)
|
checkSurvivors(t, db, busy, minOriginChangesPerPrefix+100,
|
||||||
}
|
now.Add(-71*time.Hour).
|
||||||
|
Add(time.Duration(minOriginChangesPerPrefix+99)*time.Minute),
|
||||||
|
now.Add(-71*time.Hour))
|
||||||
|
|
||||||
gotOther, err := db.GetOriginChanges(t.Context(), other, 10)
|
gotOther, err := db.GetOriginChanges(t.Context(), other, 10)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -167,6 +155,33 @@ func survivorTimes(
|
|||||||
return times
|
return times
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkSurvivors checks that prefix kept exactly wantCount rows, with the
|
||||||
|
// newest survivor at wantNewest and the oldest at wantOldest.
|
||||||
|
func checkSurvivors(
|
||||||
|
t *testing.T, db *Database, prefix netip.Prefix,
|
||||||
|
wantCount int, wantNewest, wantOldest time.Time,
|
||||||
|
) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
times := survivorTimes(t, db, prefix, wantCount+1)
|
||||||
|
if len(times) != wantCount {
|
||||||
|
t.Errorf("GetOriginChanges(%s) = %d rows, want %d",
|
||||||
|
prefix, len(times), wantCount)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !times[0].Equal(wantNewest) {
|
||||||
|
t.Errorf("the newest surviving row's time = %v, want %v",
|
||||||
|
times[0], wantNewest)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !times[wantCount-1].Equal(wantOldest) {
|
||||||
|
t.Errorf("the oldest surviving row's time = %v, want %v",
|
||||||
|
times[wantCount-1], wantOldest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestOriginChangesSurviveRouteDeletion checks that deleting a routes row
|
// TestOriginChangesSurviveRouteDeletion checks that deleting a routes row
|
||||||
// leaves the prefix's origin_changes rows.
|
// leaves the prefix's origin_changes rows.
|
||||||
func TestOriginChangesSurviveRouteDeletion(t *testing.T) {
|
func TestOriginChangesSurviveRouteDeletion(t *testing.T) {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ const (
|
|||||||
|
|
||||||
tablePrefixV4 = "10.1.2.0/24"
|
tablePrefixV4 = "10.1.2.0/24"
|
||||||
tablePrefixShortV4 = "10.9.0.0/16"
|
tablePrefixShortV4 = "10.9.0.0/16"
|
||||||
|
tablePrefixBusyV4 = "10.10.0.0/16"
|
||||||
tablePrefixV6 = "2001:db8:1::/48"
|
tablePrefixV6 = "2001:db8:1::/48"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// maxWaitingOriginChanges is the most origin changes the table keeps waiting
|
// MaxWaitingOriginChanges is the most origin changes the table keeps waiting
|
||||||
// for the prefix handler to write. The oldest change past it is dropped and
|
// for the prefix handler to write. The oldest change past it is dropped and
|
||||||
// counted.
|
// counted.
|
||||||
const maxWaitingOriginChanges = 100000
|
const MaxWaitingOriginChanges = 100000
|
||||||
|
|
||||||
// OriginChange is one change of a prefix and origin AS pair: the origin AS
|
// OriginChange is one change of a prefix and origin AS pair: the origin AS
|
||||||
// gained its first peer, or lost its last.
|
// gained its first peer, or lost its last.
|
||||||
@@ -76,7 +76,7 @@ func (t *Table) PutBackOriginChanges(changes []OriginChange) {
|
|||||||
// trimOriginChanges drops the oldest origin changes past the cap and counts
|
// trimOriginChanges drops the oldest origin changes past the cap and counts
|
||||||
// them. The caller holds the lock.
|
// them. The caller holds the lock.
|
||||||
func (t *Table) trimOriginChanges() {
|
func (t *Table) trimOriginChanges() {
|
||||||
if extra := len(t.originChanges) - maxWaitingOriginChanges; extra > 0 {
|
if extra := len(t.originChanges) - MaxWaitingOriginChanges; extra > 0 {
|
||||||
t.originChangesDropped += extra
|
t.originChangesDropped += extra
|
||||||
t.originChanges = t.originChanges[extra:]
|
t.originChanges = t.originChanges[extra:]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -184,25 +184,24 @@ func TestOriginChangesPutBackPutsRowsInFront(t *testing.T) {
|
|||||||
func TestOriginChangesDropPastTheLimit(t *testing.T) {
|
func TestOriginChangesDropPastTheLimit(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const limit = 100000
|
|
||||||
|
|
||||||
table := routetable.New(routetable.Params{})
|
table := routetable.New(routetable.Params{})
|
||||||
addr := netip.MustParseAddr("10.0.0.0")
|
addr := netip.MustParseAddr("10.0.0.0")
|
||||||
|
|
||||||
for range limit + 1 {
|
for range routetable.MaxWaitingOriginChanges + 1 {
|
||||||
prefix := netip.PrefixFrom(addr, 32)
|
prefix := netip.PrefixFrom(addr, 32)
|
||||||
table.Announce(prefix, asA, routeOf(peerA, asA, 10))
|
table.Announce(prefix, asA, routeOf(peerA, asA, 10))
|
||||||
|
|
||||||
addr = addr.Next()
|
addr = addr.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
changes, dropped := table.TakeOriginChanges(limit)
|
changes, dropped := table.TakeOriginChanges(routetable.MaxWaitingOriginChanges)
|
||||||
if dropped != 1 {
|
if dropped != 1 {
|
||||||
t.Errorf("dropped = %d, want 1", dropped)
|
t.Errorf("dropped = %d, want 1", dropped)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(changes) != limit {
|
if len(changes) != routetable.MaxWaitingOriginChanges {
|
||||||
t.Errorf("len(changes) = %d, want %d", len(changes), limit)
|
t.Errorf("len(changes) = %d, want %d", len(changes),
|
||||||
|
routetable.MaxWaitingOriginChanges)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,17 +211,15 @@ func TestOriginChangesDropPastTheLimit(t *testing.T) {
|
|||||||
func TestOriginChangesTakeResetsTheDroppedCount(t *testing.T) {
|
func TestOriginChangesTakeResetsTheDroppedCount(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const limit = 100000
|
|
||||||
|
|
||||||
table := routetable.New(routetable.Params{})
|
table := routetable.New(routetable.Params{})
|
||||||
addr := netip.MustParseAddr("10.0.0.0")
|
addr := netip.MustParseAddr("10.0.0.0")
|
||||||
|
|
||||||
for range limit + 1 {
|
for range routetable.MaxWaitingOriginChanges + 1 {
|
||||||
table.Announce(netip.PrefixFrom(addr, 32), asA, routeOf(peerA, asA, 10))
|
table.Announce(netip.PrefixFrom(addr, 32), asA, routeOf(peerA, asA, 10))
|
||||||
addr = addr.Next()
|
addr = addr.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
_, dropped := table.TakeOriginChanges(limit)
|
_, dropped := table.TakeOriginChanges(routetable.MaxWaitingOriginChanges)
|
||||||
if dropped != 1 {
|
if dropped != 1 {
|
||||||
t.Fatalf("dropped = %d, want 1", dropped)
|
t.Fatalf("dropped = %d, want 1", dropped)
|
||||||
}
|
}
|
||||||
@@ -241,7 +238,6 @@ func TestOriginChangesPutBackTrimsToTheLimit(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const (
|
const (
|
||||||
limit = 100000
|
|
||||||
taken = 20000
|
taken = 20000
|
||||||
added = 20000
|
added = 20000
|
||||||
)
|
)
|
||||||
@@ -263,7 +259,7 @@ func TestOriginChangesPutBackTrimsToTheLimit(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
announce(limit)
|
announce(routetable.MaxWaitingOriginChanges)
|
||||||
|
|
||||||
takenChanges, _ := table.TakeOriginChanges(taken)
|
takenChanges, _ := table.TakeOriginChanges(taken)
|
||||||
|
|
||||||
@@ -276,8 +272,9 @@ func TestOriginChangesPutBackTrimsToTheLimit(t *testing.T) {
|
|||||||
t.Errorf("dropped = %d, want %d", dropped, added)
|
t.Errorf("dropped = %d, want %d", dropped, added)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(changes) != limit {
|
if len(changes) != routetable.MaxWaitingOriginChanges {
|
||||||
t.Errorf("len(changes) = %d, want %d", len(changes), limit)
|
t.Errorf("len(changes) = %d, want %d", len(changes),
|
||||||
|
routetable.MaxWaitingOriginChanges)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The put-back rows are the oldest, so they are the ones dropped. The
|
// The put-back rows are the oldest, so they are the ones dropped. The
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ import (
|
|||||||
"git.eeqj.de/sneak/routewatch/internal/routetable"
|
"git.eeqj.de/sneak/routewatch/internal/routetable"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// waitingOriginChangesLimit is the routing table's maxWaitingOriginChanges,
|
||||||
|
// which this package cannot read.
|
||||||
|
const waitingOriginChangesLimit = 100000
|
||||||
|
|
||||||
// TestPrefixHandlerWritesOriginChanges hands a running prefix handler an
|
// TestPrefixHandlerWritesOriginChanges hands a running prefix handler an
|
||||||
// announcement and flushes. The flush must write the origin change to the
|
// announcement and flushes. The flush must write the origin change to the
|
||||||
// database.
|
// database.
|
||||||
@@ -145,7 +149,7 @@ func TestPrefixHandlerLogsTheDroppedOriginChanges(t *testing.T) {
|
|||||||
&slog.HandlerOptions{Level: slog.LevelWarn})),
|
&slog.HandlerOptions{Level: slog.LevelWarn})),
|
||||||
}
|
}
|
||||||
|
|
||||||
announceManyPrefixes(h.table, 100001)
|
announceManyPrefixes(h.table, waitingOriginChangesLimit+1)
|
||||||
|
|
||||||
h.flushBatch()
|
h.flushBatch()
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
@@ -16,6 +17,11 @@ import (
|
|||||||
"git.eeqj.de/sneak/routewatch/internal/streamer"
|
"git.eeqj.de/sneak/routewatch/internal/streamer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// testUncarriedASN is an origin AS the prefix page test's routing table does
|
||||||
|
// not carry for the prefix, so the AS link the page shows comes from the
|
||||||
|
// origin changes table.
|
||||||
|
const testUncarriedASN = 64501
|
||||||
|
|
||||||
// newPrefixDetailServer returns a server whose routing table holds testPrefixV4
|
// newPrefixDetailServer returns a server whose routing table holds testPrefixV4
|
||||||
// with origin AS 64500, and whose database holds the given origin changes.
|
// with origin AS 64500, and whose database holds the given origin changes.
|
||||||
func newPrefixDetailServer(
|
func newPrefixDetailServer(
|
||||||
@@ -44,17 +50,18 @@ func newPrefixDetailServer(
|
|||||||
|
|
||||||
// TestPrefixDetailShowsOriginChanges checks that the prefix page shows the
|
// TestPrefixDetailShowsOriginChanges checks that the prefix page shows the
|
||||||
// origin changes newest first, with the announced and withdrawn rows and the
|
// origin changes newest first, with the announced and withdrawn rows and the
|
||||||
// AS link.
|
// 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) {
|
func TestPrefixDetailShowsOriginChanges(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
prefix := netip.MustParsePrefix(testPrefixV4)
|
prefix := netip.MustParsePrefix(testPrefixV4)
|
||||||
|
|
||||||
s := newPrefixDetailServer(t, []routetable.OriginChange{
|
s := newPrefixDetailServer(t, []routetable.OriginChange{
|
||||||
{Prefix: prefix, OriginASN: 64500, Announced: true, PeerCount: 1,
|
{Prefix: prefix, OriginASN: testUncarriedASN, Announced: true,
|
||||||
Time: testRouteTime(1)},
|
PeerCount: 1, Time: testRouteTime(1)},
|
||||||
{Prefix: prefix, OriginASN: 64500, Announced: false, PeerCount: 0,
|
{Prefix: prefix, OriginASN: testUncarriedASN, Announced: false,
|
||||||
Time: testRouteTime(2)},
|
PeerCount: 0, Time: testRouteTime(2)},
|
||||||
})
|
})
|
||||||
|
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
@@ -79,8 +86,8 @@ func TestPrefixDetailShowsOriginChanges(t *testing.T) {
|
|||||||
t.Errorf("the withdrawn row is not shown before the announced row")
|
t.Errorf("the withdrawn row is not shown before the announced row")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.Contains(page, `href="/as/64500"`) {
|
if !strings.Contains(page, `href="/as/64501"`) {
|
||||||
t.Errorf("page does not link to AS64500")
|
t.Errorf("page does not link to AS64501")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,15 +122,16 @@ func TestPrefixDetailShowsOriginZeroAsNoKnownOrigin(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestPrefixDetailSaysWhenMoreThan5000Changes checks that the prefix page says
|
// TestPrefixDetailSaysWhenThereAreMoreChangesThanItShows checks that the
|
||||||
// so when a prefix has more than 5,000 origin changes.
|
// prefix page says so when a prefix has more origin changes than the page's
|
||||||
func TestPrefixDetailSaysWhenMoreThan5000Changes(t *testing.T) {
|
// limit.
|
||||||
|
func TestPrefixDetailSaysWhenThereAreMoreChangesThanItShows(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
prefix := netip.MustParsePrefix(testPrefixV4)
|
prefix := netip.MustParsePrefix(testPrefixV4)
|
||||||
|
|
||||||
changes := make([]routetable.OriginChange, 0, 5001)
|
changes := make([]routetable.OriginChange, 0, maxOriginChangesOnPage+1)
|
||||||
for i := range 5001 {
|
for i := range maxOriginChangesOnPage + 1 {
|
||||||
changes = append(changes, routetable.OriginChange{
|
changes = append(changes, routetable.OriginChange{
|
||||||
Prefix: prefix, OriginASN: 64500, Announced: true, PeerCount: 1,
|
Prefix: prefix, OriginASN: 64500, Announced: true, PeerCount: 1,
|
||||||
Time: testRouteTime(i),
|
Time: testRouteTime(i),
|
||||||
@@ -142,24 +150,27 @@ func TestPrefixDetailSaysWhenMoreThan5000Changes(t *testing.T) {
|
|||||||
|
|
||||||
page := rec.Body.String()
|
page := rec.Body.String()
|
||||||
|
|
||||||
if !strings.Contains(page, "more than 5000 changes") {
|
note := fmt.Sprintf("more than %d changes", maxOriginChangesOnPage)
|
||||||
t.Errorf("page does not say there are more than 5000 changes")
|
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 != 5000 {
|
if got := strings.Count(page, "<td>Announced</td>"); got != maxOriginChangesOnPage {
|
||||||
t.Errorf("page shows %d change rows, want 5000", got)
|
t.Errorf("page shows %d change rows, want %d", got,
|
||||||
|
maxOriginChangesOnPage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestPrefixDetailShowsNoNoteAtExactly5000Changes checks that a prefix with
|
// TestPrefixDetailShowsNoNoteAtTheLimit checks that a prefix with exactly the
|
||||||
// exactly 5,000 origin changes shows all 5,000 and no note.
|
// page's limit of origin changes shows all of them and no note.
|
||||||
func TestPrefixDetailShowsNoNoteAtExactly5000Changes(t *testing.T) {
|
func TestPrefixDetailShowsNoNoteAtTheLimit(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
prefix := netip.MustParsePrefix(testPrefixV4)
|
prefix := netip.MustParsePrefix(testPrefixV4)
|
||||||
|
|
||||||
changes := make([]routetable.OriginChange, 0, 5000)
|
changes := make([]routetable.OriginChange, 0, maxOriginChangesOnPage)
|
||||||
for i := range 5000 {
|
for i := range maxOriginChangesOnPage {
|
||||||
changes = append(changes, routetable.OriginChange{
|
changes = append(changes, routetable.OriginChange{
|
||||||
Prefix: prefix, OriginASN: 64500, Announced: true, PeerCount: 1,
|
Prefix: prefix, OriginASN: 64500, Announced: true, PeerCount: 1,
|
||||||
Time: testRouteTime(i),
|
Time: testRouteTime(i),
|
||||||
@@ -179,11 +190,13 @@ func TestPrefixDetailShowsNoNoteAtExactly5000Changes(t *testing.T) {
|
|||||||
page := rec.Body.String()
|
page := rec.Body.String()
|
||||||
|
|
||||||
if strings.Contains(page, "more than") {
|
if strings.Contains(page, "more than") {
|
||||||
t.Errorf("page says there are more changes, but a prefix has 5000")
|
t.Errorf("page says there are more changes, but a prefix has %d",
|
||||||
|
maxOriginChangesOnPage)
|
||||||
}
|
}
|
||||||
|
|
||||||
if got := strings.Count(page, "<td>Announced</td>"); got != 5000 {
|
if got := strings.Count(page, "<td>Announced</td>"); got != maxOriginChangesOnPage {
|
||||||
t.Errorf("page shows %d change rows, want 5000", got)
|
t.Errorf("page shows %d change rows, want %d", got,
|
||||||
|
maxOriginChangesOnPage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user