Files
RenewCMS/test/image_test.go
T
2026-09-30 18:35:28 +02:00

226 lines
5.6 KiB
Go

package test
import (
imageDomain "RenewCMS/internal/domain/image"
"RenewCMS/internal/infrastructure/useCases"
"errors"
"io"
"sort"
"strconv"
"strings"
"testing"
"time"
)
var mockImageEpoch = time.Date(2026, time.June, 1, 12, 0, 0, 0, time.UTC)
var mockImageExtensions = map[string]string{
"image/png": ".png",
"image/jpeg": ".jpeg",
"image/webp": ".webp",
"image/svg+xml": ".svg",
}
type mockImageRepo struct {
images map[uint32]imageDomain.Image
nextID uint32
fail failSwitch
failDeleteFor map[uint32]error
deleteCalls []uint32
createCalls int
lastContent []byte
}
func newMockImageRepo() *mockImageRepo {
return &mockImageRepo{images: make(map[uint32]imageDomain.Image)}
}
func (m *mockImageRepo) sortedImages() []imageDomain.Image {
result := make([]imageDomain.Image, 0, len(m.images))
for _, img := range m.images {
result = append(result, img)
}
sort.Slice(result, func(i, j int) bool { return result[i].ID < result[j].ID })
return result
}
func (m *mockImageRepo) Create(input imageDomain.ImageInput) (imageDomain.Image, error) {
m.createCalls++
if err := m.fail.trigger("Create"); err != nil {
return imageDomain.Image{}, err
}
if input.Content != nil {
m.lastContent, _ = io.ReadAll(input.Content)
}
for m.images[m.nextID+1].ID != 0 {
m.nextID++
}
m.nextID++
img := imageDomain.Image{
ID: m.nextID,
Path: "/static/uploadedImages/" + strconv.Itoa(int(m.nextID)) + mockImageExtensions[input.ContentType],
Filename: input.Filename,
Size: input.Size,
ContentType: input.ContentType,
Width: input.Width,
Height: input.Height,
SHA256: input.SHA256,
UploadedByID: input.UploadedByID,
CreatedAt: mockImageEpoch.Add(time.Duration(m.nextID) * time.Minute),
}
m.images[img.ID] = img
return img, nil
}
func (m *mockImageRepo) Get(id uint32) (imageDomain.Image, error) {
if err := m.fail.trigger("Get"); err != nil {
return imageDomain.Image{}, err
}
img, ok := m.images[id]
if !ok {
return imageDomain.Image{}, imageDomain.ErrImageNotFound
}
return img, nil
}
func (m *mockImageRepo) Delete(id uint32) error {
m.deleteCalls = append(m.deleteCalls, id)
if err := m.fail.trigger("Delete"); err != nil {
return err
}
if err, ok := m.failDeleteFor[id]; ok {
return err
}
if _, ok := m.images[id]; !ok {
return imageDomain.ErrImageNotFound
}
delete(m.images, id)
return nil
}
func (m *mockImageRepo) List(query imageDomain.SearchQuery) ([]imageDomain.Image, error) {
if err := m.fail.trigger("List"); err != nil {
return nil, err
}
result := make([]imageDomain.Image, 0)
for _, img := range m.sortedImages() {
if query.Search != "" && !strings.Contains(strings.ToLower(img.Filename), strings.ToLower(query.Search)) {
continue
}
if query.Hash != "" && img.SHA256 != query.Hash {
continue
}
result = append(result, img)
}
return result, nil
}
func (m *mockImageRepo) FindByHash(hash string) ([]imageDomain.Image, error) {
if err := m.fail.trigger("FindByHash"); err != nil {
return nil, err
}
result := make([]imageDomain.Image, 0)
for _, img := range m.sortedImages() {
if hash != "" && img.SHA256 == hash {
result = append(result, img)
}
}
return result, nil
}
func (m *mockImageRepo) Rename(id uint32, filename string) (imageDomain.Image, error) {
if err := m.fail.trigger("Rename"); err != nil {
return imageDomain.Image{}, err
}
img, ok := m.images[id]
if !ok {
return imageDomain.Image{}, imageDomain.ErrImageNotFound
}
img.Filename = filename
m.images[id] = img
return img, nil
}
func (m *mockImageRepo) hashCounts() map[string]int {
counts := map[string]int{}
for _, img := range m.images {
if img.SHA256 != "" {
counts[img.SHA256]++
}
}
return counts
}
func (m *mockImageRepo) ListDuplicates() ([]imageDomain.Image, error) {
if err := m.fail.trigger("ListDuplicates"); err != nil {
return nil, err
}
counts := m.hashCounts()
result := make([]imageDomain.Image, 0)
for _, img := range m.sortedImages() {
if img.SHA256 != "" && counts[img.SHA256] > 1 {
result = append(result, img)
}
}
return result, nil
}
func (m *mockImageRepo) Counts() (imageDomain.Counts, error) {
if err := m.fail.trigger("Counts"); err != nil {
return imageDomain.Counts{}, err
}
groups := 0
for _, count := range m.hashCounts() {
if count > 1 {
groups++
}
}
return imageDomain.Counts{All: len(m.images), DuplicateGroups: groups}, nil
}
var _ imageDomain.Repository = &mockImageRepo{}
func TestImageUseCases(t *testing.T) {
repo := newMockImageRepo()
createUc := useCases.NewCreateImageUseCase(repo)
deleteUc := useCases.NewDeleteMediaUseCase(repo)
t.Run("Create Image", func(t *testing.T) {
input := imageDomain.ImageInput{
Content: strings.NewReader(string(pngBytes(t, 3, 2))),
Filename: "test.png",
ContentType: "image/png",
}
res, err := createUc.CreateImage(input)
if err != nil {
t.Fatalf("CreateImage failed: %v", err)
}
if res.ID == 0 {
t.Errorf("Expected valid ID, got 0")
}
if res.Filename != "test.png" {
t.Errorf("Filename mismatch: %s", res.Filename)
}
})
t.Run("Delete Image", func(t *testing.T) {
repo.images[99] = imageDomain.Image{ID: 99, Path: "/static/delete-me.png"}
err := deleteUc.Delete(99)
if err != nil {
t.Fatalf("Delete failed: %v", err)
}
if _, exists := repo.images[99]; exists {
t.Errorf("Image 99 should have been deleted")
}
})
t.Run("Delete unknown image", func(t *testing.T) {
if err := deleteUc.Delete(12345); !errors.Is(err, imageDomain.ErrImageNotFound) {
t.Errorf("expected ErrImageNotFound, got %v", err)
}
})
}