package fqapi import ( "database/sql" "fanqie/internal/config" "fmt" "sync" "testing" _ "github.com/mattn/go-sqlite3" ) // TestConcurrentCacheAccess 验证删除全局串行锁后并发读写缓存不死锁、不 panic、数据正确(配合 -race) func TestConcurrentCacheAccess(t *testing.T) { if err := config.LoadConfig("../../config/config.yaml"); err != nil { t.Skipf("无法加载配置: %v", err) } db, err := sql.Open("sqlite3", t.TempDir()+"/test.db?_journal_mode=WAL&_busy_timeout=5000") if err != nil { t.Fatal(err) } oldDB, oldDriver := cacheDB, dbDriver cacheDB, dbDriver = db, "sqlite3" defer func() { cacheDB, dbDriver = oldDB, oldDriver db.Close() }() if _, err := db.Exec(`CREATE TABLE IF NOT EXISTS cache ( key TEXT PRIMARY KEY, value TEXT, created_at INTEGER DEFAULT (strftime('%s', 'now')), updated_at INTEGER DEFAULT (strftime('%s', 'now')) )`); err != nil { t.Fatal(err) } var wg sync.WaitGroup for i := 0; i < 8; i++ { wg.Add(1) go func(i int) { defer wg.Done() for j := 0; j < 30; j++ { key := fmt.Sprintf("k-%d-%d", i, j) if err := UpdateCache(key, "value-"+key, 60); err != nil { t.Errorf("UpdateCache(%s): %v", key, err) return } if got := GetCache(key); got != "value-"+key { t.Errorf("GetCache(%s) = %q, want %q", key, got, "value-"+key) return } } }(i) } wg.Wait() } // TestWithCache 验证缓存命中不回源、失败响应不缓存 func TestWithCache(t *testing.T) { if err := config.LoadConfig("../../config/config.yaml"); err != nil { t.Skipf("无法加载配置: %v", err) } db, err := sql.Open("sqlite3", t.TempDir()+"/test.db?_journal_mode=WAL&_busy_timeout=5000") if err != nil { t.Fatal(err) } oldDB, oldDriver := cacheDB, dbDriver cacheDB, dbDriver = db, "sqlite3" defer func() { cacheDB, dbDriver = oldDB, oldDriver db.Close() }() if _, err := db.Exec(`CREATE TABLE IF NOT EXISTS cache ( key TEXT PRIMARY KEY, value TEXT, created_at INTEGER DEFAULT (strftime('%s', 'now')), updated_at INTEGER DEFAULT (strftime('%s', 'now')) )`); err != nil { t.Fatal(err) } calls := 0 fetch := func() (string, error) { calls++; return "result", nil } if got, _ := withCache("wc_hit", 60, fetch); got != "result" { t.Fatalf("首次应返回回源结果, got %q", got) } if got, _ := withCache("wc_hit", 60, fetch); got != "result" { t.Fatalf("命中应返回缓存结果, got %q", got) } if calls != 1 { t.Fatalf("命中缓存不应回源, fetch 调用 %d 次", calls) } errCalls := 0 errFetch := func() (string, error) { errCalls++; return "", fmt.Errorf("boom") } withCache("wc_miss", 60, errFetch) withCache("wc_miss", 60, errFetch) if errCalls != 2 { t.Fatalf("失败响应不应被缓存, fetch 调用 %d 次", errCalls) } }