check / check (push) Canceled after 0s
Removes output_content and request_cache, which nothing writes, and every column that was written but never read: the host, path, query, content_type and fetched_at of source_content, the content_type and created_at of variant_content, and the status_code, error_message and fetched_at of negative_cache. StoreSource no longer takes the fetch result and LookupSource returns only the source key. StoreVariant no longer takes a content type and StoreNegative no longer takes a status code or message, so extractStatusCode and the status constants, which only fed status_code, are gone. Reconciliation adopts a variant file without opening it, as its row no longer records a content type. Tests that set or checked a removed column follow. Model: opus-5-5
178 lines
3.7 KiB
Go
178 lines
3.7 KiB
Go
package imgcache
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
_ "modernc.org/sqlite"
|
|
)
|
|
|
|
func setupTestDB(t *testing.T) *sql.DB {
|
|
t.Helper()
|
|
|
|
db, err := sql.Open("sqlite", ":memory:")
|
|
if err != nil {
|
|
t.Fatalf("failed to open test db: %v", err)
|
|
}
|
|
|
|
// Create schema
|
|
schema := `
|
|
CREATE TABLE source_content (
|
|
source_key TEXT PRIMARY KEY,
|
|
size_bytes INTEGER NOT NULL,
|
|
last_accessed_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE TABLE negative_cache (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
source_host TEXT NOT NULL,
|
|
source_path TEXT NOT NULL,
|
|
source_query TEXT NOT NULL DEFAULT '',
|
|
expires_at DATETIME NOT NULL,
|
|
UNIQUE(source_host, source_path, source_query)
|
|
);
|
|
CREATE TABLE cache_stats (
|
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
hit_count INTEGER NOT NULL DEFAULT 0,
|
|
miss_count INTEGER NOT NULL DEFAULT 0,
|
|
upstream_fetch_count INTEGER NOT NULL DEFAULT 0,
|
|
upstream_fetch_bytes INTEGER NOT NULL DEFAULT 0,
|
|
transform_count INTEGER NOT NULL DEFAULT 0,
|
|
last_updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
INSERT INTO cache_stats (id) VALUES (1);
|
|
`
|
|
|
|
_, err = db.ExecContext(t.Context(), schema)
|
|
if err != nil {
|
|
t.Fatalf("failed to create schema: %v", err)
|
|
}
|
|
|
|
return db
|
|
}
|
|
|
|
func setupTestCache(t *testing.T) *Cache {
|
|
t.Helper()
|
|
|
|
tmpDir := t.TempDir()
|
|
db := setupTestDB(t)
|
|
|
|
cache, err := NewCache(db, CacheConfig{
|
|
StateDir: tmpDir,
|
|
NegativeTTL: 5 * time.Minute,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("failed to create cache: %v", err)
|
|
}
|
|
|
|
return cache
|
|
}
|
|
|
|
func TestCache_Stats(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cache := setupTestCache(t)
|
|
ctx := context.Background()
|
|
|
|
// Increment some stats
|
|
cache.IncrementStats(true, 0)
|
|
cache.IncrementStats(true, 0)
|
|
cache.IncrementStats(false, 1024)
|
|
|
|
stats, err := cache.Stats(ctx)
|
|
if err != nil {
|
|
t.Fatalf("Stats() error = %v", err)
|
|
}
|
|
|
|
if stats.HitCount != 2 {
|
|
t.Errorf("Stats() HitCount = %d, want 2", stats.HitCount)
|
|
}
|
|
|
|
if stats.MissCount != 1 {
|
|
t.Errorf("Stats() MissCount = %d, want 1", stats.MissCount)
|
|
}
|
|
}
|
|
|
|
func TestCache_CleanExpired(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tmpDir := t.TempDir()
|
|
db := setupTestDB(t)
|
|
|
|
cache, _ := NewCache(db, CacheConfig{
|
|
StateDir: tmpDir,
|
|
NegativeTTL: 1 * time.Millisecond,
|
|
})
|
|
|
|
ctx := context.Background()
|
|
|
|
// Insert expired negative cache entry directly
|
|
_, err := db.ExecContext(ctx, `
|
|
INSERT INTO negative_cache
|
|
(source_host, source_path, source_query, expires_at)
|
|
VALUES ('example.com', '/old.jpg', '', datetime('now', '-1 hour'))
|
|
`)
|
|
if err != nil {
|
|
t.Fatalf("failed to insert test data: %v", err)
|
|
}
|
|
|
|
// Verify it exists
|
|
var count int
|
|
|
|
err = db.QueryRowContext(ctx, `SELECT COUNT(*) FROM negative_cache`).Scan(&count)
|
|
if err != nil {
|
|
t.Fatalf("failed to count negative cache entries: %v", err)
|
|
}
|
|
|
|
if count != 1 {
|
|
t.Fatalf("expected 1 negative cache entry, got %d", count)
|
|
}
|
|
|
|
// Clean expired
|
|
err = cache.CleanExpired(ctx)
|
|
if err != nil {
|
|
t.Fatalf("CleanExpired() error = %v", err)
|
|
}
|
|
|
|
// Verify it's gone
|
|
err = db.QueryRowContext(ctx, `SELECT COUNT(*) FROM negative_cache`).Scan(&count)
|
|
if err != nil {
|
|
t.Fatalf("failed to count negative cache entries: %v", err)
|
|
}
|
|
|
|
if count != 0 {
|
|
t.Errorf("expected 0 negative cache entries after clean, got %d", count)
|
|
}
|
|
}
|
|
|
|
func TestCache_StorageDirectoriesCreated(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tmpDir := t.TempDir()
|
|
db := setupTestDB(t)
|
|
|
|
_, err := NewCache(db, CacheConfig{
|
|
StateDir: tmpDir,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("NewCache() error = %v", err)
|
|
}
|
|
|
|
// Verify directories were created
|
|
dirs := []string{
|
|
"cache/sources",
|
|
"cache/variants",
|
|
}
|
|
|
|
for _, dir := range dirs {
|
|
path := tmpDir + "/" + dir
|
|
|
|
_, err := os.Stat(path)
|
|
if os.IsNotExist(err) {
|
|
t.Errorf("directory %s was not created", dir)
|
|
}
|
|
}
|
|
}
|