package test import ( domain "RenewCMS/internal/domain/image" "bytes" "errors" "strings" "testing" "time" ) func TestVariantNames(t *testing.T) { rows := []struct { name string valid bool }{ {"ab", true}, {"a-b-c", true}, {"q1", true}, {strings.Repeat("a", 32), true}, {strings.Repeat("a", 33), false}, {"a", false}, {"", false}, {"Hero", false}, {"he ro", false}, {"he_ro", false}, {"hé", false}, } for _, row := range rows { t.Run("name "+row.name, func(t *testing.T) { if got := domain.IsValidVariantName(row.name); got != row.valid { t.Errorf("valid = %v", got) } }) } } func TestVariantSettingsNormalization(t *testing.T) { settings := func(width *int, height *int, quality *int, format domain.VariantFormat) domain.VariantSettings { return domain.VariantSettings{Width: width, Height: height, Quality: quality, Format: format} } valid := []struct { name string in domain.VariantSettings }{ {"width 1", settings(intPointer(1), nil, nil, "")}, {"width 8000", settings(intPointer(8000), nil, nil, "")}, {"height 1", settings(nil, intPointer(1), nil, "")}, {"height 8000", settings(nil, intPointer(8000), nil, "")}, {"quality 1", settings(intPointer(5), nil, intPointer(1), "")}, {"quality 100", settings(intPointer(5), nil, intPointer(100), "")}, } for _, row := range valid { t.Run("accepts "+row.name, func(t *testing.T) { if _, err := row.in.Normalized(); err != nil { t.Errorf("err = %v", err) } }) } invalid := []struct { name string in domain.VariantSettings }{ {"no dimension", settings(nil, nil, nil, "")}, {"width 0", settings(intPointer(0), nil, nil, "")}, {"width 8001", settings(intPointer(8001), nil, nil, "")}, {"height 0", settings(nil, intPointer(0), nil, "")}, {"height 8001", settings(nil, intPointer(8001), nil, "")}, {"height negative", settings(intPointer(10), intPointer(-1), nil, "")}, {"quality 0", settings(intPointer(5), nil, intPointer(0), "")}, {"quality 101", settings(intPointer(5), nil, intPointer(101), "")}, {"unknown format", settings(intPointer(5), nil, nil, "avif")}, {"unknown fit", domain.VariantSettings{Width: intPointer(5), Fit: "stretch"}}, } for _, row := range invalid { t.Run("refuses "+row.name, func(t *testing.T) { if _, err := row.in.Normalized(); !errors.Is(err, domain.ErrVariantInvalid) { t.Errorf("err = %v", err) } }) } t.Run("Defaults are cover, original and quality 82", func(t *testing.T) { normalized, err := settings(intPointer(400), nil, nil, "").Normalized() if err != nil || normalized.Fit != domain.FitCover || normalized.Format != domain.FormatOriginal || *normalized.Quality != 82 { t.Errorf("normalized = %+v, err = %v", normalized, err) } }) t.Run("A png variant stores no quality", func(t *testing.T) { normalized, _ := settings(intPointer(400), nil, intPointer(30), domain.FormatPNG).Normalized() if normalized.Quality != nil { t.Errorf("quality = %v", *normalized.Quality) } }) t.Run("Normalizing does not alter the input", func(t *testing.T) { quality := 40 input := settings(intPointer(400), nil, &quality, domain.FormatPNG) _, _ = input.Normalized() if input.Quality == nil || *input.Quality != 40 { t.Error("the input quality was cleared") } }) } func TestVariantFingerprint(t *testing.T) { base := domain.VariantSettings{Width: intPointer(400), Height: intPointer(300), Fit: domain.FitCover, Format: domain.FormatJPEG, Quality: intPointer(80)} variations := map[string]domain.VariantSettings{ "width": {Width: intPointer(401), Height: intPointer(300), Fit: domain.FitCover, Format: domain.FormatJPEG, Quality: intPointer(80)}, "height": {Width: intPointer(400), Height: nil, Fit: domain.FitCover, Format: domain.FormatJPEG, Quality: intPointer(80)}, "fit": {Width: intPointer(400), Height: intPointer(300), Fit: domain.FitInside, Format: domain.FormatJPEG, Quality: intPointer(80)}, "format": {Width: intPointer(400), Height: intPointer(300), Fit: domain.FitCover, Format: domain.FormatWebP, Quality: intPointer(80)}, "quality": {Width: intPointer(400), Height: intPointer(300), Fit: domain.FitCover, Format: domain.FormatJPEG, Quality: intPointer(79)}, } t.Run("Equal settings share a fingerprint", func(t *testing.T) { same := domain.VariantSettings{Width: intPointer(400), Height: intPointer(300), Fit: domain.FitCover, Format: domain.FormatJPEG, Quality: intPointer(80)} if !base.IsSameAs(same) { t.Error("fingerprints differ") } }) for field, changed := range variations { t.Run("A different "+field+" changes the fingerprint", func(t *testing.T) { if base.IsSameAs(changed) { t.Error("fingerprints match") } }) } t.Run("An absent dimension differs from a present one", func(t *testing.T) { widthOnly := domain.VariantSettings{Width: intPointer(300)} heightOnly := domain.VariantSettings{Height: intPointer(300)} if widthOnly.IsSameAs(heightOnly) { t.Error("fingerprints match") } }) } func TestVariantConstruction(t *testing.T) { now := time.Date(2026, 10, 8, 9, 0, 0, 0, time.UTC) t.Run("A new variant carries the normalized settings and timestamps", func(t *testing.T) { variant, err := domain.NewVariant("hero", domain.VariantSettings{Width: intPointer(100), Format: domain.FormatPNG, Quality: intPointer(5)}, true, false, now) if err != nil || variant.Quality != nil || variant.Fit != domain.FitCover || !variant.GenerateOnUpload || variant.Enabled || !variant.CreatedAt.Equal(now) || !variant.UpdatedAt.Equal(now) { t.Errorf("variant = %+v, err = %v", variant, err) } }) t.Run("A new variant refuses an invalid name", func(t *testing.T) { _, err := domain.NewVariant("A", domain.VariantSettings{Width: intPointer(100)}, false, true, now) if !errors.Is(err, domain.ErrVariantInvalid) { t.Errorf("err = %v", err) } }) } func TestVariantETagAndRequestErrors(t *testing.T) { settings := domain.VariantSettings{Width: intPointer(400), Fit: domain.FitCover, Format: domain.FormatJPEG, Quality: intPointer(80)} t.Run("The ETag is quoted and depends on the source", func(t *testing.T) { first, second := domain.VariantETag(settings, "a.png"), domain.VariantETag(settings, "b.png") if !strings.HasPrefix(first, `"`) || !strings.HasSuffix(first, `"`) || first == second || first != domain.VariantETag(settings, "a.png") { t.Errorf("etags = %q %q", first, second) } }) matches := []struct { header string want bool }{ {`"abc"`, true}, {`W/"abc"`, true}, {`"x", "abc"`, true}, {`*`, true}, {`"abd"`, false}, {``, false}, } for _, row := range matches { t.Run("If-None-Match "+row.header, func(t *testing.T) { if got := domain.ETagMatches(row.header, `"abc"`); got != row.want { t.Errorf("matches = %v", got) } }) } t.Run("Request errors unwrap to their sentinel and carry the detail", func(t *testing.T) { unknown := domain.NewUnknownVariantError("banner") unsupported := domain.NewUnsupportedParameterError("width") if !errors.Is(unknown, domain.ErrUnknownVariant) || errors.Is(unknown, domain.ErrUnsupportedParameter) || domain.VariantRequestDetail(unknown) != "banner" { t.Errorf("unknown = %v", unknown) } if !errors.Is(unsupported, domain.ErrUnsupportedParameter) || domain.VariantRequestDetail(unsupported) != "width" || domain.VariantRequestDetail(errors.New("other")) != "" { t.Errorf("unsupported = %v", unsupported) } }) } func TestAnimatedGIFDetection(t *testing.T) { t.Run("A two-frame GIF is animated", func(t *testing.T) { animated, err := domain.IsAnimatedGIF(bytes.NewReader(animatedGIF(t))) if err != nil || !animated { t.Errorf("animated = %v, err = %v", animated, err) } }) t.Run("A one-frame GIF is not animated", func(t *testing.T) { animated, err := domain.IsAnimatedGIF(bytes.NewReader(stillGIF(t))) if err != nil || animated { t.Errorf("animated = %v, err = %v", animated, err) } }) t.Run("Bytes that are not a GIF are refused", func(t *testing.T) { _, err := domain.IsAnimatedGIF(bytes.NewReader(solidPNG(t, 4, 4))) if !errors.Is(err, domain.ErrNotAGif) { t.Errorf("err = %v", err) } }) t.Run("A truncated GIF reports an error", func(t *testing.T) { truncated := animatedGIF(t)[:20] if _, err := domain.IsAnimatedGIF(bytes.NewReader(truncated)); err == nil { t.Error("expected an error") } }) }