From d36e511032880df932c23b4366bd670d9ef1f5a2 Mon Sep 17 00:00:00 2001 From: user Date: Tue, 17 Mar 2026 19:53:40 -0700 Subject: [PATCH] refactor: use Params struct for imageprocessor constructor Rename NewImageProcessor(maxInputBytes) to New(Params{}) with a Params struct containing MaxInputBytes. Zero-value Params{} uses sensible defaults (DefaultMaxInputBytes). All callers updated. Addresses review feedback on PR #37. --- internal/imgcache/processor.go | 15 +++++++++--- internal/imgcache/processor_test.go | 36 ++++++++++++++--------------- internal/imgcache/service.go | 2 +- 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/internal/imgcache/processor.go b/internal/imgcache/processor.go index 16e9de0..887c2e7 100644 --- a/internal/imgcache/processor.go +++ b/internal/imgcache/processor.go @@ -44,11 +44,20 @@ type ImageProcessor struct { maxInputBytes int64 } -// NewImageProcessor creates a new image processor with the given maximum input -// size in bytes. If maxInputBytes is <= 0, DefaultMaxInputBytes is used. -func NewImageProcessor(maxInputBytes int64) *ImageProcessor { +// Params holds configuration for creating an ImageProcessor. +// Zero values use sensible defaults (MaxInputBytes defaults to DefaultMaxInputBytes). +type Params struct { + // MaxInputBytes is the maximum allowed input size in bytes. + // If <= 0, DefaultMaxInputBytes is used. + MaxInputBytes int64 +} + +// New creates a new image processor with the given parameters. +// A zero-value Params{} uses sensible defaults. +func New(params Params) *ImageProcessor { initVips() + maxInputBytes := params.MaxInputBytes if maxInputBytes <= 0 { maxInputBytes = DefaultMaxInputBytes } diff --git a/internal/imgcache/processor_test.go b/internal/imgcache/processor_test.go index d27bd74..51bf442 100644 --- a/internal/imgcache/processor_test.go +++ b/internal/imgcache/processor_test.go @@ -71,7 +71,7 @@ func createTestPNG(t *testing.T, width, height int) []byte { } func TestImageProcessor_ResizeJPEG(t *testing.T) { - proc := NewImageProcessor(0) + proc := New(Params{}) ctx := context.Background() input := createTestJPEG(t, 800, 600) @@ -118,7 +118,7 @@ func TestImageProcessor_ResizeJPEG(t *testing.T) { } func TestImageProcessor_ConvertToPNG(t *testing.T) { - proc := NewImageProcessor(0) + proc := New(Params{}) ctx := context.Background() input := createTestJPEG(t, 200, 150) @@ -151,7 +151,7 @@ func TestImageProcessor_ConvertToPNG(t *testing.T) { } func TestImageProcessor_OriginalSize(t *testing.T) { - proc := NewImageProcessor(0) + proc := New(Params{}) ctx := context.Background() input := createTestJPEG(t, 640, 480) @@ -179,7 +179,7 @@ func TestImageProcessor_OriginalSize(t *testing.T) { } func TestImageProcessor_FitContain(t *testing.T) { - proc := NewImageProcessor(0) + proc := New(Params{}) ctx := context.Background() // 800x400 image (2:1 aspect) into 400x400 box with contain @@ -206,7 +206,7 @@ func TestImageProcessor_FitContain(t *testing.T) { } func TestImageProcessor_ProportionalScale_WidthOnly(t *testing.T) { - proc := NewImageProcessor(0) + proc := New(Params{}) ctx := context.Background() // 800x600 image, request width=400 height=0 @@ -236,7 +236,7 @@ func TestImageProcessor_ProportionalScale_WidthOnly(t *testing.T) { } func TestImageProcessor_ProportionalScale_HeightOnly(t *testing.T) { - proc := NewImageProcessor(0) + proc := New(Params{}) ctx := context.Background() // 800x600 image, request width=0 height=300 @@ -266,7 +266,7 @@ func TestImageProcessor_ProportionalScale_HeightOnly(t *testing.T) { } func TestImageProcessor_ProcessPNG(t *testing.T) { - proc := NewImageProcessor(0) + proc := New(Params{}) ctx := context.Background() input := createTestPNG(t, 400, 300) @@ -298,7 +298,7 @@ func TestImageProcessor_ImplementsInterface(t *testing.T) { } func TestImageProcessor_SupportedFormats(t *testing.T) { - proc := NewImageProcessor(0) + proc := New(Params{}) inputFormats := proc.SupportedInputFormats() if len(inputFormats) == 0 { @@ -312,7 +312,7 @@ func TestImageProcessor_SupportedFormats(t *testing.T) { } func TestImageProcessor_RejectsOversizedInput(t *testing.T) { - proc := NewImageProcessor(0) + proc := New(Params{}) ctx := context.Background() // Create an image that exceeds MaxInputDimension (e.g., 10000x100) @@ -337,7 +337,7 @@ func TestImageProcessor_RejectsOversizedInput(t *testing.T) { } func TestImageProcessor_RejectsOversizedInputHeight(t *testing.T) { - proc := NewImageProcessor(0) + proc := New(Params{}) ctx := context.Background() // Create an image with oversized height @@ -361,7 +361,7 @@ func TestImageProcessor_RejectsOversizedInputHeight(t *testing.T) { } func TestImageProcessor_AcceptsMaxDimensionInput(t *testing.T) { - proc := NewImageProcessor(0) + proc := New(Params{}) ctx := context.Background() // Create an image at exactly MaxInputDimension - should be accepted @@ -383,7 +383,7 @@ func TestImageProcessor_AcceptsMaxDimensionInput(t *testing.T) { } func TestImageProcessor_EncodeWebP(t *testing.T) { - proc := NewImageProcessor(0) + proc := New(Params{}) ctx := context.Background() input := createTestJPEG(t, 200, 150) @@ -426,7 +426,7 @@ func TestImageProcessor_EncodeWebP(t *testing.T) { } func TestImageProcessor_DecodeAVIF(t *testing.T) { - proc := NewImageProcessor(0) + proc := New(Params{}) ctx := context.Background() // Load test AVIF file @@ -468,7 +468,7 @@ func TestImageProcessor_DecodeAVIF(t *testing.T) { func TestImageProcessor_RejectsOversizedInputData(t *testing.T) { // Create a processor with a very small byte limit const limit = 1024 - proc := NewImageProcessor(limit) + proc := New(Params{MaxInputBytes: limit}) ctx := context.Background() // Create a valid JPEG that exceeds the byte limit @@ -499,7 +499,7 @@ func TestImageProcessor_AcceptsInputWithinLimit(t *testing.T) { input := createTestJPEG(t, 10, 10) limit := int64(len(input)) * 10 // 10× headroom - proc := NewImageProcessor(limit) + proc := New(Params{MaxInputBytes: limit}) ctx := context.Background() req := &ImageRequest{ @@ -518,20 +518,20 @@ func TestImageProcessor_AcceptsInputWithinLimit(t *testing.T) { func TestImageProcessor_DefaultMaxInputBytes(t *testing.T) { // Passing 0 should use the default - proc := NewImageProcessor(0) + proc := New(Params{}) if proc.maxInputBytes != DefaultMaxInputBytes { t.Errorf("maxInputBytes = %d, want %d", proc.maxInputBytes, DefaultMaxInputBytes) } // Passing negative should also use the default - proc = NewImageProcessor(-1) + proc = New(Params{MaxInputBytes: -1}) if proc.maxInputBytes != DefaultMaxInputBytes { t.Errorf("maxInputBytes = %d, want %d", proc.maxInputBytes, DefaultMaxInputBytes) } } func TestImageProcessor_EncodeAVIF(t *testing.T) { - proc := NewImageProcessor(0) + proc := New(Params{}) ctx := context.Background() input := createTestJPEG(t, 200, 150) diff --git a/internal/imgcache/service.go b/internal/imgcache/service.go index 2000f6c..5cd66aa 100644 --- a/internal/imgcache/service.go +++ b/internal/imgcache/service.go @@ -82,7 +82,7 @@ func NewService(cfg *ServiceConfig) (*Service, error) { return &Service{ cache: cfg.Cache, fetcher: fetcher, - processor: NewImageProcessor(maxResponseSize), + processor: New(Params{MaxInputBytes: maxResponseSize}), signer: signer, whitelist: NewHostWhitelist(cfg.Whitelist), log: log,