feat: activity log backend foundation
This commit is contained in:
1 parent
8907bb49fe
commit
50b1649965
74 files changed
+5949
-239
No files matched your search
@@ -0,0 +1,92 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user