mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-06-22 03:42:51 +08:00
- Updated Antigravity Credits fallback to handle KV store unavailability as a service error. - Enhanced signature caching mechanisms with request-time KV access and sliding expiration. - Added and improved tests for KV client interactions, including error handling and expiration behaviors. - Introduced `CacheSignatureBestEffort` for non-critical signature caching and clarified function flows with required context. - Ensured consistent error reporting for missing or unavailable KV stores in various scenarios. - Replaced direct `homekv` calls with injectable KV client interfaces for `antigravity` and `codex_reasoning_replay` modules. - Improved error reporting and handling for KV operations, including `KVGet`, `KVSet`, `KVDel`, and `KVExpire`. - Introduced dedicated fake KV clients for expanded and granular test coverage. - Added new unit tests to validate KV client behaviors and error scenarios, ensuring robustness and sliding expiration functionality.
166 lines
4.8 KiB
Go
166 lines
4.8 KiB
Go
package helps
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func resetUserIDCache() {
|
|
userIDCacheMu.Lock()
|
|
userIDCache = make(map[string]userIDCacheEntry)
|
|
userIDCacheMu.Unlock()
|
|
}
|
|
|
|
func TestCachedUserID_ReusesWithinTTL(t *testing.T) {
|
|
resetUserIDCache()
|
|
|
|
first := CachedUserID("api-key-1")
|
|
second := CachedUserID("api-key-1")
|
|
|
|
if first == "" {
|
|
t.Fatal("expected generated user_id to be non-empty")
|
|
}
|
|
if first != second {
|
|
t.Fatalf("expected cached user_id to be reused, got %q and %q", first, second)
|
|
}
|
|
}
|
|
|
|
func TestCachedUserID_ExpiresAfterTTL(t *testing.T) {
|
|
resetUserIDCache()
|
|
|
|
expiredID := CachedUserID("api-key-expired")
|
|
cacheKey := userIDCacheKey("api-key-expired")
|
|
userIDCacheMu.Lock()
|
|
userIDCache[cacheKey] = userIDCacheEntry{
|
|
value: expiredID,
|
|
expire: time.Now().Add(-time.Minute),
|
|
}
|
|
userIDCacheMu.Unlock()
|
|
|
|
newID := CachedUserID("api-key-expired")
|
|
if newID == expiredID {
|
|
t.Fatalf("expected expired user_id to be replaced, got %q", newID)
|
|
}
|
|
if newID == "" {
|
|
t.Fatal("expected regenerated user_id to be non-empty")
|
|
}
|
|
}
|
|
|
|
func TestCachedUserID_IsScopedByAPIKey(t *testing.T) {
|
|
resetUserIDCache()
|
|
|
|
first := CachedUserID("api-key-1")
|
|
second := CachedUserID("api-key-2")
|
|
|
|
if first == second {
|
|
t.Fatalf("expected different API keys to have different user_ids, got %q", first)
|
|
}
|
|
}
|
|
|
|
func TestCachedUserID_RenewsTTLOnHit(t *testing.T) {
|
|
resetUserIDCache()
|
|
|
|
key := "api-key-renew"
|
|
id := CachedUserID(key)
|
|
cacheKey := userIDCacheKey(key)
|
|
|
|
soon := time.Now()
|
|
userIDCacheMu.Lock()
|
|
userIDCache[cacheKey] = userIDCacheEntry{
|
|
value: id,
|
|
expire: soon.Add(2 * time.Second),
|
|
}
|
|
userIDCacheMu.Unlock()
|
|
|
|
if refreshed := CachedUserID(key); refreshed != id {
|
|
t.Fatalf("expected cached user_id to be reused before expiry, got %q", refreshed)
|
|
}
|
|
|
|
userIDCacheMu.RLock()
|
|
entry := userIDCache[cacheKey]
|
|
userIDCacheMu.RUnlock()
|
|
|
|
if entry.expire.Sub(soon) < 30*time.Minute {
|
|
t.Fatalf("expected TTL to renew, got %v remaining", entry.expire.Sub(soon))
|
|
}
|
|
}
|
|
|
|
func TestCachedUserIDRequiredHomeReusesKVAcrossLocalCacheReset(t *testing.T) {
|
|
resetUserIDCache()
|
|
client := newFakeClaudeIDKVClient()
|
|
useFakeClaudeIDKVClient(t, client, true, nil)
|
|
|
|
first, errFirst := CachedUserIDRequired(context.Background(), "api-key-1")
|
|
if errFirst != nil {
|
|
t.Fatalf("CachedUserIDRequired() first error = %v", errFirst)
|
|
}
|
|
resetUserIDCache()
|
|
second, errSecond := CachedUserIDRequired(context.Background(), "api-key-1")
|
|
if errSecond != nil {
|
|
t.Fatalf("CachedUserIDRequired() second error = %v", errSecond)
|
|
}
|
|
if first != second {
|
|
t.Fatalf("user id = %q then %q, want same Home KV value", first, second)
|
|
}
|
|
if !IsValidUserID(first) {
|
|
t.Fatalf("user id %q is not valid", first)
|
|
}
|
|
if client.setCount != 1 {
|
|
t.Fatalf("KVSetNX count = %d, want 1", client.setCount)
|
|
}
|
|
if client.expireCount != 1 || client.lastExpireTTL != userIDTTL {
|
|
t.Fatalf("KVExpire count/ttl = %d/%v, want 1/%v", client.expireCount, client.lastExpireTTL, userIDTTL)
|
|
}
|
|
if client.lastSetTTL != userIDTTL {
|
|
t.Fatalf("KVSetNX ttl = %v, want %v", client.lastSetTTL, userIDTTL)
|
|
}
|
|
}
|
|
|
|
func TestCachedUserIDRequiredEmptyAPIKeyDoesNotUseHomeKV(t *testing.T) {
|
|
client := newFakeClaudeIDKVClient()
|
|
useFakeClaudeIDKVClient(t, client, true, nil)
|
|
|
|
value, errValue := CachedUserIDRequired(context.Background(), "")
|
|
if errValue != nil {
|
|
t.Fatalf("CachedUserIDRequired(empty) error = %v", errValue)
|
|
}
|
|
if !IsValidUserID(value) {
|
|
t.Fatalf("user id %q is not valid", value)
|
|
}
|
|
if client.getCount != 0 || client.setCount != 0 || client.expireCount != 0 {
|
|
t.Fatalf("KV calls = get %d set %d expire %d, want all zero", client.getCount, client.setCount, client.expireCount)
|
|
}
|
|
}
|
|
|
|
func TestCachedUserIDRequiredHomeKVFailures(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
client *fakeClaudeIDKVClient
|
|
}{
|
|
{name: "get", client: &fakeClaudeIDKVClient{values: make(map[string][]byte), getErr: errors.New("get failed")}},
|
|
{name: "set", client: &fakeClaudeIDKVClient{values: make(map[string][]byte), setErr: errors.New("set failed")}},
|
|
{name: "expire", client: &fakeClaudeIDKVClient{values: map[string][]byte{
|
|
claudeUserIDKVKey("api-key-1"): []byte(GenerateFakeUserID()),
|
|
}, expireErr: errors.New("expire failed")}},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
useFakeClaudeIDKVClient(t, tc.client, true, nil)
|
|
if _, errValue := CachedUserIDRequired(context.Background(), "api-key-1"); errValue == nil {
|
|
t.Fatalf("CachedUserIDRequired() error = nil, want error")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCachedUserIDRequiredHomeRequiresReadAfterSet(t *testing.T) {
|
|
client := newFakeClaudeIDKVClient()
|
|
client.setNoPersist = true
|
|
useFakeClaudeIDKVClient(t, client, true, nil)
|
|
|
|
if _, errValue := CachedUserIDRequired(context.Background(), "api-key-1"); errValue == nil {
|
|
t.Fatalf("CachedUserIDRequired() error = nil, want missing-after-set error")
|
|
}
|
|
}
|