soteria/internal/server/server_start_test.go

126 lines
3.7 KiB
Go
Raw Permalink Normal View History

package server
import (
"context"
"strings"
"sync"
"testing"
"time"
"scm.bstein.dev/bstein/soteria/internal/api"
"scm.bstein.dev/bstein/soteria/internal/config"
"scm.bstein.dev/bstein/soteria/internal/k8s"
)
type startTestKubeClient struct {
*fakeKubeClient
mu sync.Mutex
loadCalls int
listPVCCalls int
}
func (k *startTestKubeClient) LoadSecretData(ctx context.Context, namespace, secretName, key string) ([]byte, error) {
k.mu.Lock()
k.loadCalls++
k.mu.Unlock()
return k.fakeKubeClient.LoadSecretData(ctx, namespace, secretName, key)
}
func (k *startTestKubeClient) ListBoundPVCs(ctx context.Context) ([]k8s.PVCSummary, error) {
k.mu.Lock()
k.listPVCCalls++
k.mu.Unlock()
return k.fakeKubeClient.ListBoundPVCs(ctx)
}
func (k *startTestKubeClient) counts() (int, int) {
k.mu.Lock()
defer k.mu.Unlock()
return k.loadCalls, k.listPVCCalls
}
func newStartTestServer(cfg *config.Config, client kubeClient) *Server {
return &Server{
cfg: cfg,
client: client,
longhorn: &fakeLonghornClient{},
metrics: newTelemetry(),
ui: newUIRenderer(),
policies: map[string]api.BackupPolicy{},
jobUsage: map[string]resticJobUsageCacheEntry{},
usageStore: map[string]resticPersistedUsageEntry{},
}
}
func TestStartRunsInitialLoadAndTickerLoopWithoutB2(t *testing.T) {
client := &startTestKubeClient{
fakeKubeClient: &fakeKubeClient{
pvcs: []k8s.PVCSummary{{Namespace: "apps", Name: "data", VolumeName: "vol-data", Phase: "Bound"}},
},
}
srv := newStartTestServer(&config.Config{
Namespace: "atlas",
PolicySecretName: "soteria-policies",
UsageSecretName: "soteria-usage",
BackupDriver: "longhorn",
BackupMaxAge: 24 * time.Hour,
MetricsRefreshInterval: 10 * time.Millisecond,
PolicyEvalInterval: 10 * time.Millisecond,
B2Enabled: false,
}, client)
ctx, cancel := context.WithCancel(context.Background())
srv.Start(ctx)
time.Sleep(35 * time.Millisecond)
cancel()
time.Sleep(20 * time.Millisecond)
loadCalls, listPVCCalls := client.counts()
if loadCalls < 2 {
t.Fatalf("expected initial policy/usage secret loads, got %d", loadCalls)
}
if listPVCCalls < 2 {
t.Fatalf("expected inventory refresh to run initially and on ticker, got %d", listPVCCalls)
}
if srv.metrics.inventoryRefreshTime == 0 {
t.Fatalf("expected inventory metrics to be recorded after start loop")
}
}
func TestStartRunsB2TickerAndStoresRefreshFailures(t *testing.T) {
client := &startTestKubeClient{
fakeKubeClient: &fakeKubeClient{
pvcs: []k8s.PVCSummary{{Namespace: "apps", Name: "data", VolumeName: "vol-data", Phase: "Bound"}},
},
}
srv := newStartTestServer(&config.Config{
Namespace: "atlas",
PolicySecretName: "soteria-policies",
UsageSecretName: "soteria-usage",
BackupDriver: "longhorn",
BackupMaxAge: 24 * time.Hour,
MetricsRefreshInterval: 10 * time.Millisecond,
PolicyEvalInterval: 10 * time.Millisecond,
B2Enabled: true,
B2Endpoint: "https://",
B2AccessKeyID: "atlas-key",
B2SecretAccessKey: "atlas-secret",
B2ScanInterval: 10 * time.Millisecond,
B2ScanTimeout: 10 * time.Millisecond,
}, client)
ctx, cancel := context.WithCancel(context.Background())
srv.Start(ctx)
time.Sleep(35 * time.Millisecond)
cancel()
time.Sleep(20 * time.Millisecond)
usage := srv.getB2Usage()
if !usage.Enabled || usage.Error == "" || !strings.Contains(usage.Error, "S3 endpoint host is empty") {
t.Fatalf("expected B2 ticker refresh failure snapshot, got %#v", usage)
}
if srv.metrics.b2ScanTimestamp == 0 {
t.Fatalf("expected B2 scan metrics to be recorded during start loop")
}
}