quality / commitlint (push) Skipped
quality / changes (push) Successful in 9s
quality / docker-check (push) Skipped
quality / openapi (push) Successful in 46s
quality / web (push) Successful in 1m16s
quality / go (push) Successful in 2m42s
quality / bird2 (push) Successful in 16s
CD / quality (push) Successful in 5m19s
CD / publish (push) Successful in 7m19s
- Deleted unused components: `DashboardActivityTimeline`, `DashboardFramePanel`, `DashboardModulesGrid`, `DashboardRecentJobsGrid`, and `DashboardRecentRevisionsGrid` to streamline the dashboard. - Updated `DashboardKpiGrid` to improve KPI display logic, including progress indicators and enhanced badge functionality. - Refactored `DashboardNetworkHealth` to provide better status representation based on loading states and network conditions. - Introduced new properties for KPI cards to support progress tracking and improved visual feedback. This cleanup aims to enhance performance and maintainability of the dashboard while providing a better user experience.
190 lines
5.4 KiB
Go
190 lines
5.4 KiB
Go
package pipeline
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"evobgp/internal/store"
|
|
)
|
|
|
|
func TestModuleDueForScheduler_JitterSpreads(t *testing.T) {
|
|
dueTicks := map[int64]int{}
|
|
for i := 0; i < 100; i++ {
|
|
mod := &store.Module{
|
|
ID: fmt.Sprintf("module-%d", i),
|
|
Enabled: true,
|
|
Type: "AS_PREFIXES",
|
|
RefreshIntervalSec: 3600,
|
|
}
|
|
for tick := int64(0); tick < 7200; tick += SchedulerTickSec {
|
|
if ModuleDueForScheduler(mod, time.Unix(tick, 0)) {
|
|
dueTicks[tick]++
|
|
}
|
|
}
|
|
}
|
|
if len(dueTicks) < 10 {
|
|
t.Fatalf("expected due events across many ticks, got %d buckets", len(dueTicks))
|
|
}
|
|
for tick, n := range dueTicks {
|
|
if n == 100 {
|
|
t.Fatalf("all 100 modules due on tick %d", tick)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCollectASPrefixRows_KeepsSamePrefixDifferentCommunity(t *testing.T) {
|
|
st := store.NewMemory()
|
|
st.SeedDemo()
|
|
tenant, _, _, _, _ := st.DemoIDs()
|
|
mod, err := st.CreateModule(tenant, &store.Module{Type: "AS_PREFIXES", Name: "as-dedup", Enabled: true})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
c1, c2 := "comm-a", "comm-b"
|
|
e1, err := st.CreateASEntry(tenant, mod.ID, &store.ASEntry{ASN: 64500, CommunityID: &c1})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
e2, err := st.CreateASEntry(tenant, mod.ID, &store.ASEntry{ASN: 64501, CommunityID: &c2})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := st.SetASNPrefixCache(64500, "a", []string{"192.0.2.0/24"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := st.SetASNPrefixCache(64501, "b", []string{"192.0.2.0/24"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
rows, err := collectASPrefixRows(context.Background(), st, http.DefaultClient, tenant, mod, []*store.ASEntry{e1, e2}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(rows) != 2 {
|
|
t.Fatalf("expected 2 rows (same prefix, different community), got %d: %+v", len(rows), rows)
|
|
}
|
|
}
|
|
|
|
func TestCollectCDNPrefixRows_PartialSkipKeepsPrior(t *testing.T) {
|
|
t.Setenv("EVOBGP_CDN_PARTIAL_OK", "1")
|
|
t.Setenv("EVOBGP_STALE_ON_UPSTREAM_ERROR", "0")
|
|
t.Setenv("EVOBGP_CDN_ALLOW_PRIVATE", "1")
|
|
|
|
good := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
_, _ = w.Write([]byte("198.51.100.0/24\n"))
|
|
}))
|
|
defer good.Close()
|
|
bad := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
http.Error(w, "down", http.StatusServiceUnavailable)
|
|
}))
|
|
defer bad.Close()
|
|
|
|
st := store.NewMemory()
|
|
st.SeedDemo()
|
|
tenant, _, _, _, _ := st.DemoIDs()
|
|
mod, err := st.CreateModule(tenant, &store.Module{Type: "CDN_CIDRS", Name: "cdn-partial", Enabled: true})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
prior := []store.PrefixRow{
|
|
{Prefix: "203.0.113.0/24", Source: "cdn:bad"},
|
|
{Prefix: "1.2.3.0/24", Source: "cdn:good"},
|
|
}
|
|
sources := []*store.CDNSource{
|
|
{ID: "good", URL: good.URL, SourceKind: "plain"},
|
|
{ID: "bad", URL: bad.URL, SourceKind: "plain"},
|
|
}
|
|
rows, err := collectCDNPrefixRows(context.Background(), st, good.Client(), tenant, mod, sources, prior)
|
|
if err != nil {
|
|
t.Fatalf("partial skip should succeed: %v", err)
|
|
}
|
|
got := map[string]string{}
|
|
for _, r := range rows {
|
|
got[r.Source] = r.Prefix
|
|
}
|
|
if got["cdn:bad"] != "203.0.113.0/24" {
|
|
t.Fatalf("skipped source lost prior row: %+v", rows)
|
|
}
|
|
if got["cdn:good"] != "198.51.100.0/24" {
|
|
t.Fatalf("fetched source missing new row: %+v", rows)
|
|
}
|
|
}
|
|
|
|
func TestMergeCDNSource_ParallelNoLostUpdate(t *testing.T) {
|
|
st := store.NewMemory()
|
|
st.SeedDemo()
|
|
tenant, _, _, _, _ := st.DemoIDs()
|
|
mod, err := st.CreateModule(tenant, &store.Module{Type: "CDN_CIDRS", Name: "cdn-lock", Enabled: true})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var wg sync.WaitGroup
|
|
wg.Add(2)
|
|
go func() {
|
|
defer wg.Done()
|
|
_ = mergeCDNSourceIntoModuleSnapshot(st, tenant, mod, "s1", []store.PrefixRow{
|
|
{Prefix: "1.0.0.0/24", Source: "cdn:s1"},
|
|
})
|
|
}()
|
|
go func() {
|
|
defer wg.Done()
|
|
_ = mergeCDNSourceIntoModuleSnapshot(st, tenant, mod, "s2", []store.PrefixRow{
|
|
{Prefix: "2.0.0.0/24", Source: "cdn:s2"},
|
|
})
|
|
}()
|
|
wg.Wait()
|
|
snap, ok, err := st.GetModulePrefixSnapshot(tenant, mod.ID)
|
|
if err != nil || !ok || snap == nil {
|
|
t.Fatalf("snapshot missing: ok=%v err=%v", ok, err)
|
|
}
|
|
got := map[string]bool{}
|
|
for _, r := range snap.Prefixes {
|
|
got[r.Prefix] = true
|
|
}
|
|
if !got["1.0.0.0/24"] || !got["2.0.0.0/24"] {
|
|
t.Fatalf("lost parallel merge update: %+v", snap.Prefixes)
|
|
}
|
|
}
|
|
|
|
func TestResolveDomainIPsCached_UsesTTL(t *testing.T) {
|
|
t.Setenv("EVOBGP_DOMAIN_CACHE_TTL_SEC", "300")
|
|
st := store.NewMemory()
|
|
if err := st.SetDomainResolveCache("cached.test", []string{"192.0.2.9"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
addrs, err := resolveDomainIPsCached(context.Background(), st, nil, nil, "", "cached.test")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(addrs) != 1 || addrs[0].String() != "192.0.2.9" {
|
|
t.Fatalf("expected cached addr, got %v", addrs)
|
|
}
|
|
}
|
|
|
|
func TestInputHashInvalidatedOnEntryCRUD(t *testing.T) {
|
|
st := store.NewMemory()
|
|
st.SeedDemo()
|
|
tenant, _, _, _, _ := st.DemoIDs()
|
|
mod, err := st.CreateModule(tenant, &store.Module{Type: "IP_RANGES", Name: "ip-hash", Enabled: true})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := st.SetModuleInputHash(tenant, mod.ID, "pre"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := st.CreateIPRangeEntry(tenant, mod.ID, &store.IPRangeEntry{Prefix: "10.0.0.0/8"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := st.GetModule(tenant, mod.ID)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got.InputHash != "" {
|
|
t.Fatalf("expected hash cleared after CRUD, got %q", got.InputHash)
|
|
}
|
|
}
|