package imageproxy import ( "errors" "os" "testing" "sneak.berlin/go/pixa/internal/imagerequest" ) // TestProxy_Get_ServesCachedVariantOfNegativeCachedURL caches a variant of // the test photo, then keeps a failed fetch of the photo's URL in the negative // cache, as a failed fetch for another variant does once the source has been // evicted. The cached variant is still served, and a variant not in the cache // is made from the cached source. Once the source is evicted, a variant not in // the cache needs a fetch and is refused. func TestProxy_Get_ServesCachedVariantOfNegativeCachedURL(t *testing.T) { t.Parallel() svc, fixtures := setupTestProxy(t) resp, err := svc.Get(t.Context(), photoVariant(fixtures, 85, imagerequest.FitCover)) if err != nil { t.Fatalf("Get() first request error = %v", err) } _ = resp.Content.Close() err = svc.cache.PutFailure(t.Context(), photoVariant(fixtures, 85, imagerequest.FitCover).Source) if err != nil { t.Fatalf("PutFailure() error = %v", err) } resp, err = svc.Get(t.Context(), photoVariant(fixtures, 85, imagerequest.FitCover)) if err != nil { t.Fatalf("Get() of the cached variant error = %v", err) } _ = resp.Content.Close() if resp.CacheStatus != CacheHit { t.Errorf("cached variant CacheStatus = %q, want %q", resp.CacheStatus, CacheHit) } resp, err = svc.Get(t.Context(), photoVariant(fixtures, 40, imagerequest.FitCover)) t.Logf("Get() of a variant made from the cached source: error = %v", err) if err != nil { t.Fatalf("Get() of a variant made from the cached source error = %v", err) } _ = resp.Content.Close() source := photoVariant(fixtures, 85, imagerequest.FitCover).Source reader, _, err := svc.cache.Source(t.Context(), source) if err != nil { t.Fatalf("Source() error = %v, want the cached source", err) } _ = reader.Close() // Evicts the source as the cache's eviction does: its row, then its file _, err = svc.db.ExecContext(t.Context(), `DELETE FROM source_content WHERE source_key = ?`, string(source.Key())) if err != nil { t.Fatalf("deleting the source's row: %v", err) } err = os.Remove(sourceFilePath(svc, source.Key())) if err != nil { t.Fatalf("removing the source's file: %v", err) } _, err = svc.Get(t.Context(), photoVariant(fixtures, 60, imagerequest.FitCover)) t.Logf("Get() of a variant whose source was evicted: error = %v", err) if !errors.Is(err, ErrNegativeCached) { t.Errorf("Get() of a variant whose source was evicted error = %v, want %v", err, ErrNegativeCached) } } func TestProxy_Get_ReturnsErrorForNegativeCachedURL(t *testing.T) { t.Parallel() // This test verifies that Proxy.Get() checks the negative cache // We can't easily test the full pipeline without vips, but we can // verify the error type err := ErrNegativeCached if !errors.Is(err, ErrNegativeCached) { t.Error("ErrNegativeCached should be identifiable with errors.Is") } }