package test import ( "RenewCMS/internal/domain/audit" "errors" "net/http" "strings" "testing" ) func validActivityEvent() audit.Event { event := audit.NewEvent("article.publish", audit.CategoryArticles) event.Source = audit.SourceWeb event.Actor = audit.Actor{Type: audit.ActorUser, Name: "marta"} return event } func TestActivityEventValidation(t *testing.T) { long := func(n int) *string { value := strings.Repeat("x", n); return &value } cases := []struct { name string mutate func(*audit.Event) valid bool }{ {"complete event", func(e *audit.Event) {}, true}, {"empty action", func(e *audit.Event) { e.Action = "" }, false}, {"action at the limit", func(e *audit.Event) { e.Action = strings.Repeat("a", 64) }, true}, {"action over the limit", func(e *audit.Event) { e.Action = strings.Repeat("a", 65) }, false}, {"unknown category", func(e *audit.Event) { e.Category = "nope" }, false}, {"unknown outcome", func(e *audit.Event) { e.Outcome = "nope" }, false}, {"unknown source", func(e *audit.Event) { e.Source = "nope" }, false}, {"unknown actor type", func(e *audit.Event) { e.Actor.Type = "nope" }, false}, {"failure reason at the limit", func(e *audit.Event) { e.FailureReason = long(255) }, true}, {"failure reason over the limit", func(e *audit.Event) { e.FailureReason = long(256) }, false}, {"unknown change kind", func(e *audit.Event) { e.Changes = []audit.Change{{Field: "a", Kind: "nope"}} }, false}, {"body change", func(e *audit.Event) { e.Changes = []audit.Change{{Field: "a", Kind: audit.ChangeBody}} }, true}, } for _, c := range cases { event := validActivityEvent() c.mutate(&event) err := event.Validate() if c.valid && err != nil { t.Errorf("%s: expected valid, got %v", c.name, err) } if !c.valid && !errors.Is(err, audit.ErrInvalidEvent) { t.Errorf("%s: expected ErrInvalidEvent, got %v", c.name, err) } } t.Run("Names are cut to 255 characters", func(t *testing.T) { event := validActivityEvent() event.Actor.Name = strings.Repeat("é", 256) event.Target.Name = strings.Repeat("é", 255) cut := event.Truncated() if len([]rune(cut.Actor.Name)) != 255 || len([]rune(cut.Target.Name)) != 255 { t.Errorf("unexpected lengths %d and %d", len([]rune(cut.Actor.Name)), len([]rune(cut.Target.Name))) } }) t.Run("The discard recorder accepts events silently", func(t *testing.T) { audit.Discard.Record(testCtx, validActivityEvent()) }) } func TestActivityHandlerErrorMapping(t *testing.T) { t.Run("A signed-in user who no longer exists is unauthorised", func(t *testing.T) { e := newActivityHTTPEnv(t, 1000) rec := e.get("/v1/activity", asUser(e, 99)) if rec.Code != http.StatusUnauthorized { t.Errorf("expected 401, got %d", rec.Code) } }) t.Run("A storage failure is a plain 500", func(t *testing.T) { for _, path := range []string{"/v1/activity", "/v1/activity/summary", "/v1/activity/export"} { e := newActivityHTTPEnv(t, 1000) e.probe.readErr = errors.New("storage down") rec := e.get(path, asUser(e, activityOwnerID)) if path != "/v1/activity/export" && rec.Code != http.StatusInternalServerError { t.Errorf("%s: expected 500, got %d", path, rec.Code) } } }) }