From 79ceed2ee49071ac8c246cfcf5042653083a5779 Mon Sep 17 00:00:00 2001 From: clawbot Date: Sun, 8 Feb 2026 15:59:51 -0800 Subject: [PATCH] fix: guard against division by zero when fetchBytes is 0 processAndStore() computed sizePercent as outputSize/fetchBytes*100 without checking for zero, producing Inf/NaN in logs and metrics. Also treat empty cached source data the same as missing (re-fetch from upstream) since zero-byte images can't be processed. Fixes #5 --- internal/imgcache/divzero_test.go | 43 +++++++++++++++++++++++++++++++ internal/imgcache/service.go | 10 ++++--- 2 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 internal/imgcache/divzero_test.go diff --git a/internal/imgcache/divzero_test.go b/internal/imgcache/divzero_test.go new file mode 100644 index 0000000..160d480 --- /dev/null +++ b/internal/imgcache/divzero_test.go @@ -0,0 +1,43 @@ +package imgcache + +import ( + "fmt" + "math" + "testing" +) + +func TestSizePercentSafeWithZeroFetchBytes(t *testing.T) { + // Simulate the calculation from processAndStore + fetchBytes := int64(0) + outputSize := int64(100) + + var sizePercent float64 + if fetchBytes > 0 { + sizePercent = float64(outputSize) / float64(fetchBytes) * 100.0 + } + + // sizePercent should be 0, not Inf or NaN + if math.IsInf(sizePercent, 0) || math.IsNaN(sizePercent) { + t.Errorf("sizePercent = %f, should not be Inf/NaN", sizePercent) + } + + // Should produce valid log output + result := fmt.Sprintf("%.1f%%", sizePercent) + if result != "0.0%" { + t.Errorf("formatted size ratio = %q, want %q", result, "0.0%") + } +} + +func TestSizePercentNormalCase(t *testing.T) { + fetchBytes := int64(1000) + outputSize := int64(500) + + var sizePercent float64 + if fetchBytes > 0 { + sizePercent = float64(outputSize) / float64(fetchBytes) * 100.0 + } + + if math.Abs(sizePercent-50.0) > 0.001 { + t.Errorf("sizePercent = %f, want 50.0", sizePercent) + } +} diff --git a/internal/imgcache/service.go b/internal/imgcache/service.go index d3a5c15..7734f96 100644 --- a/internal/imgcache/service.go +++ b/internal/imgcache/service.go @@ -153,8 +153,8 @@ func (s *Service) processFromSourceOrFetch( } } - // Fetch from upstream if we don't have source data - if sourceData == nil { + // Fetch from upstream if we don't have source data or it's empty + if len(sourceData) == 0 { resp, err := s.fetchAndProcess(ctx, req, cacheKey) if err != nil { return nil, err @@ -264,7 +264,11 @@ func (s *Service) processAndStore( // Log conversion details outputSize := int64(len(processedData)) - sizePercent := float64(outputSize) / float64(fetchBytes) * 100.0 //nolint:mnd // percentage calculation + + var sizePercent float64 + if fetchBytes > 0 { + sizePercent = float64(outputSize) / float64(fetchBytes) * 100.0 //nolint:mnd // percentage calculation + } s.log.Info("image converted", "host", req.SourceHost,