A container registry that uses the AT Protocol for manifest storage and S3 for blob storage.
at codeberg-source 195 lines 5.1 kB view raw
1package auth 2 3import ( 4 "testing" 5 "time" 6) 7 8func TestGetServiceToken_NotCached(t *testing.T) { 9 // Clear cache first 10 globalServiceTokensMu.Lock() 11 globalServiceTokens = make(map[string]*serviceTokenEntry) 12 globalServiceTokensMu.Unlock() 13 14 did := "did:plc:test123" 15 holdDID := "did:web:hold.example.com" 16 17 token, expiresAt := GetServiceToken(did, holdDID) 18 if token != "" { 19 t.Errorf("Expected empty token for uncached entry, got %q", token) 20 } 21 if !expiresAt.IsZero() { 22 t.Error("Expected zero time for uncached entry") 23 } 24} 25 26func TestSetServiceToken_ManualExpiry(t *testing.T) { 27 // Clear cache first 28 globalServiceTokensMu.Lock() 29 globalServiceTokens = make(map[string]*serviceTokenEntry) 30 globalServiceTokensMu.Unlock() 31 32 did := "did:plc:test123" 33 holdDID := "did:web:hold.example.com" 34 token := "invalid_jwt_token" // Will fall back to 50s default 35 36 // This should succeed with default 50s TTL since JWT parsing will fail 37 err := SetServiceToken(did, holdDID, token) 38 if err != nil { 39 t.Fatalf("SetServiceToken() error = %v", err) 40 } 41 42 // Verify token was cached 43 cachedToken, expiresAt := GetServiceToken(did, holdDID) 44 if cachedToken != token { 45 t.Errorf("Expected token %q, got %q", token, cachedToken) 46 } 47 if expiresAt.IsZero() { 48 t.Error("Expected non-zero expiry time") 49 } 50 51 // Expiry should be approximately 50s from now (with 10s margin subtracted in some cases) 52 expectedExpiry := time.Now().Add(50 * time.Second) 53 diff := expiresAt.Sub(expectedExpiry) 54 if diff < -5*time.Second || diff > 5*time.Second { 55 t.Errorf("Expiry time off by %v (expected ~50s from now)", diff) 56 } 57} 58 59func TestGetServiceToken_Expired(t *testing.T) { 60 // Manually insert an expired token 61 did := "did:plc:test123" 62 holdDID := "did:web:hold.example.com" 63 cacheKey := did + ":" + holdDID 64 65 globalServiceTokensMu.Lock() 66 globalServiceTokens[cacheKey] = &serviceTokenEntry{ 67 token: "expired_token", 68 expiresAt: time.Now().Add(-1 * time.Hour), // 1 hour ago 69 } 70 globalServiceTokensMu.Unlock() 71 72 // Try to get - should return empty since expired 73 token, expiresAt := GetServiceToken(did, holdDID) 74 if token != "" { 75 t.Errorf("Expected empty token for expired entry, got %q", token) 76 } 77 if !expiresAt.IsZero() { 78 t.Error("Expected zero time for expired entry") 79 } 80 81 // Verify token was removed from cache 82 globalServiceTokensMu.RLock() 83 _, exists := globalServiceTokens[cacheKey] 84 globalServiceTokensMu.RUnlock() 85 86 if exists { 87 t.Error("Expected expired token to be removed from cache") 88 } 89} 90 91func TestInvalidateServiceToken(t *testing.T) { 92 // Set a token 93 did := "did:plc:test123" 94 holdDID := "did:web:hold.example.com" 95 token := "test_token" 96 97 err := SetServiceToken(did, holdDID, token) 98 if err != nil { 99 t.Fatalf("SetServiceToken() error = %v", err) 100 } 101 102 // Verify it's cached 103 cachedToken, _ := GetServiceToken(did, holdDID) 104 if cachedToken != token { 105 t.Fatal("Token should be cached") 106 } 107 108 // Invalidate 109 InvalidateServiceToken(did, holdDID) 110 111 // Verify it's gone 112 cachedToken, _ = GetServiceToken(did, holdDID) 113 if cachedToken != "" { 114 t.Error("Expected token to be invalidated") 115 } 116} 117 118func TestCleanExpiredTokens(t *testing.T) { 119 // Clear cache first 120 globalServiceTokensMu.Lock() 121 globalServiceTokens = make(map[string]*serviceTokenEntry) 122 globalServiceTokensMu.Unlock() 123 124 // Add expired and valid tokens 125 globalServiceTokensMu.Lock() 126 globalServiceTokens["expired:hold1"] = &serviceTokenEntry{ 127 token: "expired1", 128 expiresAt: time.Now().Add(-1 * time.Hour), 129 } 130 globalServiceTokens["valid:hold2"] = &serviceTokenEntry{ 131 token: "valid1", 132 expiresAt: time.Now().Add(1 * time.Hour), 133 } 134 globalServiceTokensMu.Unlock() 135 136 // Clean expired 137 CleanExpiredTokens() 138 139 // Verify only valid token remains 140 globalServiceTokensMu.RLock() 141 _, expiredExists := globalServiceTokens["expired:hold1"] 142 _, validExists := globalServiceTokens["valid:hold2"] 143 globalServiceTokensMu.RUnlock() 144 145 if expiredExists { 146 t.Error("Expected expired token to be removed") 147 } 148 if !validExists { 149 t.Error("Expected valid token to remain") 150 } 151} 152 153func TestGetCacheStats(t *testing.T) { 154 // Clear cache first 155 globalServiceTokensMu.Lock() 156 globalServiceTokens = make(map[string]*serviceTokenEntry) 157 globalServiceTokensMu.Unlock() 158 159 // Add some tokens 160 globalServiceTokensMu.Lock() 161 globalServiceTokens["did1:hold1"] = &serviceTokenEntry{ 162 token: "token1", 163 expiresAt: time.Now().Add(1 * time.Hour), 164 } 165 globalServiceTokens["did2:hold2"] = &serviceTokenEntry{ 166 token: "token2", 167 expiresAt: time.Now().Add(1 * time.Hour), 168 } 169 globalServiceTokensMu.Unlock() 170 171 stats := GetCacheStats() 172 if stats == nil { 173 t.Fatal("Expected non-nil stats") 174 } 175 176 // GetCacheStats returns map[string]any with "total_entries" key 177 totalEntries, ok := stats["total_entries"].(int) 178 if !ok { 179 t.Fatalf("Expected total_entries in stats map, got: %v", stats) 180 } 181 182 if totalEntries != 2 { 183 t.Errorf("Expected 2 entries, got %d", totalEntries) 184 } 185 186 // Also check valid_tokens 187 validTokens, ok := stats["valid_tokens"].(int) 188 if !ok { 189 t.Fatal("Expected valid_tokens in stats map") 190 } 191 192 if validTokens != 2 { 193 t.Errorf("Expected 2 valid tokens, got %d", validTokens) 194 } 195}