Make the home page's AS name search work (closes #151)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
The AS Name Search card on the home page said the search was coming soon.
It now asks the new GET /api/v1/search/as?q=<text> and lists the matches
as links to their AS pages. asinfo.Search ignores case, returns the first
matches up to a limit in AS number order, and counts all matches. An
empty q answers 400.
When the database has no record of an AS, its page and /api/v1/as/{asn}
take its handle and description from asinfo and still list its prefixes.
They answer 404 only for an AS with no record, no asinfo entry and no
prefix. AS 0 means no known origin: it always answers 404, and the search
never lists it.
Model: opus-5-5
This commit is contained in:
@@ -20,6 +20,7 @@ import (
|
||||
|
||||
"git.eeqj.de/sneak/routewatch/internal/config"
|
||||
"git.eeqj.de/sneak/routewatch/internal/logger"
|
||||
"git.eeqj.de/sneak/routewatch/pkg/asinfo"
|
||||
"github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
@@ -1699,6 +1700,86 @@ func TestGetASDetailsListsEachPrefixOnce(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetASDetailsOfASNotInDatabase asks for ASes that the database has no
|
||||
// record of, some with stored routes. Each gets the prefixes of its routes and
|
||||
// no seen times. One that the AS info lookup knows gets its handle and
|
||||
// description from it. An AS with no record, no lookup entry and no route is
|
||||
// ErrNoRoute. Before the fix an AS with no record got no prefixes, although
|
||||
// its routes were stored.
|
||||
func TestGetASDetailsOfASNotInDatabase(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, err := New(&config.Config{StateDir: t.TempDir()}, logger.New())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create database: %v", err)
|
||||
}
|
||||
defer func() { _ = db.Close() }()
|
||||
|
||||
ts := time.Date(2026, 1, 2, 3, 4, 5, 0, time.UTC)
|
||||
|
||||
tests := []struct {
|
||||
asn int
|
||||
listed bool // in the AS info lookup
|
||||
prefixes []string // of the routes stored for the AS
|
||||
}{
|
||||
{15169, true, []string{"192.0.2.0/24"}},
|
||||
{13335, true, nil},
|
||||
{4200000000, false, []string{"198.51.100.0/24"}},
|
||||
}
|
||||
|
||||
var routes []*LiveRoute
|
||||
|
||||
for _, tt := range tests {
|
||||
if _, ok := asinfo.Get(tt.asn); ok != tt.listed {
|
||||
t.Fatalf("AS%d in the AS info data: %t, want %t", tt.asn, ok, tt.listed)
|
||||
}
|
||||
|
||||
for _, prefix := range tt.prefixes {
|
||||
routes = append(routes, mkV4Route(t, prefix, tt.asn, ts))
|
||||
}
|
||||
}
|
||||
|
||||
err = db.UpsertLiveRouteBatch(routes)
|
||||
if err != nil {
|
||||
t.Fatalf("UpsertLiveRouteBatch: %v", err)
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
got, prefixes, err := db.GetASDetails(tt.asn)
|
||||
if err != nil {
|
||||
t.Errorf("GetASDetails(%d): %v", tt.asn, err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
want := ASN{ASN: tt.asn}
|
||||
if info, ok := asinfo.Get(tt.asn); ok {
|
||||
want.Handle, want.Description = info.Handle, info.Description
|
||||
}
|
||||
|
||||
var gotPrefixes []string
|
||||
for _, route := range prefixes {
|
||||
gotPrefixes = append(gotPrefixes, route.Prefix)
|
||||
}
|
||||
|
||||
if *got != want || !slices.Equal(gotPrefixes, tt.prefixes) {
|
||||
t.Errorf("GetASDetails(%d) = %+v with prefixes %v, want %+v with %v",
|
||||
tt.asn, *got, gotPrefixes, want, tt.prefixes)
|
||||
}
|
||||
}
|
||||
|
||||
const absentASN = 4200000001 // no record, no lookup entry and no route
|
||||
|
||||
if _, ok := asinfo.Get(absentASN); ok {
|
||||
t.Fatalf("AS%d is in the AS info data", absentASN)
|
||||
}
|
||||
|
||||
_, _, err = db.GetASDetails(absentASN)
|
||||
if !errors.Is(err, ErrNoRoute) {
|
||||
t.Errorf("GetASDetails(%d): error %v, want %v", absentASN, err, ErrNoRoute)
|
||||
}
|
||||
}
|
||||
|
||||
// TestASPrefixQueriesUseCoveringIndexes checks the plan of each query that
|
||||
// lists the prefixes an AS originates: a search of a covering index and no
|
||||
// temporary B-tree, so the list reads one index entry per prefix, not one
|
||||
|
||||
Reference in New Issue
Block a user