package test import ( domain "RenewCMS/internal/domain/image" "RenewCMS/internal/infrastructure/useCases" "errors" "net/http" "net/http/httptest" "strings" "testing" ) const uploadedFilename = "photo.png" func (e *variantEnv) seedUploadVariants() { seedVariant(e.variants, "thumb", 400, 300, domain.FitCover, domain.FormatJPEG, 80) seedVariant(e.variants, "hero", 1200, 630, domain.FitCover, domain.FormatJPEG, 82) seedVariant(e.variants, "og", 1200, 630, domain.FitCover, domain.FormatJPEG, 80) seedVariant(e.variants, "card", 800, 0, domain.FitInside, domain.FormatJPEG, 80) e.flagOnUpload("thumb", true) e.flagOnUpload("hero", true) e.flagOnUpload("og", true) e.disable("og") } func (e *variantEnv) flagOnUpload(name string, flag bool) { variant := e.variants.stored(name) variant.GenerateOnUpload = flag e.variants.variants[name] = variant } func (e *variantEnv) upload(filename string, contentType string, content []byte) *httptest.ResponseRecorder { e.t.Helper() request := newMultipartFileRequest(e.t, http.MethodPost, "/v1/media", filename, contentType, content) request.AddCookie(e.cookie(variantOwnerId, 0)) recorder := httptest.NewRecorder() e.admin.ServeHTTP(recorder, request) return recorder } func storedNameOf(t *testing.T, recorder *httptest.ResponseRecorder) string { t.Helper() return strings.TrimPrefix(decodeBody(t, recorder)["url"].(string), domain.PublicPathPrefix) } func TestGenerateVariantsOnUpload(t *testing.T) { t.Run("Flagged and enabled variants are generated before the response", func(t *testing.T) { env := newVariantEnv(t) env.seedUploadVariants() recorder := env.upload(uploadedFilename, "image/png", solidPNG(t, 1000, 800)) stored := storedNameOf(t, recorder) body := decodeBody(t, recorder) thumb, hasThumb, _ := env.files.Find("thumb", stored) hero, hasHero, _ := env.files.Find("hero", stored) _, hasOg, _ := env.files.Find("og", stored) _, hasCard, _ := env.files.Find("card", stored) if recorder.Code != 201 || !hasThumb || !hasHero || hasOg || hasCard || body["variant_count"] != 2.0 || body["variant_size"] != float64(thumb.Size+hero.Size) { t.Fatalf("response = %d %q", recorder.Code, recorder.Body.String()) } if action, _ := env.lastEvent(); env.events() != 1 || action != "media.upload" { t.Errorf("events = %d, last = %s", env.events(), action) } }) t.Run("A failing variant never fails the upload", func(t *testing.T) { env := newVariantEnv(t) env.seedUploadVariants() heroFingerprint := env.variants.stored("hero").Settings().Fingerprint() env.renderer.failFor = func(settings domain.VariantSettings) error { if settings.Fingerprint() == heroFingerprint { return errors.New("hero broke") } return nil } recorder := env.upload(uploadedFilename, "image/png", solidPNG(t, 1000, 800)) stored := storedNameOf(t, recorder) _, hasThumb, _ := env.files.Find("thumb", stored) _, hasHero, _ := env.files.Find("hero", stored) if recorder.Code != 201 || !hasThumb || hasHero || decodeBody(t, recorder)["variant_count"] != 1.0 || len(env.images.images) != 1 { t.Fatalf("response = %d %q", recorder.Code, recorder.Body.String()) } if logged := env.logs.String(); !strings.Contains(logged, "variant=hero") || !strings.Contains(logged, "image=1") { t.Errorf("log = %q", logged) } }) t.Run("A source beyond a cap still uploads", func(t *testing.T) { env := newVariantEnvLimits(t, newCountingRenderer(), useCases.ServeVariantLimits{MaxSourceBytes: variantDefaultCap, MaxSourcePixels: 100, MaxConcurrentRenders: variantDefaultRenders}) env.seedUploadVariants() recorder := env.upload(uploadedFilename, "image/png", solidPNG(t, 1000, 800)) if recorder.Code != 201 || decodeBody(t, recorder)["variant_count"] != 0.0 { t.Errorf("response = %d %q", recorder.Code, recorder.Body.String()) } }) t.Run("An SVG upload generates nothing", func(t *testing.T) { env := newVariantEnv(t) env.seedUploadVariants() recorder := env.upload("logo.svg", "image/svg+xml", []byte(``)) if recorder.Code != 201 || env.renderer.renderCount() != 0 || decodeBody(t, recorder)["variant_count"] != 0.0 { t.Errorf("response = %d %q, renders = %d", recorder.Code, recorder.Body.String(), env.renderer.renderCount()) } }) t.Run("No flagged variant means no render", func(t *testing.T) { env := newVariantEnv(t) env.seedUploadVariants() env.flagOnUpload("thumb", false) env.flagOnUpload("hero", false) recorder := env.upload(uploadedFilename, "image/png", solidPNG(t, 1000, 800)) if recorder.Code != 201 || env.renderer.renderCount() != 0 { t.Errorf("status = %d, renders = %d", recorder.Code, env.renderer.renderCount()) } }) t.Run("A refused upload generates nothing", func(t *testing.T) { env := newVariantEnv(t) env.seedUploadVariants() recorder := env.upload("doc.pdf", "application/pdf", []byte("%PDF-1.4")) if recorder.Code != 415 || env.renderer.renderCount() != 0 { t.Errorf("status = %d, renders = %d", recorder.Code, env.renderer.renderCount()) } }) t.Run("Changing the flag does not backfill existing images: creating a variant", func(t *testing.T) { env := newVariantEnv(t) env.seedGenerateBackground() recorder := env.sendAsAdmin(http.MethodPost, "/v1/image-variants", `{"name": "wide", "width": 900, "generate_on_upload": true}`) if recorder.Code != 201 || env.renderer.renderCount() != 0 || env.files.rowCount() != 0 { t.Errorf("status = %d, renders = %d", recorder.Code, env.renderer.renderCount()) } }) t.Run("Changing the flag does not backfill existing images: updating a variant", func(t *testing.T) { env := newVariantEnv(t) env.seedGenerateBackground() recorder := env.sendAsAdmin(http.MethodPut, "/v1/image-variants/thumb", `{"width": 400, "height": 300, "format": "jpeg", "quality": 80, "generate_on_upload": true}`) if recorder.Code != 200 || env.renderer.renderCount() != 0 || env.files.rowCount() != 0 { t.Errorf("status = %d %q, renders = %d", recorder.Code, recorder.Body.String(), env.renderer.renderCount()) } }) t.Run("Uploading the same bytes again generates files for the new image only", func(t *testing.T) { env := newVariantEnv(t) env.seedUploadVariants() content := solidPNG(t, 1000, 800) first := env.upload(uploadedFilename, "image/png", content) firstStored := storedNameOf(t, first) firstRow, _, _ := env.files.Find("thumb", firstStored) second := env.upload(uploadedFilename, "image/png", content) secondStored := storedNameOf(t, second) secondRow, hasSecond, _ := env.files.Find("thumb", secondStored) keptRow, _, _ := env.files.Find("thumb", firstStored) if second.Code != 201 || secondStored == firstStored || !hasSecond || secondRow.CachedName == firstRow.CachedName || keptRow != firstRow { t.Errorf("first = %q, second = %q", firstStored, secondStored) } }) }