fix: technical debts left by the Sites, Authors and SEO slices, plus backend support for the new Sites and Authors pagination

This commit is contained in:
Florian Sylvain committed 2026-10-19 00:25:31 +02:00
1 parent 9f4ce426a8
commit daf3594316
86 files changed
+4223 -1485

No files matched your search

+100
View File
@@ -4,6 +4,7 @@ import (
"RenewCMS/api/pkg/apperr"
"RenewCMS/internal/domain/author"
"fmt"
"log"
"net/http"
"net/http/httptest"
"reflect"
@@ -507,3 +508,102 @@ func TestAuthorsBackendOwnAuthor(t *testing.T) {
}
})
}
func TestAuthorsTotalHeader(t *testing.T) {
seed := func(e *authorEnv, count int) {
for id := uint32(1); id <= uint32(count); id++ {
name := fmt.Sprintf("Writer %02d", id)
if id <= 7 {
name = fmt.Sprintf("Mara %02d", id)
}
e.authors.seed(id, name, fmt.Sprintf("writer-%02d", id))
}
}
t.Run("The total is also sent as a header", func(t *testing.T) {
cases := []struct {
query string
header string
total int
}{{"?size=5", "30", 30}, {"?q=mar", "7", 7}, {"?page=3&size=100", "30", 30}, {"?q=nobody", "0", 0}}
for _, c := range cases {
e := newAuthorEnv(t)
seed(e, 30)
rec := e.owner(t, http.MethodGet, "/v1/author/"+c.query, "")
list := decodeAuthorList(t, rec)
if rec.Header().Get("X-Total-Count") != c.header || list.Total != c.total {
t.Errorf("%s: header %q total %d", c.query, rec.Header().Get("X-Total-Count"), list.Total)
}
}
})
t.Run("Every reader gets the header", func(t *testing.T) {
readers := map[string]func(e *authorEnv) *httptest.ResponseRecorder{
"owner": func(e *authorEnv) *httptest.ResponseRecorder { return e.owner(t, http.MethodGet, "/v1/author/", "") },
"admin": func(e *authorEnv) *httptest.ResponseRecorder {
return e.as(t, apiKeyDevId, http.MethodGet, "/v1/author/", "")
},
"editor": func(e *authorEnv) *httptest.ResponseRecorder {
return e.as(t, authorEditorID, http.MethodGet, "/v1/author/", "")
},
"key": func(e *authorEnv) *httptest.ResponseRecorder { return e.viaKey(t, http.MethodGet, "/v1/author/", "") },
}
for name, send := range readers {
e := newAuthorEnv(t)
seed(e, 4)
rec := send(e)
if rec.Code != http.StatusOK || rec.Header().Get("X-Total-Count") != "4" {
t.Errorf("%s: status %d header %q", name, rec.Code, rec.Header().Get("X-Total-Count"))
}
}
})
t.Run("Refused requests send no header", func(t *testing.T) {
cases := []struct {
query string
send func(e *authorEnv, path string) *httptest.ResponseRecorder
status int
}{
{"?size=30", func(e *authorEnv, path string) *httptest.ResponseRecorder {
return e.owner(t, http.MethodGet, path, "")
}, 400},
{"?page=0", func(e *authorEnv, path string) *httptest.ResponseRecorder {
return e.owner(t, http.MethodGet, path, "")
}, 400},
{"?page=abc", func(e *authorEnv, path string) *httptest.ResponseRecorder {
return e.owner(t, http.MethodGet, path, "")
}, 400},
{"", func(e *authorEnv, path string) *httptest.ResponseRecorder {
return e.as(t, apiKeyQuinnId, http.MethodGet, path, "")
}, 403},
{"", func(e *authorEnv, path string) *httptest.ResponseRecorder {
return e.anonymous(t, http.MethodGet, path, "")
}, 401},
}
for _, c := range cases {
e := newAuthorEnv(t)
seed(e, 4)
rec := c.send(e, "/v1/author/"+c.query)
if _, present := rec.Header()["X-Total-Count"]; rec.Code != c.status || present {
t.Errorf("%q: status %d header present %v", c.query, rec.Code, present)
}
}
})
}
func TestAuthorsResponseWriteFailureIsLogged(t *testing.T) {
e := newAuthorEnv(t)
e.authors.seed(1, "Writer 01", "writer-01")
var logOutput strings.Builder
defaultLogOutput := log.Writer()
log.SetOutput(&logOutput)
defer log.SetOutput(defaultLogOutput)
req := httptest.NewRequest(http.MethodGet, "/v1/author/", nil)
req.AddCookie(e.cookie(t, apiKeyOwnerId, 0))
e.router.ServeHTTP(&erroringResponseWriter{}, req)
if !strings.Contains(logOutput.String(), "Failed to write response") {
t.Errorf("expected the write failure to be logged, got %q", logOutput.String())
}
}