feat: more role backend and admin/writer roles additions and adjustments

This commit is contained in:
Florian Sylvain committed 2026-10-26 18:15:26 +01:00
1 parent 8c6c6456da
commit de142f6c5f
131 files changed
+2663 -435

No files matched your search

+18 -15
View File
@@ -4,6 +4,7 @@ import (
"RenewCMS/internal/domain/apikey"
"RenewCMS/internal/domain/article"
"RenewCMS/internal/domain/audit"
"RenewCMS/internal/domain/role"
"RenewCMS/internal/domain/user"
"RenewCMS/internal/infrastructure/useCases"
"bytes"
@@ -36,6 +37,7 @@ func newActivityWorld(t *testing.T) *activityWorld {
images: newMockImageRepo(),
keys: newMockAPIKeyRepo(),
}
w.probe.maxReads = 50
w.users.users[1] = user.User{ID: 1, Username: "owner", IsVerified: true, Role: user.RoleOwner}
w.users.users[2] = user.User{ID: 2, Username: "admin", IsVerified: true, Role: user.RoleAdmin}
w.users.users[3] = user.User{ID: 3, Username: "marta", DisplayName: "Marta Alves", IsVerified: true, Role: "editor"}
@@ -62,7 +64,7 @@ func TestActivityListUseCase(t *testing.T) {
recent := w.seedAt(t, 2*day, nil)
w.seedAt(t, 10*day, nil)
page, err := w.list.ListActivity(useCases.ListActivityCommand{ViewerID: 1})
page, err := w.list.ListActivity(useCases.ListActivityCommand{ViewerID: 1, Grants: activityViewAllGrants})
if err != nil || len(page.Items) != 1 || page.Items[0].Event.ID != recent.ID {
t.Fatalf("expected only the recent event, got %+v, %v", page, err)
@@ -94,7 +96,7 @@ func TestActivityListUseCase(t *testing.T) {
w := newActivityWorld(t)
w.seedAt(t, c.ago, nil)
page, err := w.list.ListActivity(useCases.ListActivityCommand{ViewerID: 1, Query: c.query})
page, err := w.list.ListActivity(useCases.ListActivityCommand{ViewerID: 1, Grants: activityViewAllGrants, Query: c.query})
if err != nil || (page.Total == 1) != c.found {
t.Errorf("expected found=%v, got total %d, %v", c.found, page.Total, err)
@@ -115,7 +117,7 @@ func TestActivityListUseCase(t *testing.T) {
for i, query := range queries {
w := newActivityWorld(t)
_, err := w.list.ListActivity(useCases.ListActivityCommand{ViewerID: 1, Query: query})
_, err := w.list.ListActivity(useCases.ListActivityCommand{ViewerID: 1, Grants: activityViewAllGrants, Query: query})
if !errors.Is(err, audit.ErrInvalidFilter) || w.probe.reads != 0 {
t.Errorf("query %d (%+v): expected ErrInvalidFilter without reading, got %v after %d reads", i, query, err, w.probe.reads)
@@ -128,14 +130,14 @@ func TestActivityListUseCase(t *testing.T) {
w.seedAt(t, day, nil)
w.seedAt(t, day, func(e *audit.Event) { e.Outcome = audit.OutcomeFailed })
page, _ := w.list.ListActivity(useCases.ListActivityCommand{ViewerID: 1, Query: useCases.ActivityQuery{Outcome: "all"}})
page, _ := w.list.ListActivity(useCases.ListActivityCommand{ViewerID: 1, Grants: activityViewAllGrants, Query: useCases.ActivityQuery{Outcome: "all"}})
if page.Total != 2 {
t.Errorf("expected both outcomes, got %d", page.Total)
}
})
t.Run("A caller who is not owner or admin is scoped to its own events", func(t *testing.T) {
t.Run("A caller with activity.view at own is scoped to its own events", func(t *testing.T) {
w := newActivityWorld(t)
w.seedAt(t, day, nil)
w.seedAt(t, day, func(e *audit.Event) {
@@ -143,7 +145,7 @@ func TestActivityListUseCase(t *testing.T) {
})
w.seedAt(t, day, func(e *audit.Event) { e.Actor = audit.SystemActor() })
page, err := w.list.ListActivity(useCases.ListActivityCommand{ViewerID: 3})
page, err := w.list.ListActivity(useCases.ListActivityCommand{ViewerID: 3, Grants: activityViewOwnGrants})
if err != nil || page.Total != 1 || *page.Items[0].Event.Actor.ID != 3 {
t.Fatalf("expected only user 3's events, got %+v, %v", page, err)
@@ -155,7 +157,7 @@ func TestActivityListUseCase(t *testing.T) {
for actor, allowed := range cases {
w := newActivityWorld(t)
_, err := w.list.ListActivity(useCases.ListActivityCommand{ViewerID: 3, Query: useCases.ActivityQuery{Actor: actor}})
_, err := w.list.ListActivity(useCases.ListActivityCommand{ViewerID: 3, Grants: activityViewOwnGrants, Query: useCases.ActivityQuery{Actor: actor}})
if allowed && err != nil {
t.Errorf("%s: expected success, got %v", actor, err)
@@ -166,13 +168,13 @@ func TestActivityListUseCase(t *testing.T) {
}
})
t.Run("An unknown viewer is reported", func(t *testing.T) {
t.Run("A caller without activity.view is refused before the repository is read", func(t *testing.T) {
w := newActivityWorld(t)
_, err := w.list.ListActivity(useCases.ListActivityCommand{ViewerID: 99})
_, err := w.list.ListActivity(useCases.ListActivityCommand{ViewerID: 3})
if !errors.Is(err, user.ErrUserNotFound) {
t.Errorf("expected ErrUserNotFound, got %v", err)
if !errors.Is(err, role.ErrMissingPermission) || w.probe.reads != 0 {
t.Errorf("expected a missing permission without reading, got %v after %d reads", err, w.probe.reads)
}
})
@@ -195,7 +197,7 @@ func TestActivityListUseCase(t *testing.T) {
c.seed(w)
w.seedAt(t, day, func(e *audit.Event) { e.Target = c.target })
page, err := w.list.ListActivity(useCases.ListActivityCommand{ViewerID: 1})
page, err := w.list.ListActivity(useCases.ListActivityCommand{ViewerID: 1, Grants: activityViewAllGrants})
if err != nil || len(page.Items) != 1 {
t.Fatalf("unexpected result %+v, %v", page, err)
@@ -209,9 +211,10 @@ func TestActivityListUseCase(t *testing.T) {
t.Run("A repository failure is returned", func(t *testing.T) {
w := newActivityWorld(t)
w.seedAt(t, 24*time.Hour, func(e *audit.Event) { e.Target = audit.Target{Type: "user", ID: "3", Name: "Marta Alves"} })
w.users.fail.failNext("Get")
_, err := w.list.ListActivity(useCases.ListActivityCommand{ViewerID: 1})
_, err := w.list.ListActivity(useCases.ListActivityCommand{ViewerID: 1, Grants: activityViewAllGrants})
if !errors.Is(err, errRepositoryFailure) {
t.Errorf("expected the repository failure, got %v", err)
@@ -307,7 +310,7 @@ func TestActivitySummaryUseCase(t *testing.T) {
func exportToString(t *testing.T, w *activityWorld, query useCases.ActivityQuery) string {
t.Helper()
prepared, err := w.export.Prepare(query)
prepared, err := w.export.Prepare(useCases.ExportActivityCommand{Grants: activityViewAllGrants, Query: query})
if err != nil {
t.Fatalf("prepare failed: %v", err)
}
@@ -445,7 +448,7 @@ func TestActivityExportUseCase(t *testing.T) {
t.Run("An invalid filter is refused before anything is prepared", func(t *testing.T) {
w := newActivityWorld(t)
_, err := w.export.Prepare(useCases.ActivityQuery{Outcome: "maybe"})
_, err := w.export.Prepare(useCases.ExportActivityCommand{Grants: activityViewAllGrants, Query: useCases.ActivityQuery{Outcome: "maybe"}})
if !errors.Is(err, audit.ErrInvalidFilter) || w.probe.reads != 0 {
t.Errorf("expected ErrInvalidFilter without reading, got %v", err)