github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/api/accessor/claims_cacher_test.go (about)

     1  package accessor_test
     2  
     3  import (
     4  	"errors"
     5  
     6  	"github.com/pf-qiu/concourse/v6/atc/api/accessor"
     7  	"github.com/pf-qiu/concourse/v6/atc/api/accessor/accessorfakes"
     8  	"github.com/pf-qiu/concourse/v6/atc/db"
     9  
    10  	. "github.com/onsi/ginkgo"
    11  	. "github.com/onsi/gomega"
    12  )
    13  
    14  var _ = Describe("ClaimsCacher", func() {
    15  	var (
    16  		fakeAccessTokenFetcher *accessorfakes.FakeAccessTokenFetcher
    17  		maxCacheSizeBytes      int
    18  
    19  		claimsCacher accessor.AccessTokenFetcher
    20  	)
    21  
    22  	BeforeEach(func() {
    23  		fakeAccessTokenFetcher = new(accessorfakes.FakeAccessTokenFetcher)
    24  		maxCacheSizeBytes = 1000
    25  	})
    26  
    27  	JustBeforeEach(func() {
    28  		claimsCacher = accessor.NewClaimsCacher(fakeAccessTokenFetcher, maxCacheSizeBytes)
    29  	})
    30  
    31  	It("fetches claims from the DB", func() {
    32  		claimsCacher.GetAccessToken("token")
    33  		Expect(fakeAccessTokenFetcher.GetAccessTokenCallCount()).To(Equal(1), "did not fetch from DB")
    34  	})
    35  
    36  	It("doesn't fetch from the DB when the result is cached", func() {
    37  		claimsCacher.GetAccessToken("token")
    38  		claimsCacher.GetAccessToken("token")
    39  		Expect(fakeAccessTokenFetcher.GetAccessTokenCallCount()).To(Equal(1), "did not cache claims")
    40  	})
    41  
    42  	It("doesn't cache claims when cache size is exceeded", func() {
    43  		fakeAccessTokenFetcher.GetAccessTokenReturns(db.AccessToken{
    44  			Claims: db.Claims{RawClaims: map[string]interface{}{"a": stringWithLen(2000)}},
    45  		}, true, nil)
    46  		claimsCacher.GetAccessToken("token")
    47  		claimsCacher.GetAccessToken("token")
    48  		Expect(fakeAccessTokenFetcher.GetAccessTokenCallCount()).To(Equal(2), "cached claims that exceed length")
    49  	})
    50  
    51  	It("evicts the least recently used access token when size limit exceeded", func() {
    52  		fakeAccessTokenFetcher.GetAccessTokenReturns(db.AccessToken{
    53  			Claims: db.Claims{RawClaims: map[string]interface{}{"a": stringWithLen(400)}},
    54  		}, true, nil)
    55  
    56  		By("filling the cache")
    57  		claimsCacher.GetAccessToken("token1")
    58  		claimsCacher.GetAccessToken("token2")
    59  		Expect(fakeAccessTokenFetcher.GetAccessTokenCallCount()).To(Equal(2))
    60  
    61  		By("overflowing the cache")
    62  		claimsCacher.GetAccessToken("token3")
    63  		Expect(fakeAccessTokenFetcher.GetAccessTokenCallCount()).To(Equal(3))
    64  
    65  		By("fetching the least recently used token")
    66  		claimsCacher.GetAccessToken("token1")
    67  		Expect(fakeAccessTokenFetcher.GetAccessTokenCallCount()).To(Equal(4), "did not evict least recently used")
    68  
    69  		By("ensuring the latest token was not evicted")
    70  		claimsCacher.GetAccessToken("token3")
    71  		Expect(fakeAccessTokenFetcher.GetAccessTokenCallCount()).To(Equal(4), "evicted the latest token")
    72  	})
    73  
    74  	It("errors when the DB fails", func() {
    75  		fakeAccessTokenFetcher.GetAccessTokenReturns(db.AccessToken{}, false, errors.New("error"))
    76  		_, _, err := claimsCacher.GetAccessToken("token")
    77  		Expect(err).To(HaveOccurred())
    78  	})
    79  
    80  	It("fetches claims from the DB concurrently", func() {
    81  		// this is designed purely to trigger the race detector
    82  		go claimsCacher.GetAccessToken("token1")
    83  		go claimsCacher.GetAccessToken("token2")
    84  		go claimsCacher.GetAccessToken("token3")
    85  		Eventually(fakeAccessTokenFetcher.GetAccessTokenCallCount).Should(Equal(3))
    86  	})
    87  })
    88  
    89  func stringWithLen(l int) string {
    90  	b := make([]byte, l)
    91  	for i := 0; i < l; i++ {
    92  		b[i] = 'a'
    93  	}
    94  	return string(b)
    95  }