CI / changes (push) Successful in 6s
CI / openapi (push) Has been skipped
CI / go (push) Successful in 38s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, , evobgp-web) (push) Has been skipped
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, evobgp-all, evobgp-web-all) (push) Has been skipped
CI / docker-bird (push) Has been skipped
CI / bird2 (push) Successful in 15s
CI / docker-go-prime (push) Successful in 23s
CI / docker-go (deploy/docker/evobgp-agent/Dockerfile, , evobgp-agent) (push) Successful in 1m0s
CI / docker-go (evobgp-all, 1, deploy/docker/gobinary/Dockerfile, , evobgp-all) (push) Successful in 2m13s
CI / docker-go (evobgp-api, 1, deploy/docker/gobinary/Dockerfile, , evobgp-api) (push) Successful in 1m23s
CI / docker-go (evobgp-deploy, 0, deploy/docker/gobinary/Dockerfile, , evobgp-deploy) (push) Successful in 1m27s
CI / docker-go (evobgp-ingest, 0, deploy/docker/gobinary/Dockerfile, , evobgp-ingest) (push) Successful in 1m23s
CI / docker-go (evobgp-node, 0, deploy/docker/gobinary/Dockerfile, , evobgp-node) (push) Successful in 1m10s
CI / docker-go (evobgp-render, 0, deploy/docker/gobinary/Dockerfile, , evobgp-render) (push) Successful in 1m24s
CI / docker-go (evobgp-scheduler, 0, deploy/docker/gobinary/Dockerfile, , evobgp-scheduler) (push) Successful in 1m27s
122 lines
3.8 KiB
Go
122 lines
3.8 KiB
Go
package pipeline
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"testing"
|
|
|
|
"evobgp/internal/store"
|
|
)
|
|
|
|
func TestRefreshModule_AggregatesAllEnabledModules(t *testing.T) {
|
|
t.Setenv("EVOBGP_ASN_RESOLVE", "0")
|
|
|
|
m := store.NewMemory()
|
|
m.SeedDemo()
|
|
tenant, _, modIP, _, _ := m.DemoIDs()
|
|
for _, mod := range m.ListModules(tenant) {
|
|
if mod == nil || mod.ID == modIP {
|
|
continue
|
|
}
|
|
disabled := false
|
|
if _, err := m.UpdateModule(tenant, mod.ID, &store.ModulePatch{Enabled: &disabled}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
mod2, err := m.CreateModule(tenant, &store.Module{Type: "IP_RANGES", Name: "extra-ip", Enabled: true, Priority: 30})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := m.CreateIPRangeEntry(tenant, modIP, &store.IPRangeEntry{Prefix: "10.0.0.0/24"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := m.CreateIPRangeEntry(tenant, mod2.ID, &store.IPRangeEntry{Prefix: "192.168.0.0/24"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
ctx := context.Background()
|
|
before, _, _ := m.ListRevisions(tenant, "", "", 200)
|
|
beforeN := len(before)
|
|
|
|
rev1, err := RefreshModule(ctx, m, http.DefaultClient, tenant, modIP)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
after1, _, _ := m.ListRevisions(tenant, "", "", 200)
|
|
if len(after1) != beforeN+1 {
|
|
t.Fatalf("first refresh: want one new tenant revision, got %d -> %d", beforeN, len(after1))
|
|
}
|
|
px, _, _ := m.ListRevisionPrefixes(tenant, rev1, "", 1000)
|
|
if len(px) != 2 {
|
|
t.Fatalf("first refresh: want 2 aggregated prefixes, got %d: %+v", len(px), px)
|
|
}
|
|
|
|
rev2, err := RefreshModule(ctx, m, http.DefaultClient, tenant, mod2.ID)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if rev2 == "" {
|
|
t.Fatal("second refresh: expected non-empty revision id")
|
|
}
|
|
after2, _, _ := m.ListRevisions(tenant, "", "", 200)
|
|
if len(after2) < len(after1) {
|
|
t.Fatalf("second refresh: revisions count should not decrease, had %d now %d", len(after1), len(after2))
|
|
}
|
|
|
|
if _, err := m.CreateIPRangeEntry(tenant, mod2.ID, &store.IPRangeEntry{Prefix: "10.0.1.0/24"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
rev3, err := RefreshModule(ctx, m, http.DefaultClient, tenant, mod2.ID)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if rev3 == rev1 {
|
|
t.Fatal("after prefix change, expected a new revision")
|
|
}
|
|
after3, _, _ := m.ListRevisions(tenant, "", "", 200)
|
|
if len(after3) < len(after2) {
|
|
t.Fatalf("third refresh: revisions count should not decrease, had %d now %d", len(after2), len(after3))
|
|
}
|
|
px3, _, _ := m.ListRevisionPrefixes(tenant, rev3, "", 1000)
|
|
if len(px3) != 2 {
|
|
t.Fatalf("third refresh: smart aggregation should merge adjacent /24, want 2 prefixes, got %d", len(px3))
|
|
}
|
|
}
|
|
|
|
func TestSmartAggregatePrefixRows_RespectsCommunityAndSource(t *testing.T) {
|
|
commA := "c-a"
|
|
commB := "c-b"
|
|
rows := []store.PrefixRow{
|
|
{Prefix: "10.0.0.0/24", CommunityID: &commA, Source: "ip_range"},
|
|
{Prefix: "10.0.1.0/24", CommunityID: &commA, Source: "ip_range"},
|
|
{Prefix: "10.0.2.0/24", CommunityID: &commA, Source: "ip_range"},
|
|
{Prefix: "10.0.3.0/24", CommunityID: &commA, Source: "ip_range"},
|
|
{Prefix: "10.0.4.0/24", CommunityID: &commB, Source: "ip_range"},
|
|
{Prefix: "10.0.5.0/24", CommunityID: &commB, Source: "cdn:x"},
|
|
}
|
|
out := smartAggregatePrefixRows(rows)
|
|
got := make(map[string]struct{}, len(out))
|
|
for _, r := range out {
|
|
c := ""
|
|
if r.CommunityID != nil {
|
|
c = *r.CommunityID
|
|
}
|
|
got[r.Prefix+"|"+c+"|"+r.Source] = struct{}{}
|
|
}
|
|
// First four /24 collapse into /22 because attributes are identical.
|
|
if _, ok := got["10.0.0.0/22|c-a|ip_range"]; !ok {
|
|
t.Fatalf("expected merged prefix for c-a/ip_range, got: %+v", out)
|
|
}
|
|
// Different community/source must stay separate.
|
|
if _, ok := got["10.0.4.0/24|c-b|ip_range"]; !ok {
|
|
t.Fatalf("expected distinct prefix for c-b/ip_range, got: %+v", out)
|
|
}
|
|
if _, ok := got["10.0.5.0/24|c-b|cdn:x"]; !ok {
|
|
t.Fatalf("expected distinct prefix for c-b/cdn:x, got: %+v", out)
|
|
}
|
|
if len(out) != 3 {
|
|
t.Fatalf("expected 3 resulting rows, got %d: %+v", len(out), out)
|
|
}
|
|
}
|