feat: change email, export my data, delete account (with data anonymization)

This commit is contained in:
Florian Sylvain committed 2026-10-30 20:19:07 +01:00
1 parent bb191c389a
commit 10a66cd979
123 files changed
+10789 -76

No files matched your search

+100
View File
@@ -0,0 +1,100 @@
package test
import (
"RenewCMS/api/middleware"
"net/http"
"net/http/httptest"
"testing"
"time"
)
func TestAccountExportDataEmptyCollectionsAreArrays(t *testing.T) {
f := newExportFixture(t)
for index := range f.events.events {
f.events.events[index].Params = nil
}
rec := f.export(t, exportMartaID)
files := openExportArchive(t, rec)
for _, item := range exportList(t, exportJSON(t, files, "authored-content.json"), "articles") {
authors, ok := item.(map[string]any)["authors"].([]any)
if !ok || len(authors) != 0 {
t.Errorf("uncredited article authors = %v, want empty list", item.(map[string]any)["authors"])
}
}
events := exportList(t, exportJSON(t, files, "account-activity.json"), "events")
if len(events) == 0 {
t.Fatal("expected activity events")
}
for _, item := range events {
params, ok := item.(map[string]any)["params"].(map[string]any)
if !ok || len(params) != 0 {
t.Errorf("event params = %v, want empty object", item.(map[string]any)["params"])
}
}
}
func successLimiterStatus(t *testing.T, handler http.Handler) *httptest.ResponseRecorder {
t.Helper()
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/", nil))
return rec
}
func newSuccessLimiterHandler(clock *exportClock, status *int) http.Handler {
limited := middleware.NewUserSuccessLimiterAt(time.Minute, clock.now)
return limited(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(*status)
}))
}
func TestUserSuccessLimiterStatusBoundary(t *testing.T) {
cases := map[string]struct {
status int
blocked bool
}{
"399 keeps the slot": {399, true},
"400 releases the slot": {400, false},
"500 releases the slot": {500, false},
"200 keeps the slot": {200, true},
"302 keeps the slot": {302, true},
"429 releases the slot": {429, false},
"401 releases the slot": {401, false},
"204 keeps the slot too": {204, true},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
clock := &exportClock{current: time.Date(2026, time.October, 29, 9, 0, 0, 0, time.UTC)}
status := tc.status
handler := newSuccessLimiterHandler(clock, &status)
successLimiterStatus(t, handler)
second := successLimiterStatus(t, handler)
if tc.blocked && second.Code != http.StatusTooManyRequests {
t.Errorf("second call = %d, want 429", second.Code)
}
if !tc.blocked && second.Code != tc.status {
t.Errorf("second call = %d, want handler status %d", second.Code, tc.status)
}
})
}
}
func TestUserSuccessLimiterWindowEdge(t *testing.T) {
clock := &exportClock{current: time.Date(2026, time.October, 29, 9, 0, 0, 0, time.UTC)}
status := http.StatusOK
handler := newSuccessLimiterHandler(clock, &status)
successLimiterStatus(t, handler)
clock.advance(time.Minute - time.Second)
blocked := successLimiterStatus(t, handler)
if blocked.Code != http.StatusTooManyRequests || blocked.Header().Get("Retry-After") != "1" {
t.Errorf("just inside the window = %d retry-after %q, want 429 and 1", blocked.Code, blocked.Header().Get("Retry-After"))
}
clock.advance(time.Second)
if edge := successLimiterStatus(t, handler); edge.Code != http.StatusOK {
t.Errorf("exactly at the window = %d, want 200", edge.Code)
}
}