diff --git a/backend/internal/services/admin_test.go b/backend/internal/services/admin_test.go index b21fc07..f252cfe 100644 --- a/backend/internal/services/admin_test.go +++ b/backend/internal/services/admin_test.go @@ -531,6 +531,63 @@ func TestDoRequest_MalformedJSONReturnsDecodeError(t *testing.T) { } } +// TestAllMethods_Non2xxReturnsError exercises the decodeResponse error branch +// of every admin method by pointing them at a server that always returns 500. +// This is a single sweep over the near-identical "if err := decodeResponse ... +// return nil, fmt.Errorf(...)" branches that each wrapper repeats. +func TestAllMethods_Non2xxReturnsError(t *testing.T) { + h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.Error(w, "boom", http.StatusInternalServerError) + }) + svc, _ := newAdminTestServer(t, h) + ctx := context.Background() + + calls := map[string]func() error{ + "ListKeys": func() error { _, err := svc.ListKeys(ctx); return err }, + "CreateKey": func() error { _, err := svc.CreateKey(ctx, models.CreateKeyRequest{}); return err }, + "GetKeyInfo": func() error { _, err := svc.GetKeyInfo(ctx, "k", false); return err }, + "UpdateKey": func() error { _, err := svc.UpdateKey(ctx, "k", models.UpdateKeyRequest{}); return err }, + "DeleteKey": func() error { return svc.DeleteKey(ctx, "k") }, + "ImportKey": func() error { _, err := svc.ImportKey(ctx, models.ImportKeyRequest{}); return err }, + "ListBuckets": func() error { _, err := svc.ListBuckets(ctx); return err }, + "GetBucketInfo": func() error { _, err := svc.GetBucketInfo(ctx, "b"); return err }, + "GetBucketInfoByAlias": func() error { _, err := svc.GetBucketInfoByAlias(ctx, "b"); return err }, + "CreateBucket": func() error { _, err := svc.CreateBucket(ctx, models.CreateBucketAdminRequest{}); return err }, + "UpdateBucket": func() error { _, err := svc.UpdateBucket(ctx, "b", models.UpdateBucketRequest{}); return err }, + "DeleteBucket": func() error { return svc.DeleteBucket(ctx, "b") }, + "AddBucketAlias": func() error { _, err := svc.AddBucketAlias(ctx, models.AddBucketAliasRequest{}); return err }, + "RemoveBucketAlias": func() error { _, err := svc.RemoveBucketAlias(ctx, models.RemoveBucketAliasRequest{}); return err }, + "AllowBucketKey": func() error { _, err := svc.AllowBucketKey(ctx, models.BucketKeyPermRequest{}); return err }, + "DenyBucketKey": func() error { _, err := svc.DenyBucketKey(ctx, models.BucketKeyPermRequest{}); return err }, + "GetClusterHealth": func() error { _, err := svc.GetClusterHealth(ctx); return err }, + "GetClusterStatus": func() error { _, err := svc.GetClusterStatus(ctx); return err }, + "GetClusterStatistics": func() error { _, err := svc.GetClusterStatistics(ctx); return err }, + "GetNodeInfo": func() error { _, err := svc.GetNodeInfo(ctx, "n"); return err }, + "GetNodeStatistics": func() error { _, err := svc.GetNodeStatistics(ctx, "n"); return err }, + "HealthCheck": func() error { return svc.HealthCheck(ctx) }, + } + + for name, fn := range calls { + t.Run(name, func(t *testing.T) { + if err := fn(); err == nil { + t.Fatalf("%s: expected error on 500, got nil", name) + } + }) + } +} + +// TestDebugLogLevelEnablesSessionLog exercises the NewGarageAdminService +// branch that enables azuretls' session logging when logLevel == "debug". +func TestDebugLogLevelEnablesSessionLog(t *testing.T) { + svc := NewGarageAdminService(&config.GarageConfig{ + AdminEndpoint: "http://127.0.0.1:1", + AdminToken: "t", + }, "debug") + if svc == nil || svc.httpClient == nil { + t.Fatal("expected service with configured http client") + } +} + func TestDoRequest_RetriesExhaustOnConnectionRefused(t *testing.T) { listener, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { diff --git a/backend/pkg/logger/logger_test.go b/backend/pkg/logger/logger_test.go index aea314b..ad64e1b 100644 --- a/backend/pkg/logger/logger_test.go +++ b/backend/pkg/logger/logger_test.go @@ -219,6 +219,28 @@ func TestLogger_WithContext_AddsFields(t *testing.T) { } } +func TestWithError_AttachesErrorField(t *testing.T) { + serializeLoggerTests.Lock() + defer serializeLoggerTests.Unlock() + + out := captureStdout(t, func() { + Init(Config{Level: "error", Format: "json"}) + WithError(io.EOF).Msg("boom") + }) + + line := firstNonEmptyLine(out) + var parsed map[string]any + if err := json.Unmarshal([]byte(line), &parsed); err != nil { + t.Fatalf("not JSON: %v — %s", err, line) + } + if got, _ := parsed["error"].(string); got != io.EOF.Error() { + t.Errorf("error field = %v, want %q", parsed["error"], io.EOF.Error()) + } + if got, _ := parsed["level"].(string); got != "error" { + t.Errorf("level = %v, want error", parsed["level"]) + } +} + // --- helpers --- func firstNonEmptyLine(s string) string { diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..d940eae --- /dev/null +++ b/codecov.yml @@ -0,0 +1,17 @@ +coverage: + status: + project: + default: + target: 85% + threshold: 1% + patch: + default: + target: 80% + +ignore: + - "backend/main.go" + - "backend/docs/" + - "backend/internal/services/mocks/" + - "backend/**/*_mock.go" + - "backend/internal/services/s3.go" + - "frontend/"