package test import ( "RenewCMS/internal/domain/audit" "strings" "testing" "time" ) func TestActivityRecorder(t *testing.T) { t.Run("A failing repository does not fail the caller and the failure is logged", func(t *testing.T) { h := newActivityHarness(t) h.probe.appendErr = errDiskFull h.recorder.Record(testCtx, activityValidEvent()) logs := h.logs.String() if !strings.Contains(logs, "audit.test") || !strings.Contains(logs, "disk full") { t.Errorf("expected a log line naming the write and the error, got %q", logs) } }) t.Run("A panicking repository does not crash the caller", func(t *testing.T) { h := newActivityHarness(t) h.probe.appendPanic = true h.recorder.Record(testCtx, activityValidEvent()) if !strings.Contains(h.logs.String(), "panicked") { t.Errorf("expected the panic to be logged, got %q", h.logs.String()) } }) t.Run("The recorder fills the event from the audit context", func(t *testing.T) { h := newActivityHarness(t) h.now = time.Date(2026, time.October, 6, 14, 0, 0, 0, time.FixedZone("CEST", 2*3600)) ctx := audit.WithOrigin(testCtx, audit.Origin{ Actor: audit.Actor{Type: audit.ActorUser, ID: activityPtr(uint32(3)), Name: "marta"}, Source: audit.SourceWeb, IP: "203.0.113.12", UserAgent: "Mozilla/5.0 test", }) event := audit.NewEvent("audit.test", audit.CategorySystem) h.recorder.Record(ctx, event) stored := h.all(t) if len(stored) != 1 { t.Fatalf("expected one event, got %d", len(stored)) } got := stored[0] if got.Actor.Type != audit.ActorUser || *got.Actor.ID != 3 || got.Actor.Name != "marta" || got.Source != audit.SourceWeb { t.Errorf("unexpected actor or source %+v", got) } if got.IP == nil || *got.IP != "203.0.113.12" || got.UserAgent == nil || *got.UserAgent != "Mozilla/5.0 test" { t.Errorf("unexpected ip or user agent %v %v", got.IP, got.UserAgent) } if !got.CreatedAt.Equal(h.now) || got.CreatedAt.Location() != time.UTC { t.Errorf("expected created_at %v in UTC, got %v", h.now.UTC(), got.CreatedAt) } }) t.Run("Values already on the event are kept", func(t *testing.T) { h := newActivityHarness(t) ctx := audit.WithOrigin(testCtx, audit.Origin{Actor: audit.AnonymousActor("x"), Source: audit.SourceWeb, IP: "203.0.113.12", UserAgent: "ua"}) event := audit.NewEvent(audit.ActionCleanup, audit.CategorySystem) event.Actor = audit.SystemActor() event.Source = audit.SourceSystem h.recorder.Record(ctx, event) got := h.all(t)[0] if got.Actor.Type != audit.ActorSystem || got.Source != audit.SourceSystem { t.Errorf("the explicit actor and source must win, got %+v", got) } }) t.Run("An event outside the vocabulary is dropped and logged", func(t *testing.T) { h := newActivityHarness(t) event := activityValidEvent() event.Category = "billing" h.recorder.Record(testCtx, event) if len(h.all(t)) != 0 || !strings.Contains(h.logs.String(), "dropped") { t.Errorf("expected nothing stored and a log line, got %q", h.logs.String()) } }) t.Run("Long names are bounded", func(t *testing.T) { h := newActivityHarness(t) event := activityValidEvent() event.Target = audit.Target{Type: "article", ID: "1", Name: strings.Repeat("é", 400)} h.recorder.Record(testCtx, event) if got := h.all(t); len(got) != 1 || len([]rune(got[0].Target.Name)) != 255 { t.Errorf("expected one event with a 255 character target name, got %+v", got) } }) } func activityValidEvent() audit.Event { event := audit.NewEvent("audit.test", audit.CategorySystem) event.Actor = audit.SystemActor() event.Source = audit.SourceSystem return event }