Files
RenewCMS/test/media_env_test.go
T
2026-10-08 02:33:32 +02:00

177 lines
6.2 KiB
Go

package test
import (
"RenewCMS/api"
validator "RenewCMS/api/pkg/validator"
domainArticle "RenewCMS/internal/domain/article"
domainImage "RenewCMS/internal/domain/image"
"RenewCMS/internal/domain/user"
"RenewCMS/internal/infrastructure/config"
"RenewCMS/internal/infrastructure/useCases"
"bytes"
"encoding/json"
"log"
"net/http"
"net/http/httptest"
"strconv"
"strings"
"testing"
"time"
"github.com/go-chi/chi/v5"
"github.com/go-chi/jwtauth/v5"
)
const (
mediaVerifiedUserId uint32 = 1
mediaSecondUserId uint32 = 2
mediaUnverifiedUserId uint32 = 3
mediaStaleUserId uint32 = 4
)
type mediaEnv struct {
articles *mockArticleRepo
images *mockImageRepo
storage *mockImageStorage
users *mockUserRepo
purgeSettings *mockPurgeSettingsRepo
clock time.Time
logs *bytes.Buffer
token *jwtauth.JWTAuth
router http.Handler
}
func newMediaEnv(t *testing.T) *mediaEnv {
t.Helper()
validator.InitValidator()
env := &mediaEnv{
articles: newMockArticleRepo(),
images: newMockImageRepo(),
storage: newMockImageStorage(),
users: newMockUserRepo(),
token: jwtauth.New("HS256", []byte("test-secret"), nil),
purgeSettings: newMockPurgeSettingsRepo(),
clock: time.Date(2026, time.October, 5, 9, 0, 0, 0, time.UTC),
logs: &bytes.Buffer{},
}
env.users.users[mediaVerifiedUserId] = user.User{ID: 1, Username: "ines", DisplayName: "Inès Duarte", IsVerified: true}
env.users.users[mediaSecondUserId] = user.User{ID: 2, Username: "marta", IsVerified: true}
env.users.users[mediaUnverifiedUserId] = user.User{ID: 3, Username: "pending", IsVerified: false}
env.users.users[mediaStaleUserId] = user.User{ID: 4, Username: "stale", IsVerified: true, TokenVersion: 1}
merge := useCases.NewMergeMediaUseCase(env.images, env.articles, env.storage, newMockVariantFileRepo(), discardRecorder)
now := func() time.Time { return env.clock }
container := &api.UseCases{
Token: env.token,
CreateArticleUseCase: useCases.NewCreateArticleUseCase(env.articles, &fakeBodyRenderer{}, discardRecorder),
GetArticleUseCase: useCases.NewGetArticleUseCase(env.articles, &fakeBodyRenderer{}),
ListArticlesUseCase: useCases.NewListArticlesUseCase(env.articles, &fakeBodyRenderer{}),
UpdateArticleUseCase: useCases.NewUpdateArticleUseCase(env.articles, &fakeBodyRenderer{}, discardRecorder),
DeleteArticleUseCase: useCases.NewDeleteArticleUseCase(env.articles, discardRecorder),
GetUserUseCase: useCases.NewGetUserUseCase(env.users),
CreateImageUseCase: useCases.NewCreateImageUseCase(env.images, env.storage, discardRecorder),
ListMediaUseCase: useCases.NewListMediaUseCase(env.images, env.articles, env.users),
RenameMediaUseCase: useCases.NewRenameMediaUseCase(env.images, discardRecorder),
DeleteMediaUseCase: useCases.NewDeleteMediaUseCase(env.images, env.storage, newMockVariantFileRepo(), discardRecorder),
MergeMediaUseCase: merge,
GetMediaUsageUseCase: useCases.NewGetMediaUsageUseCase(env.images, env.articles),
GetPurgeSettingsUseCase: useCases.NewGetPurgeSettingsUseCase(env.purgeSettings),
UpdatePurgeSettingsUseCase: useCases.NewUpdatePurgeSettingsUseCase(env.purgeSettings, now),
PreviewPurgeUseCase: useCases.NewPreviewPurgeUseCase(env.images, env.articles),
RunPurgeUseCase: useCases.NewRunPurgeUseCase(env.images, env.articles, merge, env.purgeSettings, now, log.New(env.logs, "", 0), discardRecorder),
}
router := chi.NewRouter()
router.Mount("/v1", api.InitBackendRoutes(container, config.Settings{}))
env.router = router
return env
}
func (e *mediaEnv) cookie(t *testing.T, userId uint32, tokenVersion uint32) *http.Cookie {
t.Helper()
_, tokenString, err := e.token.Encode(map[string]any{"user_id": userId, "token_version": tokenVersion})
if err != nil {
t.Fatalf("failed to encode jwt: %v", err)
}
return &http.Cookie{Name: "jwt", Value: tokenString}
}
func (e *mediaEnv) serve(t *testing.T, req *http.Request, userId uint32) *httptest.ResponseRecorder {
t.Helper()
if userId != 0 {
req.AddCookie(e.cookie(t, userId, 0))
}
rec := httptest.NewRecorder()
e.router.ServeHTTP(rec, req)
return rec
}
func (e *mediaEnv) send(t *testing.T, method string, path string, body string, userId uint32) *httptest.ResponseRecorder {
t.Helper()
req := httptest.NewRequest(method, path, strings.NewReader(body))
if body != "" {
req.Header.Set("Content-Type", "application/json")
}
return e.serve(t, req, userId)
}
func (e *mediaEnv) seedImage(id uint32, filename string, size int64, contentType string, width int, height int, hash string, uploadedBy uint32) {
extension := map[string]string{"image/jpeg": ".jpeg", "image/svg+xml": ".svg", "image/png": ".png"}[contentType]
e.images.images[id] = domainImage.Image{
ID: id,
Path: "/static/uploadedImages/img-" + strconv.Itoa(int(id)) + "-" + extension,
Filename: filename,
Size: size,
ContentType: contentType,
Width: width,
Height: height,
SHA256: hash,
UploadedByID: uploadedBy,
CreatedAt: mockImageEpoch.Add(time.Duration(id) * time.Minute),
}
if id > e.images.nextID {
e.images.nextID = id
}
}
func (e *mediaEnv) urlOf(id uint32) string {
return e.images.images[id].Path
}
func (e *mediaEnv) seedArticle(id uint32, title string, online bool, body string) {
e.articles.articles[id] = domainArticle.Article{ID: id, Title: title, IsOnline: online, Body: body}
if id > e.articles.nextID {
e.articles.nextID = id
}
}
func (e *mediaEnv) bodies() map[uint32]string {
result := map[uint32]string{}
for id, a := range e.articles.articles {
result[id] = a.Body
}
return result
}
func (e *mediaEnv) imageIds() []uint32 {
ids := make([]uint32, 0)
for _, img := range e.images.sortedImages() {
ids = append(ids, img.ID)
}
return ids
}
func decodeObject(t *testing.T, body string) map[string]any {
t.Helper()
var decoded map[string]any
if err := json.Unmarshal([]byte(body), &decoded); err != nil {
t.Fatalf("failed to decode %q: %v", body, err)
}
return decoded
}
func numberOf(value any) uint32 {
number, _ := value.(float64)
return uint32(number)
}