test(soteria): lift helper and B2 refresh coverage
This commit is contained in:
parent
7df3552f4f
commit
7b592edb6d
18
internal/k8s/client_test.go
Normal file
18
internal/k8s/client_test.go
Normal file
@ -0,0 +1,18 @@
|
||||
package k8s
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewReturnsErrorWhenNoClusterConfigOrValidKubeconfigExists(t *testing.T) {
|
||||
t.Setenv("KUBECONFIG", "/definitely/missing/kubeconfig")
|
||||
|
||||
client, err := New()
|
||||
if err == nil || client != nil {
|
||||
t.Fatalf("expected New to fail without usable Kubernetes config, got client=%#v err=%v", client, err)
|
||||
}
|
||||
if !strings.Contains(err.Error(), "load kubeconfig") {
|
||||
t.Fatalf("expected kubeconfig load error, got %v", err)
|
||||
}
|
||||
}
|
||||
121
internal/k8s/job_helpers_test.go
Normal file
121
internal/k8s/job_helpers_test.go
Normal file
@ -0,0 +1,121 @@
|
||||
package k8s
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"scm.bstein.dev/bstein/soteria/internal/api"
|
||||
"scm.bstein.dev/bstein/soteria/internal/config"
|
||||
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
k8sfake "k8s.io/client-go/kubernetes/fake"
|
||||
)
|
||||
|
||||
func TestJobSupportHelpersCoverFallbackAndFormattingBranches(t *testing.T) {
|
||||
if got := sanitizeRepositorySegment(" "); got != "unknown" {
|
||||
t.Fatalf("expected empty repository segment to fall back to unknown, got %q", got)
|
||||
}
|
||||
if got := keepLastWithDefault(nil); got != 0 {
|
||||
t.Fatalf("expected nil keep-last to default to zero, got %d", got)
|
||||
}
|
||||
negative := -1
|
||||
if got := keepLastWithDefault(&negative); got != 0 {
|
||||
t.Fatalf("expected negative keep-last to clamp to zero, got %d", got)
|
||||
}
|
||||
if got := appendRepositoryPath("", "isolated/apps/data"); got != "" {
|
||||
t.Fatalf("expected empty base repository to stay empty, got %q", got)
|
||||
}
|
||||
if got := appendRepositoryPath("s3:https://repo/root", ""); got != "s3:https://repo/root" {
|
||||
t.Fatalf("expected empty suffix to preserve base repository, got %q", got)
|
||||
}
|
||||
if got := parseIntWithDefault("7", 1); got != 7 {
|
||||
t.Fatalf("expected valid integer parse, got %d", got)
|
||||
}
|
||||
if got := parseIntWithDefault("-1", 1); got != 1 {
|
||||
t.Fatalf("expected negative integer to fall back, got %d", got)
|
||||
}
|
||||
if got := parseBoolWithDefault("off", true); got != false {
|
||||
t.Fatalf("expected explicit false parse, got %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobNameBackupCommandAndResticEnvCoverRemainingBranches(t *testing.T) {
|
||||
cfg := &config.Config{
|
||||
ResticRepository: "s3:https://repo/root",
|
||||
ResticBackupArgs: []string{"--exclude", "*.tmp"},
|
||||
ResticForgetArgs: []string{"--keep-daily", "7", "--prune"},
|
||||
S3Endpoint: "https://s3.us-west-001.backblazeb2.com",
|
||||
S3Region: "us-west-001",
|
||||
}
|
||||
|
||||
name := jobName("backup", "Data_Set.With Spaces.and.extra-long-segment-to-force-truncation-beyond-sixty-three-characters")
|
||||
if len(name) > 63 || strings.Contains(name, "_") || strings.Contains(name, " ") || strings.Contains(name, ".") {
|
||||
t.Fatalf("expected sanitized Kubernetes-safe job name, got %q", name)
|
||||
}
|
||||
if !strings.HasPrefix(name, "soteria-backup-data-set-with-spaces-and-extra") {
|
||||
t.Fatalf("expected job name prefix to be sanitized, got %q", name)
|
||||
}
|
||||
|
||||
dedupe := false
|
||||
keepLast := 3
|
||||
cmd := backupCommand(cfg, api.BackupRequest{
|
||||
PVC: "data",
|
||||
Tags: []string{" nightly ", "", "prod"},
|
||||
Dedupe: &dedupe,
|
||||
KeepLast: &keepLast,
|
||||
})
|
||||
if !strings.Contains(cmd, "restic init") || !strings.Contains(cmd, "--tag dedupe=off") || !strings.Contains(cmd, "--keep-last 3") {
|
||||
t.Fatalf("expected backup command to include bootstrap/dedupe/keep-last logic, got %q", cmd)
|
||||
}
|
||||
if !strings.Contains(cmd, "--exclude *.tmp") || !strings.Contains(cmd, "--tag nightly") || !strings.Contains(cmd, "--tag prod") {
|
||||
t.Fatalf("expected backup command to include trimmed tags and extra args, got %q", cmd)
|
||||
}
|
||||
|
||||
cmd = backupCommand(cfg, api.BackupRequest{PVC: "data"})
|
||||
if !strings.Contains(cmd, "restic forget --keep-daily 7 --prune") {
|
||||
t.Fatalf("expected backup command to use configured forget args, got %q", cmd)
|
||||
}
|
||||
|
||||
env := resticEnv(cfg, "restic-secret", "")
|
||||
values := map[string]string{}
|
||||
for _, item := range env {
|
||||
if item.Value != "" {
|
||||
values[item.Name] = item.Value
|
||||
}
|
||||
}
|
||||
if values["RESTIC_REPOSITORY"] != "s3:https://repo/root" || values["RESTIC_S3_ENDPOINT"] != cfg.S3Endpoint || values["AWS_REGION"] != cfg.S3Region {
|
||||
t.Fatalf("expected repository/endpoint/region env vars, got %#v", values)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopySecretAndBindSecretToJobCoverErrorBranches(t *testing.T) {
|
||||
client := &Client{Clientset: k8sfake.NewSimpleClientset()}
|
||||
|
||||
if _, err := client.copySecret(context.Background(), "shared", "missing", "apps", "copy", nil); err == nil {
|
||||
t.Fatalf("expected missing source secret error")
|
||||
}
|
||||
|
||||
if err := client.bindSecretToJob(context.Background(), "apps", "missing", &batchv1.Job{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "job-a"},
|
||||
}); err == nil {
|
||||
t.Fatalf("expected missing secret bind error")
|
||||
}
|
||||
|
||||
client = &Client{Clientset: k8sfake.NewSimpleClientset(
|
||||
&corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "src", Namespace: "shared"},
|
||||
Type: corev1.SecretTypeOpaque,
|
||||
Data: map[string][]byte{"token": []byte("atlas")},
|
||||
},
|
||||
)}
|
||||
secret, err := client.copySecret(context.Background(), "shared", "src", "apps", "copy", map[string]string{"app": "soteria"})
|
||||
if err != nil {
|
||||
t.Fatalf("copy secret: %v", err)
|
||||
}
|
||||
if secret.Namespace != "apps" || secret.Name != "copy" || secret.Labels["app"] != "soteria" {
|
||||
t.Fatalf("expected copied secret in destination namespace, got %#v", secret)
|
||||
}
|
||||
}
|
||||
64
internal/server/b2_refresh_test.go
Normal file
64
internal/server/b2_refresh_test.go
Normal file
@ -0,0 +1,64 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"scm.bstein.dev/bstein/soteria/internal/config"
|
||||
)
|
||||
|
||||
func TestRefreshB2UsageRecordsCredentialResolutionErrors(t *testing.T) {
|
||||
srv := &Server{
|
||||
cfg: &config.Config{
|
||||
B2Enabled: true,
|
||||
B2ScanTimeout: time.Second,
|
||||
},
|
||||
client: &fakeKubeClient{},
|
||||
metrics: newTelemetry(),
|
||||
}
|
||||
|
||||
srv.refreshB2Usage(context.Background())
|
||||
|
||||
usage := srv.getB2Usage()
|
||||
if !usage.Enabled || usage.Error == "" || !strings.Contains(usage.Error, "B2 endpoint is not configured") {
|
||||
t.Fatalf("expected B2 credential resolution error snapshot, got %#v", usage)
|
||||
}
|
||||
if usage.ScannedAt == "" {
|
||||
t.Fatalf("expected failed refresh to record scanned timestamp, got %#v", usage)
|
||||
}
|
||||
if srv.metrics.b2ScanSuccess != 0 || srv.metrics.b2ScanTimestamp == 0 {
|
||||
t.Fatalf("expected failed B2 scan metrics to be recorded, got success=%f timestamp=%f", srv.metrics.b2ScanSuccess, srv.metrics.b2ScanTimestamp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefreshB2UsageRecordsScanErrorsAfterCredentialsResolve(t *testing.T) {
|
||||
srv := &Server{
|
||||
cfg: &config.Config{
|
||||
B2Enabled: true,
|
||||
B2Endpoint: "https://",
|
||||
B2AccessKeyID: "atlas-key",
|
||||
B2SecretAccessKey: "atlas-secret",
|
||||
B2ScanTimeout: time.Second,
|
||||
},
|
||||
client: &fakeKubeClient{},
|
||||
metrics: newTelemetry(),
|
||||
}
|
||||
|
||||
srv.refreshB2Usage(context.Background())
|
||||
|
||||
usage := srv.getB2Usage()
|
||||
if usage.Endpoint != "https://" {
|
||||
t.Fatalf("expected resolved endpoint to be preserved in failed snapshot, got %#v", usage)
|
||||
}
|
||||
if usage.Error == "" || !strings.Contains(usage.Error, "S3 endpoint host is empty") {
|
||||
t.Fatalf("expected B2 scan error snapshot, got %#v", usage)
|
||||
}
|
||||
if usage.ScannedAt == "" || usage.ScanDurationMS < 0 {
|
||||
t.Fatalf("expected scan metadata on failure, got %#v", usage)
|
||||
}
|
||||
if srv.metrics.b2ScanSuccess != 0 || srv.metrics.b2ScanDurationSeconds < 0 {
|
||||
t.Fatalf("expected failure metrics after scan error, got success=%f duration=%f", srv.metrics.b2ScanSuccess, srv.metrics.b2ScanDurationSeconds)
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user