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

117 lines
3.9 KiB
Go

package test
import (
domain "RenewCMS/internal/domain/image"
"RenewCMS/internal/infrastructure/useCases"
"errors"
"io"
"testing"
)
type understatedSizeVariantStorage struct {
*mockVariantStorage
}
func (s understatedSizeVariantStorage) Open(storedName string) (domain.Blob, error) {
blob, err := s.mockVariantStorage.Open(storedName)
blob.Size = 1
return blob, err
}
type sizingVariantRenderer struct {
received int
calls int
}
func (r *sizingVariantRenderer) Render(source io.Reader, settings domain.VariantSettings) (domain.RenderedVariant, error) {
data, err := io.ReadAll(source)
r.received = len(data)
r.calls++
return domain.RenderedVariant{Content: []byte("rendered"), ContentType: "image/png"}, err
}
func (r *sizingVariantRenderer) DeclaredSize(source io.Reader) (int, int, error) {
return 1, 1, nil
}
func (r *sizingVariantRenderer) EncodableFormats() []domain.VariantFormat {
return []domain.VariantFormat{domain.FormatOriginal, domain.FormatJPEG, domain.FormatPNG}
}
type lateStaleVariantFileRepo struct {
*mockVariantFileRepo
finds int
stale domain.VariantFile
}
func (r *lateStaleVariantFileRepo) Find(variantName string, sourceName string) (domain.VariantFile, bool, error) {
r.finds++
if r.finds == 1 {
return domain.VariantFile{}, false, nil
}
return r.stale, true, nil
}
func TestImageVariantsServeInternals(t *testing.T) {
const cap = 1000
serve := func(storage domain.Storage, files domain.VariantFileRepository, renderer domain.VariantRenderer, variants *mockVariantRepo) (useCases.ServedVariant, error) {
name := "thumb"
images := newMockImageRepo()
images.images[1] = domain.Image{ID: 1, Path: domain.PublicPath("a.png")}
return useCases.NewServeVariantUseCase(variants, files, images, storage, renderer, defaultServeLimits(cap)).Serve(useCases.ServeVariantRequest{StoredName: "a.png", Variant: &name})
}
t.Run("A streamed source of exactly the cap is rendered whole", func(t *testing.T) {
variants := newMockVariantRepo()
seedVariant(variants, "thumb", 400, 300, domain.FitCover, domain.FormatJPEG, 80)
storage := newMockVariantStorage()
storage.seed("a.png", make([]byte, cap))
renderer := &sizingVariantRenderer{}
_, err := serve(understatedSizeVariantStorage{storage}, newMockVariantFileRepo(), renderer, variants)
if err != nil || renderer.calls != 1 || renderer.received != cap {
t.Errorf("err = %v, calls = %d, received = %d", err, renderer.calls, renderer.received)
}
})
t.Run("A streamed source beyond the cap is rejected even when its reported size is small", func(t *testing.T) {
variants := newMockVariantRepo()
seedVariant(variants, "thumb", 400, 300, domain.FitCover, domain.FormatJPEG, 80)
storage := newMockVariantStorage()
storage.seed("a.png", make([]byte, cap+1))
renderer := &sizingVariantRenderer{}
_, err := serve(understatedSizeVariantStorage{storage}, newMockVariantFileRepo(), renderer, variants)
if !errors.Is(err, domain.ErrSourceTooLarge) || renderer.calls != 0 {
t.Errorf("err = %v, calls = %d", err, renderer.calls)
}
})
t.Run("A stale row that appears during generation is regenerated, not served", func(t *testing.T) {
variants := newMockVariantRepo()
seedVariant(variants, "thumb", 400, 300, domain.FitCover, domain.FormatJPEG, 80)
storage := newMockVariantStorage()
storage.seed("a.png", []byte("source"))
storage.seed("old.png", []byte("old"))
files := &lateStaleVariantFileRepo{
mockVariantFileRepo: newMockVariantFileRepo(),
stale: domain.VariantFile{VariantName: "thumb", SourceName: "a.png", CachedName: "old.png", ContentType: "image/png", Size: 3, Fingerprint: "stale"},
}
renderer := &sizingVariantRenderer{}
served, err := serve(storage, files, renderer, variants)
if err != nil || renderer.calls != 1 {
t.Fatalf("err = %v, calls = %d", err, renderer.calls)
}
defer served.Content.Close()
body, _ := io.ReadAll(served.Content)
if string(body) != "rendered" {
t.Errorf("body = %q", body)
}
})
}