check / check (push) Waiting to run
CacheConfig.CacheTTL was set to DefaultCacheTTL (24 hours) and never read, so cached images never expired. Remove the field, the constant and the line that set it. Cached source and transformed images stay until eviction removes them to keep the cache within cache_max_bytes. A new test caches a source and a transformed image, sets back two days fetched_at in source_metadata, fetched_at and last_accessed_at in source_content, created_at and last_accessed_at in variant_content, the fetched_at in the source's metadata file, the created_at in the .meta file, and the access and modification times of those two files and of the two image files, and checks that both are still served from the cache without a new upstream fetch. Existing tests lose only the CacheTTL line of their cache config. Model: opus-5-5
108 lines
2.1 KiB
Go
108 lines
2.1 KiB
Go
package imgcache
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestNegativeCache_StoreAndCheck(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
db := setupTestDB(t)
|
|
dir := t.TempDir()
|
|
|
|
cache, err := NewCache(db, CacheConfig{
|
|
StateDir: dir,
|
|
NegativeTTL: 5 * time.Minute,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
ctx := context.Background()
|
|
req := &ImageRequest{
|
|
SourceHost: testHostExample,
|
|
SourcePath: "/missing.jpg",
|
|
}
|
|
|
|
// Initially should not be in negative cache
|
|
hit, err := cache.checkNegativeCache(ctx, req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if hit {
|
|
t.Error("expected no negative cache hit initially")
|
|
}
|
|
|
|
// Store a negative entry
|
|
err = cache.StoreNegative(ctx, req, 404, "not found")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Now should be in negative cache
|
|
hit, err = cache.checkNegativeCache(ctx, req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !hit {
|
|
t.Error("expected negative cache hit after storing")
|
|
}
|
|
}
|
|
|
|
func TestNegativeCache_Expired(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
db := setupTestDB(t)
|
|
dir := t.TempDir()
|
|
|
|
cache, err := NewCache(db, CacheConfig{
|
|
StateDir: dir,
|
|
NegativeTTL: 1 * time.Millisecond, // very short TTL
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
ctx := context.Background()
|
|
req := &ImageRequest{
|
|
SourceHost: testHostExample,
|
|
SourcePath: "/expired.jpg",
|
|
}
|
|
|
|
// Store a negative entry with very short TTL
|
|
err = cache.StoreNegative(ctx, req, 500, "server error")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Wait for expiry
|
|
time.Sleep(10 * time.Millisecond)
|
|
|
|
// Should no longer be in negative cache
|
|
hit, err := cache.checkNegativeCache(ctx, req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if hit {
|
|
t.Error("expected expired negative cache entry to be a miss")
|
|
}
|
|
}
|
|
|
|
func TestService_Get_ReturnsErrorForNegativeCachedURL(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// This test verifies that Service.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")
|
|
}
|
|
}
|