feat(activity): update summary cards to toggles and introduce export menu
This commit is contained in:
1 parent
73f4b539fe
commit
03b39e64f2
30 files changed
+2376
-238
No files matched your search
@@ -0,0 +1,77 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"RenewCMS/api/pkg/apperr"
|
||||
"RenewCMS/internal/domain/audit"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestActivityRangeAll(t *testing.T) {
|
||||
const day = 24 * time.Hour
|
||||
|
||||
t.Run("range=all covers every event", func(t *testing.T) {
|
||||
e := newActivityHTTPEnv(t, 1000)
|
||||
recent := e.seedBy(t, userActor(1, "a"), 2*day)
|
||||
old := e.seedBy(t, userActor(1, "a"), 90*day, func(ev *audit.Event) {
|
||||
ev.Params = map[string]any{"marker": "kept"}
|
||||
})
|
||||
|
||||
got := decodeActivityList(t, e.get("/v1/activity?range=all", asUser(e, activityOwnerID))).ids()
|
||||
if !sameIDs(got, []uint64{recent.ID, old.ID}) {
|
||||
t.Errorf("expected both events listed, got %v", got)
|
||||
}
|
||||
|
||||
raw := e.get("/v1/activity?range=all", asUser(e, activityOwnerID)).Body.String()
|
||||
if !strings.Contains(raw, `"changes":[]`) || strings.Contains(raw, `"changes":null`) || !strings.Contains(raw, `"params":{}`) || !strings.Contains(raw, `"marker":"kept"`) {
|
||||
t.Errorf("expected empty changes arrays, got %s", raw)
|
||||
}
|
||||
|
||||
summary := e.get("/v1/activity/summary?range=all", asUser(e, activityOwnerID))
|
||||
if summary.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d", summary.Code)
|
||||
}
|
||||
var counts map[string]any
|
||||
_ = json.Unmarshal(summary.Body.Bytes(), &counts)
|
||||
if counts["total"] != float64(2) {
|
||||
t.Errorf("expected a summary total of 2, got %v", counts["total"])
|
||||
}
|
||||
|
||||
export := e.get("/v1/activity/export?range=all", asUser(e, activityOwnerID))
|
||||
rows, err := csv.NewReader(strings.NewReader(export.Body.String())).ReadAll()
|
||||
if export.Code != http.StatusOK || err != nil || len(rows) != 3 {
|
||||
t.Errorf("expected header and two rows, got %d %q %v", export.Code, export.Body.String(), err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Omitting range still means 7 days", func(t *testing.T) {
|
||||
e := newActivityHTTPEnv(t, 1000)
|
||||
recent := e.seedBy(t, userActor(1, "a"), 2*day)
|
||||
e.seedBy(t, userActor(1, "a"), 90*day)
|
||||
|
||||
got := decodeActivityList(t, e.get("/v1/activity", asUser(e, activityOwnerID))).ids()
|
||||
|
||||
if !reflect.DeepEqual(got, []uint64{recent.ID}) {
|
||||
t.Errorf("expected only the recent event, got %v", got)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Other invalid ranges still answer 400", func(t *testing.T) {
|
||||
for _, path := range []string{"/v1/activity", "/v1/activity/summary", "/v1/activity/export"} {
|
||||
for _, query := range []string{"range=ALL", "range=everything", "range=year"} {
|
||||
e := newActivityHTTPEnv(t, 1000)
|
||||
assertPlain(t, e.get(path+"?"+query, asUser(e, activityOwnerID)), http.StatusBadRequest, activityMismatch)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("A non-owner export with range=all is still 403", func(t *testing.T) {
|
||||
e := newActivityHTTPEnv(t, 1000)
|
||||
assertPlain(t, e.get("/v1/activity/export?range=all", asUser(e, activityEditorID)), http.StatusForbidden, apperr.OwnerOrAdminRequiredErrorMessage)
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user