196 lines · 5.7 KB · Go
RawDownload
package helps import (	"context"	"encoding/json"	"errors"	"testing"	"time") func resetUserIDCache() {	userIDCacheMu.Lock()	userIDCache = make(map[string]userIDCacheEntry)	userIDCacheMu.Unlock()} func TestGenerateFakeUserIDUsesClaudeCode220JSONShape(t *testing.T) {	userID := GenerateFakeUserID()	if !IsValidUserID(userID) {		t.Fatalf("user ID %q is not valid", userID)	}	var value claudeMetadataUserID	if errUnmarshal := json.Unmarshal([]byte(userID), &value); errUnmarshal != nil {		t.Fatalf("unmarshal user ID: %v", errUnmarshal)	}	if value.AccountUUID != "" {		t.Fatalf("account_uuid = %q, want empty", value.AccountUUID)	}} func TestCachedUserIDUsesCachedClaudeSessionID(t *testing.T) {	resetUserIDCache()	resetSessionIDCache() 	const key = "api-key-shared-session"	sessionID := CachedSessionID(key)	userID := CachedUserID(key)	var value claudeMetadataUserID	if errUnmarshal := json.Unmarshal([]byte(userID), &value); errUnmarshal != nil {		t.Fatalf("unmarshal user ID: %v", errUnmarshal)	}	if value.SessionID != sessionID {		t.Fatalf("metadata session_id = %q, header session ID = %q", value.SessionID, sessionID)	}} 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 != 2 {		t.Fatalf("KVSetNX count = %d, want 2 (session and user ID)", 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")	}}