check / check (push) Canceled after 0s
The new package internal/imageproxy holds Proxy, which was imgcache.Service, with its Config, ImageResponse, CacheStatus and errors. imageproxy.New makes and registers the counters of upstream fetches and transforms. internal/imgcache holds the cache only, with its hit and miss counters, and imgcache.CacheConfig is imgcache.Config. The cache reads and writes each kind it keeps with one pair of methods: Variant and PutVariant, Source and PutSource, Failure and PutFailure. Source replaces LookupSource followed by GetSourceContent. imgcache.go and module.go are gone. The proxy's tests moved with it. The cache's tests check that the reconciliation pass removes an empty or cut file and its row. The proxy's tests remove them by hand, then check that Get fetches or converts again. Model: opus-5-5
101 lines
2.9 KiB
Go
101 lines
2.9 KiB
Go
package imageproxy
|
|
|
|
import (
|
|
"bytes"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"sneak.berlin/go/pixa/internal/imagerequest"
|
|
"sneak.berlin/go/pixa/internal/imgcache"
|
|
)
|
|
|
|
// TestProxy_Get_MakesAgainVariantOfNoOutputFormat replaces a cached
|
|
// variant's file with text, and checks that the next request is a miss
|
|
// answered with the variant made again, and the one after a hit.
|
|
func TestProxy_Get_MakesAgainVariantOfNoOutputFormat(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
svc, fixtures := setupTestProxy(t)
|
|
req := photoVariant(fixtures, 85, imagerequest.FitCover)
|
|
|
|
want, _ := getImage(t, svc, req)
|
|
|
|
path := variantFilePath(svc, imagerequest.CacheKey(req))
|
|
|
|
err := os.WriteFile(path, []byte("this is text, not an image"),
|
|
imgcache.StorageFilePerm)
|
|
if err != nil {
|
|
t.Fatalf("replacing the variant file: %v", err)
|
|
}
|
|
|
|
got, status := getImage(t, svc, req)
|
|
t.Logf("after the replacement: %d bytes, %s", len(got), status)
|
|
|
|
if status != CacheMiss || !bytes.Equal(got, want) {
|
|
t.Errorf("after the replacement: Get() = %d bytes, %s; want the %d bytes "+
|
|
"made again, %s", len(got), status, len(want), CacheMiss)
|
|
}
|
|
|
|
got, status = getImage(t, svc, req)
|
|
|
|
if status != CacheHit || !bytes.Equal(got, want) {
|
|
t.Errorf("next request: Get() = %d bytes, %s; want the %d bytes, %s",
|
|
len(got), status, len(want), CacheHit)
|
|
}
|
|
}
|
|
|
|
// TestProxy_Get_HitWaitsForNoDatabaseWrite holds the database's one
|
|
// connection while a cached variant is requested. The hit must be answered
|
|
// while the connection is still held, and its access time must reach the
|
|
// database once it is free.
|
|
func TestProxy_Get_HitWaitsForNoDatabaseWrite(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
svc, fixtures := setupTestProxy(t)
|
|
req := photoVariant(fixtures, 85, imagerequest.FitCover)
|
|
key := imagerequest.CacheKey(req)
|
|
|
|
getImage(t, svc, req)
|
|
|
|
// Each connection to an in-memory SQLite database opens a new, empty one,
|
|
// so the goroutine that writes the access times must share the
|
|
// connection that holds the schema.
|
|
svc.db.SetMaxOpenConns(1)
|
|
svc.cache.StartPendingAccessTimeWrites()
|
|
|
|
defer func() { _ = svc.cache.StopPendingAccessTimeWrites(t.Context()) }()
|
|
|
|
anHourAgo := time.Now().Add(-time.Hour)
|
|
setVariantLastAccessed(t, svc.db, key, anHourAgo)
|
|
|
|
releaseDatabase := holdDatabase(t, svc.db)
|
|
defer releaseDatabase()
|
|
|
|
results := startGet(t.Context(), svc, req)
|
|
|
|
select {
|
|
case got := <-results:
|
|
if got.err != nil {
|
|
t.Fatalf("Get() of the cached variant error = %v", got.err)
|
|
}
|
|
case <-time.After(pendingAccessTimesWait):
|
|
t.Fatal("the cache hit waited for the database")
|
|
}
|
|
|
|
releaseDatabase()
|
|
|
|
deadline := time.Now().Add(pendingAccessTimesWait)
|
|
|
|
for countRows(t, svc.db, `
|
|
SELECT COUNT(*) FROM variant_content
|
|
WHERE cache_key = ? AND last_accessed_at > ?
|
|
`, string(key), anHourAgo.UTC().Format(sqliteTimestampFormat)) == 0 {
|
|
if time.Now().After(deadline) {
|
|
t.Fatal("the hit's access time was not written")
|
|
}
|
|
|
|
time.Sleep(10 * time.Millisecond)
|
|
}
|
|
}
|