github.com/argoproj/argo-cd@v1.8.7/reposerver/cache/cache_test.go (about) 1 package cache 2 3 import ( 4 "encoding/json" 5 "errors" 6 "strings" 7 "testing" 8 "time" 9 10 "github.com/spf13/cobra" 11 "github.com/stretchr/testify/assert" 12 13 . "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1" 14 appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1" 15 "github.com/argoproj/argo-cd/reposerver/apiclient" 16 cacheutil "github.com/argoproj/argo-cd/util/cache" 17 ) 18 19 type fixtures struct { 20 *Cache 21 } 22 23 func newFixtures() *fixtures { 24 return &fixtures{NewCache( 25 cacheutil.NewCache(cacheutil.NewInMemoryCache(1*time.Hour)), 26 1*time.Minute, 27 )} 28 } 29 30 func TestCache_GetRevisionMetadata(t *testing.T) { 31 cache := newFixtures().Cache 32 // cache miss 33 _, err := cache.GetRevisionMetadata("my-repo-url", "my-revision") 34 assert.Equal(t, ErrCacheMiss, err) 35 // populate cache 36 err = cache.SetRevisionMetadata("my-repo-url", "my-revision", &RevisionMetadata{Message: "my-message"}) 37 assert.NoError(t, err) 38 // cache miss 39 _, err = cache.GetRevisionMetadata("other-repo-url", "my-revision") 40 assert.Equal(t, ErrCacheMiss, err) 41 // cache miss 42 _, err = cache.GetRevisionMetadata("my-repo-url", "other-revision") 43 assert.Equal(t, ErrCacheMiss, err) 44 // cache hit 45 value, err := cache.GetRevisionMetadata("my-repo-url", "my-revision") 46 assert.NoError(t, err) 47 assert.Equal(t, &RevisionMetadata{Message: "my-message"}, value) 48 } 49 50 func TestCache_ListApps(t *testing.T) { 51 cache := newFixtures().Cache 52 // cache miss 53 _, err := cache.ListApps("my-repo-url", "my-revision") 54 assert.Equal(t, ErrCacheMiss, err) 55 // populate cache 56 err = cache.SetApps("my-repo-url", "my-revision", map[string]string{"foo": "bar"}) 57 assert.NoError(t, err) 58 // cache miss 59 _, err = cache.ListApps("other-repo-url", "my-revision") 60 assert.Equal(t, ErrCacheMiss, err) 61 // cache miss 62 _, err = cache.ListApps("my-repo-url", "other-revision") 63 assert.Equal(t, ErrCacheMiss, err) 64 // cache hit 65 value, err := cache.ListApps("my-repo-url", "my-revision") 66 assert.NoError(t, err) 67 assert.Equal(t, map[string]string{"foo": "bar"}, value) 68 } 69 70 func TestCache_GetManifests(t *testing.T) { 71 cache := newFixtures().Cache 72 // cache miss 73 value := &CachedManifestResponse{} 74 err := cache.GetManifests("my-revision", &ApplicationSource{}, "my-namespace", "my-app-label-key", "my-app-label-value", value) 75 assert.Equal(t, ErrCacheMiss, err) 76 // populate cache 77 res := &CachedManifestResponse{ManifestResponse: &apiclient.ManifestResponse{SourceType: "my-source-type"}} 78 err = cache.SetManifests("my-revision", &ApplicationSource{}, "my-namespace", "my-app-label-key", "my-app-label-value", res) 79 assert.NoError(t, err) 80 // cache miss 81 err = cache.GetManifests("other-revision", &ApplicationSource{}, "my-namespace", "my-app-label-key", "my-app-label-value", value) 82 assert.Equal(t, ErrCacheMiss, err) 83 // cache miss 84 err = cache.GetManifests("my-revision", &ApplicationSource{Path: "other-path"}, "my-namespace", "my-app-label-key", "my-app-label-value", value) 85 assert.Equal(t, ErrCacheMiss, err) 86 // cache miss 87 err = cache.GetManifests("my-revision", &ApplicationSource{}, "other-namespace", "my-app-label-key", "my-app-label-value", value) 88 assert.Equal(t, ErrCacheMiss, err) 89 // cache miss 90 err = cache.GetManifests("my-revision", &ApplicationSource{}, "my-namespace", "other-app-label-key", "my-app-label-value", value) 91 assert.Equal(t, ErrCacheMiss, err) 92 // cache miss 93 err = cache.GetManifests("my-revision", &ApplicationSource{}, "my-namespace", "my-app-label-key", "other-app-label-value", value) 94 assert.Equal(t, ErrCacheMiss, err) 95 // cache hit 96 err = cache.GetManifests("my-revision", &ApplicationSource{}, "my-namespace", "my-app-label-key", "my-app-label-value", value) 97 assert.NoError(t, err) 98 assert.Equal(t, &CachedManifestResponse{ManifestResponse: &apiclient.ManifestResponse{SourceType: "my-source-type"}}, value) 99 } 100 101 func TestCache_GetAppDetails(t *testing.T) { 102 cache := newFixtures().Cache 103 // cache miss 104 value := &apiclient.RepoAppDetailsResponse{} 105 err := cache.GetAppDetails("my-revision", &ApplicationSource{}, value) 106 assert.Equal(t, ErrCacheMiss, err) 107 res := &apiclient.RepoAppDetailsResponse{Type: "my-type"} 108 err = cache.SetAppDetails("my-revision", &ApplicationSource{}, res) 109 assert.NoError(t, err) 110 //cache miss 111 err = cache.GetAppDetails("other-revision", &ApplicationSource{}, value) 112 assert.Equal(t, ErrCacheMiss, err) 113 //cache miss 114 err = cache.GetAppDetails("my-revision", &ApplicationSource{Path: "other-path"}, value) 115 assert.Equal(t, ErrCacheMiss, err) 116 // cache hit 117 err = cache.GetAppDetails("my-revision", &ApplicationSource{}, value) 118 assert.NoError(t, err) 119 assert.Equal(t, &apiclient.RepoAppDetailsResponse{Type: "my-type"}, value) 120 } 121 122 func TestAddCacheFlagsToCmd(t *testing.T) { 123 cache, err := AddCacheFlagsToCmd(&cobra.Command{})() 124 assert.NoError(t, err) 125 assert.Equal(t, 24*time.Hour, cache.repoCacheExpiration) 126 } 127 128 func TestCachedManifestResponse_HashBehavior(t *testing.T) { 129 130 inMemCache := cacheutil.NewInMemoryCache(1 * time.Hour) 131 132 repoCache := NewCache( 133 cacheutil.NewCache(inMemCache), 134 1*time.Minute, 135 ) 136 137 response := apiclient.ManifestResponse{ 138 Namespace: "default", 139 Revision: "revision", 140 Manifests: []string{"sample-text"}, 141 } 142 appSrc := &appv1.ApplicationSource{} 143 appKey := "key" 144 appValue := "value" 145 146 // Set the value in the cache 147 store := &CachedManifestResponse{ 148 FirstFailureTimestamp: 0, 149 ManifestResponse: &response, 150 MostRecentError: "", 151 NumberOfCachedResponsesReturned: 0, 152 NumberOfConsecutiveFailures: 0, 153 } 154 err := repoCache.SetManifests(response.Revision, appSrc, response.Namespace, appKey, appValue, store) 155 if err != nil { 156 t.Fatal(err) 157 } 158 159 // Get the cache entry of the set value directly from the in memory cache, and check the values 160 var cacheKey string 161 var cmr *CachedManifestResponse 162 { 163 164 items := getInMemoryCacheContents(t, inMemCache) 165 166 assert.Equal(t, len(items), 1) 167 168 for key, val := range items { 169 cmr = val 170 cacheKey = key 171 } 172 assert.NotEmpty(t, cmr.CacheEntryHash) 173 assert.NotNil(t, cmr.ManifestResponse) 174 assert.Equal(t, cmr.ManifestResponse, store.ManifestResponse) 175 176 regeneratedHash, err := cmr.generateCacheEntryHash() 177 if err != nil { 178 t.Fatal(err) 179 } 180 assert.Equal(t, cmr.CacheEntryHash, regeneratedHash) 181 } 182 183 // Retrieve the value using 'GetManifests' and confirm it works 184 retrievedVal := &CachedManifestResponse{} 185 err = repoCache.GetManifests(response.Revision, appSrc, response.Namespace, appKey, appValue, retrievedVal) 186 if err != nil { 187 t.Fatal(err) 188 } 189 assert.Equal(t, retrievedVal, store) 190 191 // Corrupt the hash so that it doesn't match 192 { 193 newCmr := cmr.shallowCopy() 194 newCmr.CacheEntryHash = "!bad-hash!" 195 196 err := inMemCache.Set(&cacheutil.Item{ 197 Key: cacheKey, 198 Object: &newCmr, 199 }) 200 if err != nil { 201 t.Fatal(err) 202 } 203 204 } 205 206 // Retrieve the value using GetManifests and confirm it returns a cache miss 207 retrievedVal = &CachedManifestResponse{} 208 err = repoCache.GetManifests(response.Revision, appSrc, response.Namespace, appKey, appValue, retrievedVal) 209 210 assert.True(t, err == cacheutil.ErrCacheMiss) 211 212 // Verify that the hash mismatch item has been deleted 213 items := getInMemoryCacheContents(t, inMemCache) 214 assert.Equal(t, len(items), 0) 215 216 } 217 218 func getInMemoryCacheContents(t *testing.T, inMemCache *cacheutil.InMemoryCache) map[string]*CachedManifestResponse { 219 items, err := inMemCache.Items(func() interface{} { return &CachedManifestResponse{} }) 220 if err != nil { 221 t.Fatal(err) 222 } 223 224 result := map[string]*CachedManifestResponse{} 225 for key, val := range items { 226 obj, ok := val.(*CachedManifestResponse) 227 if !ok { 228 t.Fatal(errors.New("Unexpected type in cache")) 229 } 230 231 result[key] = obj 232 } 233 234 return result 235 } 236 237 func TestCachedManifestResponse_ShallowCopy(t *testing.T) { 238 239 pre := &CachedManifestResponse{ 240 CacheEntryHash: "value", 241 FirstFailureTimestamp: 1, 242 ManifestResponse: &apiclient.ManifestResponse{ 243 Manifests: []string{"one", "two"}, 244 }, 245 MostRecentError: "error", 246 NumberOfCachedResponsesReturned: 2, 247 NumberOfConsecutiveFailures: 3, 248 } 249 250 post := pre.shallowCopy() 251 assert.Equal(t, pre, post) 252 253 unequal := &CachedManifestResponse{ 254 CacheEntryHash: "diff-value", 255 FirstFailureTimestamp: 1, 256 ManifestResponse: &apiclient.ManifestResponse{ 257 Manifests: []string{"one", "two"}, 258 }, 259 MostRecentError: "error", 260 NumberOfCachedResponsesReturned: 2, 261 NumberOfConsecutiveFailures: 3, 262 } 263 assert.NotEqual(t, pre, unequal) 264 } 265 266 func TestCachedManifestResponse_ShallowCopyExpectedFields(t *testing.T) { 267 268 // Attempt to ensure that the developer updated CachedManifestResponse.shallowCopy(), by doing a sanity test of the structure here 269 270 val := &CachedManifestResponse{} 271 272 str, err := json.Marshal(val) 273 if err != nil { 274 assert.FailNow(t, "Unable to marshal", err) 275 return 276 } 277 278 jsonMap := map[string]interface{}{} 279 err = json.Unmarshal(str, &jsonMap) 280 if err != nil { 281 assert.FailNow(t, "Unable to unmarshal", err) 282 return 283 } 284 285 expectedFields := []string{"cacheEntryHash", "manifestResponse", "mostRecentError", "firstFailureTimestamp", 286 "numberOfConsecutiveFailures", "numberOfCachedResponsesReturned"} 287 288 assert.Equal(t, len(jsonMap), len(expectedFields)) 289 290 // If this test failed, you probably also forgot to update CachedManifestResponse.shallowCopy(), so 291 // go do that first :) 292 293 for _, expectedField := range expectedFields { 294 assert.Truef(t, strings.Contains(string(str), "\""+expectedField+"\""), "Missing field: %s", expectedField) 295 } 296 297 }