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

+123
View File
@@ -0,0 +1,123 @@
package test
import (
"RenewCMS/api/pkg/apperr"
"RenewCMS/internal/domain/user"
"encoding/json"
"net/http"
"net/http/httptest"
"reflect"
"testing"
)
const mediaGetEditorID = 9
func newMediaGetEnv(t *testing.T) *siteEnv {
t.Helper()
e := newSiteEnvWithMain(t)
e.users.users[mediaGetEditorID] = user.User{ID: mediaGetEditorID, Username: "editor", Email: "editor@example.com", IsVerified: true, Role: user.Role("editor")}
e.seedImageRecord(5)
return e
}
func (e *siteEnv) seedImageRecord(id uint32) {
e.seedImage(id)
img := e.images.images[id]
img.Size, img.Width, img.Height, img.SHA256 = 2048, 800, 600, hashA
e.images.images[id] = img
e.seedSpring(1, `<img src="`+img.Path+`">`, true)
}
func TestMediaGetOne(t *testing.T) {
t.Run("Who can read one image", func(t *testing.T) {
cases := []struct {
credentials string
send func(e *siteEnv) *httptest.ResponseRecorder
status int
}{
{"the owner's jwt cookie", func(e *siteEnv) *httptest.ResponseRecorder {
return e.as(t, apiKeyOwnerId, http.MethodGet, "/v1/media/5", "")
}, http.StatusOK},
{"a verified admin's jwt cookie", func(e *siteEnv) *httptest.ResponseRecorder {
return e.as(t, apiKeyDevId, http.MethodGet, "/v1/media/5", "")
}, http.StatusOK},
{"a verified account with another role", func(e *siteEnv) *httptest.ResponseRecorder {
return e.as(t, mediaGetEditorID, http.MethodGet, "/v1/media/5", "")
}, http.StatusOK},
{"a valid API key", func(e *siteEnv) *httptest.ResponseRecorder {
raw, _ := e.issueKey(apiKeyDevId, "Deploy bot")
return e.asKey(t, raw, http.MethodGet, "/v1/media/5", "")
}, http.StatusOK},
{"an unverified account's jwt cookie", func(e *siteEnv) *httptest.ResponseRecorder {
return e.as(t, apiKeyQuinnId, http.MethodGet, "/v1/media/5", "")
}, http.StatusForbidden},
{"no credentials", func(e *siteEnv) *httptest.ResponseRecorder {
return e.anonymous(t, http.MethodGet, "/v1/media/5", "")
}, http.StatusUnauthorized},
}
for _, c := range cases {
t.Run(c.credentials, func(t *testing.T) {
e := newMediaGetEnv(t)
if rec := c.send(e); rec.Code != c.status {
t.Errorf("status = %d, expected %d", rec.Code, c.status)
}
})
}
})
t.Run("The item has the shape of the list", func(t *testing.T) {
e := newMediaGetEnv(t)
single := e.owner(t, http.MethodGet, "/v1/media/5", "")
listed := e.owner(t, http.MethodGet, "/v1/media/", "")
var one map[string]any
var page struct {
Items []map[string]any `json:"items"`
}
if err := json.Unmarshal(single.Body.Bytes(), &one); err != nil {
t.Fatalf("decode item: %v", err)
}
if err := json.Unmarshal(listed.Body.Bytes(), &page); err != nil {
t.Fatalf("decode list: %v", err)
}
var expected map[string]any
for _, item := range page.Items {
if item["id"] == 5.0 {
expected = item
}
}
if expected == nil || !reflect.DeepEqual(one, expected) {
t.Fatalf("item %v, list element %v", one, expected)
}
if one["used_in"] != 1.0 || one["size"] != 2048.0 {
t.Errorf("item = %v", one)
}
})
t.Run("A missing image is not found", func(t *testing.T) {
e := newMediaGetEnv(t)
rec := e.owner(t, http.MethodGet, "/v1/media/999", "")
expectSiteStatusAndBody(t, rec, http.StatusNotFound, apperr.ImageNotFoundErrorMessage)
})
t.Run("A malformed id is refused", func(t *testing.T) {
for _, id := range []string{"abc", "-1", "4294967296", "1.5"} {
e := newMediaGetEnv(t)
rec := e.owner(t, http.MethodGet, "/v1/media/"+id, "")
expectSiteStatusAndBody(t, rec, http.StatusBadRequest, apperr.IdUint32ErrorMessage)
}
})
t.Run("Reading one image records nothing", func(t *testing.T) {
e := newMediaGetEnv(t)
e.owner(t, http.MethodGet, "/v1/media/5", "")
e.noEvent(t)
})
t.Run("The route does not shadow the usage route", func(t *testing.T) {
e := newMediaGetEnv(t)
rec := e.owner(t, http.MethodGet, "/v1/media/5/usage", "")
if rec.Code != http.StatusOK || rec.Body.String() == "" {
t.Fatalf("status %d", rec.Code)
}
})
}