package test import ( "net/http" "net/http/httptest" "os" "path/filepath" "regexp" "strconv" "strings" "testing" ) func TestActivityLogBackendQAResetResponseIsIndistinguishable(t *testing.T) { t.Run("unknown email answers the same bytes as an existing account", func(t *testing.T) { e := newActivityHTTPEnv(t, 1000) known := e.send(http.MethodPost, "/v1/auth/password-reset", `{"email":"marta@example.com"}`) unknown := e.send(http.MethodPost, "/v1/auth/password-reset", `{"email":"nobody@example.com"}`) if known.Code != unknown.Code || known.Body.String() != unknown.Body.String() { t.Errorf("responses differ: %d %q versus %d %q", known.Code, known.Body.String(), unknown.Code, unknown.Body.String()) } }) } func TestActivityLogBackendQAProducersSurviveRecorderFailure(t *testing.T) { type producer func(e *activityHTTPEnv) *httptest.ResponseRecorder producers := map[string]producer{ "login failure": func(e *activityHTTPEnv) *httptest.ResponseRecorder { return e.send(http.MethodPost, "/v1/auth/login", `{"username":"marta","password":"wrongpassword1"}`) }, "logout": func(e *activityHTTPEnv) *httptest.ResponseRecorder { return e.send(http.MethodPost, "/v1/auth/logout", "", asUser(e, activityOwnerID)) }, "password reset request": func(e *activityHTTPEnv) *httptest.ResponseRecorder { return e.send(http.MethodPost, "/v1/auth/password-reset", `{"email":"marta@example.com"}`) }, "api key creation": func(e *activityHTTPEnv) *httptest.ResponseRecorder { return e.send(http.MethodPost, "/v1/api-key", `{"name":"Deploy bot"}`, asUser(e, activityOwnerID)) }, "api key revoke": func(e *activityHTTPEnv) *httptest.ResponseRecorder { _, id := e.issueKey(t, activityOwnerID, "Deploy bot") return e.send(http.MethodDelete, "/v1/api-key/"+strconv.Itoa(int(id)), "", asUser(e, activityOwnerID)) }, "retention change": func(e *activityHTTPEnv) *httptest.ResponseRecorder { return e.send(http.MethodPatch, "/v1/settings", `{"activity_retention_days": 30}`, asUser(e, activityOwnerID)) }, } scrub := regexp.MustCompile(`"(id|key|prefix|created_at)":\s*("[^"]*"|[0-9]+)`) for name, run := range producers { t.Run(name, func(t *testing.T) { healthy := run(newActivityHTTPEnv(t, 1000)) broken := newActivityHTTPEnv(t, 1000) broken.probe.appendErr = errDiskFull got := run(broken) if got.Code != healthy.Code || scrub.ReplaceAllString(got.Body.String(), "") != scrub.ReplaceAllString(healthy.Body.String(), "") { t.Errorf("a failing recorder changed the answer: %d %q versus %d %q", got.Code, got.Body.String(), healthy.Code, healthy.Body.String()) } }) } } func TestActivityLogBackendQAGuards(t *testing.T) { read := func(t *testing.T, root string, suffix string) map[string]string { t.Helper() files := map[string]string{} err := filepath.Walk(filepath.Join("..", root), func(path string, info os.FileInfo, err error) error { if err != nil || info.IsDir() || !strings.HasSuffix(path, suffix) { return err } content, readErr := os.ReadFile(path) files[path] = string(content) return readErr }) if err != nil { t.Fatal(err) } if len(files) == 0 { t.Fatalf("no files under %s", root) } return files } t.Run("The audit repository source uses no dialect specific SQL", func(t *testing.T) { for path, source := range read(t, "internal/infrastructure/persistence/repositories", ".go") { if !strings.Contains(filepath.Base(path), "audit") { continue } for _, banned := range []string{"ILIKE", "strftime", "date_trunc"} { if strings.Contains(source, banned) { t.Errorf("%s contains %s", path, banned) } } } }) t.Run("The audit domain imports only the stdlib and domain packages", func(t *testing.T) { importLine := regexp.MustCompile(`(?m)^\s*"([^"]+)"$`) for path, source := range read(t, "internal/domain/audit", ".go") { for _, match := range importLine.FindAllStringSubmatch(source, -1) { if strings.Contains(match[1], ".") && !strings.HasPrefix(match[1], "RenewCMS/internal/domain/") { t.Errorf("%s imports %s", path, match[1]) } if strings.HasPrefix(match[1], "RenewCMS/") && !strings.HasPrefix(match[1], "RenewCMS/internal/domain/") { t.Errorf("%s imports %s", path, match[1]) } } } }) t.Run("The activity use cases and handlers keep their layering", func(t *testing.T) { banned := []string{"gorm.io", "infrastructure/persistence", "infrastructure/mailer", "net/http", "go-chi", "RenewCMS/api"} for path, source := range read(t, "internal/infrastructure/useCases", ".go") { base := filepath.Base(path) if !strings.Contains(base, "Activity") && !strings.Contains(base, "activity") { continue } for _, word := range banned { if strings.Contains(source, word+`"`) || strings.Contains(source, `"`+word) { t.Errorf("%s imports %s", path, word) } } } for path, source := range read(t, "api/handlers/activity", ".go") { for _, word := range []string{"gorm.io", "persistence", "infrastructure/mailer"} { if strings.Contains(source, word) { t.Errorf("%s references %s", path, word) } } } }) t.Run("The persistence repository asserts the audit contract", func(t *testing.T) { for path, source := range read(t, "internal/infrastructure/persistence/repositories", "auditEventRepository.go") { if !strings.Contains(source, "var _ audit.Repository = &") { t.Errorf("%s lacks the interface assertion", path) } } }) t.Run("The new Go files hold no comment", func(t *testing.T) { line := regexp.MustCompile(`(?m)^\s*//`) for _, root := range []string{"internal/domain/audit", "internal/infrastructure/auditing", "api/handlers/activity"} { for path, source := range read(t, root, ".go") { if line.MatchString(source) { t.Errorf("%s holds a comment", path) } } } }) t.Run("The docs describe the activity surface", func(t *testing.T) { wants := map[string][]string{ "docs/api.md": {"/v1/activity", "/v1/activity/summary", "/v1/activity/export", "activity_retention_days"}, "docs/architecture.md": {"audit"}, "docs/configuration.md": {"retention"}, } for doc, needles := range wants { content, err := os.ReadFile(filepath.Join("..", doc)) if err != nil { t.Fatal(err) } for _, needle := range needles { if !strings.Contains(string(content), needle) { t.Errorf("%s does not mention %s", doc, needle) } } } }) }