feat: add lookup functionality for IP/domain verification and enhance dashboard links
Introduced a new lookup feature allowing users to quickly verify IP addresses or domains against community lists. Updated the DashboardQuickLinks component to include a new action for IP/domain checks, enhancing user navigation. Expanded API documentation to include the new lookup endpoint and its response structure, ensuring comprehensive coverage of the feature. Updated UI design documentation to reflect the integration of the lookup functionality.
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
package lookup
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"evobgp/internal/store"
|
||||
)
|
||||
|
||||
func TestLookupIPEntryAndSnapshot(t *testing.T) {
|
||||
m := store.NewMemory()
|
||||
m.SeedDemo()
|
||||
tenant, _, modIP, _, _ := m.DemoIDs()
|
||||
|
||||
cid := ""
|
||||
comms, err := m.ListCommunities(tenant)
|
||||
if err != nil || len(comms) == 0 {
|
||||
t.Fatal("expected demo community")
|
||||
}
|
||||
cid = comms[0].ID
|
||||
|
||||
def := cid
|
||||
if _, err := m.UpdateModule(tenant, modIP, &store.ModulePatch{DefaultCommunityID: &def}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
entryComm := cid
|
||||
e, err := m.CreateIPRangeEntry(tenant, modIP, &store.IPRangeEntry{
|
||||
Prefix: "203.0.113.0/24",
|
||||
CommunityID: &entryComm,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := m.SetModulePrefixSnapshot(tenant, modIP, "hash1", []store.PrefixRow{
|
||||
{Prefix: "203.0.113.0/24", CommunityID: &cid, Source: "ip_range"},
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
res, err := Lookup(m, tenant, "203.0.113.10")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if res.QueryKind != KindIP || res.Normalized != "203.0.113.10" {
|
||||
t.Fatalf("kind/normalized: %+v", res)
|
||||
}
|
||||
if !res.Matched || res.MatchCount < 2 {
|
||||
t.Fatalf("expected entry+snapshot matches, got %+v", res)
|
||||
}
|
||||
|
||||
var entryHit, snapHit bool
|
||||
for _, hit := range res.Matches {
|
||||
if hit.Layer == LayerEntry && hit.EntryID == e.ID {
|
||||
entryHit = true
|
||||
if hit.Community != "demo-comm" || hit.CommunityTitle != "Demo" {
|
||||
t.Fatalf("entry community: %+v", hit)
|
||||
}
|
||||
}
|
||||
if hit.Layer == LayerSnapshot && hit.MatchedValue == "203.0.113.0/24" {
|
||||
snapHit = true
|
||||
}
|
||||
}
|
||||
if !entryHit || !snapHit {
|
||||
t.Fatalf("missing layers entry=%v snap=%v matches=%+v", entryHit, snapHit, res.Matches)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookupIPCommunityFallback(t *testing.T) {
|
||||
m := store.NewMemory()
|
||||
m.SeedDemo()
|
||||
tenant, _, modIP, _, _ := m.DemoIDs()
|
||||
comms, _ := m.ListCommunities(tenant)
|
||||
cid := comms[0].ID
|
||||
if _, err := m.UpdateModule(tenant, modIP, &store.ModulePatch{DefaultCommunityID: &cid}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := m.CreateIPRangeEntry(tenant, modIP, &store.IPRangeEntry{Prefix: "10.0.0.0/8"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
res, err := Lookup(m, tenant, "10.1.2.3")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !res.Matched {
|
||||
t.Fatal("expected match")
|
||||
}
|
||||
found := false
|
||||
for _, hit := range res.Matches {
|
||||
if hit.Layer == LayerEntry {
|
||||
found = true
|
||||
if hit.CommunityID == nil || *hit.CommunityID != cid {
|
||||
t.Fatalf("expected default community, got %+v", hit)
|
||||
}
|
||||
if hit.Community != "demo-comm" {
|
||||
t.Fatalf("community value: %+v", hit)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatal("no entry match")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookupDomainEntryAndSnapshot(t *testing.T) {
|
||||
m := store.NewMemory()
|
||||
m.SeedDemo()
|
||||
tenant, _, _, _, _ := m.DemoIDs()
|
||||
|
||||
mod, err := m.CreateModule(tenant, &store.Module{
|
||||
Type: "DOMAINS",
|
||||
Name: "demo-domains",
|
||||
Enabled: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
comms, _ := m.ListCommunities(tenant)
|
||||
cid := comms[0].ID
|
||||
|
||||
e, err := m.CreateDomainEntry(tenant, mod.ID, &store.DomainEntry{
|
||||
FQDN: "Example.COM.",
|
||||
CommunityID: &cid,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := m.SetModulePrefixSnapshot(tenant, mod.ID, "hash-d", []store.PrefixRow{
|
||||
{Prefix: "198.51.100.1/32", CommunityID: &cid, Source: "domain"},
|
||||
{Prefix: "203.0.113.9/32", CommunityID: &cid, Source: "other"},
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
res, err := Lookup(m, tenant, "example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if res.QueryKind != KindDomain || res.Normalized != "example.com" {
|
||||
t.Fatalf("kind/normalized: %+v", res)
|
||||
}
|
||||
if !res.Matched {
|
||||
t.Fatal("expected match")
|
||||
}
|
||||
|
||||
var entryHit, snapHit, otherSnap bool
|
||||
for _, hit := range res.Matches {
|
||||
if hit.Layer == LayerEntry && hit.EntryID == e.ID {
|
||||
entryHit = true
|
||||
}
|
||||
if hit.Layer == LayerSnapshot && hit.MatchedValue == "198.51.100.1/32" {
|
||||
snapHit = true
|
||||
}
|
||||
if hit.MatchedValue == "203.0.113.9/32" {
|
||||
otherSnap = true
|
||||
}
|
||||
}
|
||||
if !entryHit || !snapHit {
|
||||
t.Fatalf("entry=%v snap=%v matches=%+v", entryHit, snapHit, res.Matches)
|
||||
}
|
||||
if otherSnap {
|
||||
t.Fatal("non-domain snapshot source should be excluded")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookupNoMatch(t *testing.T) {
|
||||
m := store.NewMemory()
|
||||
m.SeedDemo()
|
||||
tenant, _, _, _, _ := m.DemoIDs()
|
||||
|
||||
res, err := Lookup(m, tenant, "192.0.2.1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if res.Matched || res.MatchCount != 0 || len(res.Matches) != 0 {
|
||||
t.Fatalf("expected empty: %+v", res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookupInvalid(t *testing.T) {
|
||||
m := store.NewMemory()
|
||||
m.SeedDemo()
|
||||
tenant, _, _, _, _ := m.DemoIDs()
|
||||
|
||||
_, err := Lookup(m, tenant, "")
|
||||
if !errors.Is(err, store.ErrInvalidInput) {
|
||||
t.Fatalf("empty: %v", err)
|
||||
}
|
||||
_, err = Lookup(m, tenant, "not a host")
|
||||
if !errors.Is(err, store.ErrInvalidInput) {
|
||||
t.Fatalf("spaces: %v", err)
|
||||
}
|
||||
_, err = Lookup(m, tenant, "localhost")
|
||||
if !errors.Is(err, store.ErrInvalidInput) {
|
||||
t.Fatalf("single label: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeFQDN(t *testing.T) {
|
||||
got, ok := normalizeFQDN(" Example.COM. ")
|
||||
if !ok || got != "example.com" {
|
||||
t.Fatalf("got %q ok=%v", got, ok)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user