mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-10-20 07:31:56 +00:00
105 lines
3.5 KiB
Go
105 lines
3.5 KiB
Go
// Copyright 2026 Beacon Contributors
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
package meshmapper
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/MeshCore-Beacon/beacon-server/internal/config"
|
|
"github.com/MeshCore-Beacon/beacon-server/internal/scopestore"
|
|
)
|
|
|
|
func TestPublicCataloguesFreshnessAndIsolation(t *testing.T) {
|
|
status := http.StatusOK
|
|
calls := 0
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
calls++
|
|
w.WriteHeader(status)
|
|
if status == http.StatusOK {
|
|
_, _ = w.Write(catalogue("yow", "YOW", "monitored-zero"))
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
cfg := config.MeshMapperScopesConfig{Enabled: true}
|
|
store := newMemoryStore("YOW")
|
|
imp, err := New(context.Background(), cfg, store, NewDirectory(newZoneListMemory(), ""), scopestore.New(), nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := imp.Catalogues(); len(got) != 1 || got[0].CheckedAt != 0 || len(got[0].Scopes) != 0 {
|
|
t.Fatal(got)
|
|
}
|
|
imp.sources[0].url = server.URL
|
|
imp.client = server.Client()
|
|
now := time.Now()
|
|
if err := imp.refresh(context.Background(), &imp.sources[0], now); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
imp.publishCatalogues()
|
|
old := imp.Catalogues()
|
|
if old[0].Scopes[0].Name != "#yow" || old[0].Scopes[1].Name != "#YOW" || len(old[0].Scopes) != 3 || old[0].Repeaters != 5 || old[0].Scoped != 3 || old[0].FreshUntil <= old[0].CheckedAt {
|
|
t.Fatal(old)
|
|
}
|
|
status = http.StatusServiceUnavailable
|
|
if err := imp.refresh(context.Background(), &imp.sources[0], now.Add(time.Minute)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
imp.publishCatalogues()
|
|
current := imp.Catalogues()[0]
|
|
if current.LastError != "HTTP 503" || current.CheckedAt != old[0].CheckedAt || current.GeneratedAt != old[0].GeneratedAt || len(current.Scopes) != 3 || old[0].LastError != "" {
|
|
t.Fatal(current, old)
|
|
}
|
|
// Readers only access published immutable data; the race suite exercises concurrent refreshes.
|
|
var wg sync.WaitGroup
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
for range 100 {
|
|
_, _ = json.Marshal(imp.Catalogues())
|
|
}
|
|
}()
|
|
for range 100 {
|
|
imp.publishCatalogues()
|
|
}
|
|
wg.Wait()
|
|
if calls != 2 {
|
|
t.Fatal("reading public metadata performed upstream requests", calls)
|
|
}
|
|
disabled, err := New(context.Background(), config.MeshMapperScopesConfig{}, store, NewDirectory(newZoneListMemory(), ""), scopestore.New(), nil)
|
|
if err != nil || len(disabled.Catalogues()) != 0 {
|
|
t.Fatal("disabled importer exposed old membership")
|
|
}
|
|
store.rows[sourceKey{"YOW", server.URL}] = Cache{Payload: []byte(`{"scopes":"invalid"}`)}
|
|
restored, err := New(context.Background(), cfg, store, NewDirectory(newZoneListMemory(), ""), scopestore.New(), nil)
|
|
if err != nil || len(restored.Catalogues()[0].Scopes) != 0 || restored.Catalogues()[0].LastError == "" {
|
|
t.Fatal("invalid saved metadata leaked")
|
|
}
|
|
}
|
|
|
|
func TestPublicCataloguesIncludeDiscoveredAndUnlistedRegions(t *testing.T) {
|
|
ctx := context.Background()
|
|
store := newMemoryStore("YOW")
|
|
dir := NewDirectory(newZoneListMemory(), "")
|
|
dir.lists["CA"] = &zoneList{zones: map[string]zoneEntry{}, fetchedAt: time.Now()}
|
|
imp, err := New(ctx, config.MeshMapperScopesConfig{Enabled: true}, store, dir, scopestore.New(), nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
before := imp.Catalogues()
|
|
store.iatas = append(store.iatas, "YKF")
|
|
if err := imp.Refresh(ctx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
after := imp.Catalogues()
|
|
if len(before) != 1 || before[0].LastError != "" || len(after) != 2 || after[1].IATA != "YKF" || after[1].LastError != "not listed" {
|
|
t.Fatal(before, after)
|
|
}
|
|
}
|