// Copyright 2020-2026 Project Capsule Authors // SPDX-License-Identifier: Apache-2.0 package cache import ( "errors" "sync" "sync/atomic" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2" ) var _ = Describe("CompiledTargetsCache", func() { var c *CompiledTargetsCache[string] target := func(kind string) CompiledTarget { return CompiledTarget{ CustomQuotaStatusTarget: capsulev1beta2.CustomQuotaStatusTarget{ GroupVersionKind: metav1.GroupVersionKind{ Group: "", Version: "v1", Kind: kind, }, }, } } BeforeEach(func() { c = NewCompiledTargetsCache[string]() }) It("returns false when key is missing", func() { value, ok := c.Get("missing") Expect(ok).To(BeFalse()) Expect(value).To(BeNil()) }) It("sets and gets values", func() { c.Set("quota-a", []CompiledTarget{target("Pod"), target("Service")}) value, ok := c.Get("quota-a") Expect(ok).To(BeTrue()) Expect(value).To(HaveLen(2)) Expect(value[0].Kind).To(Equal("Pod")) Expect(value[1].Kind).To(Equal("Service")) Expect(c.Stats()).To(Equal(1)) }) It("returns a copy from Get", func() { c.Set("quota-a", []CompiledTarget{target("Pod")}) value, ok := c.Get("quota-a") Expect(ok).To(BeTrue()) value[0].Kind = "Mutated" value = append(value, target("Service")) again, ok := c.Get("quota-a") Expect(ok).To(BeTrue()) Expect(again).To(HaveLen(1)) Expect(again[0].Kind).To(Equal("Pod")) }) It("stores a copy from Set", func() { original := []CompiledTarget{target("Pod")} c.Set("quota-a", original) original[0].Kind = "Mutated" original = append(original, target("Service")) value, ok := c.Get("quota-a") Expect(ok).To(BeTrue()) Expect(value).To(HaveLen(1)) Expect(value[0].Kind).To(Equal("Pod")) }) It("builds missing values with GetOrBuild", func() { var calls atomic.Int32 value, err := c.GetOrBuild("quota-a", func() ([]CompiledTarget, error) { calls.Add(1) return []CompiledTarget{target("Pod")}, nil }) Expect(err).NotTo(HaveOccurred()) Expect(value).To(HaveLen(1)) Expect(value[0].Kind).To(Equal("Pod")) Expect(calls.Load()).To(Equal(int32(1))) cached, ok := c.Get("quota-a") Expect(ok).To(BeTrue()) Expect(cached).To(HaveLen(1)) }) It("does not call build when key already exists", func() { c.Set("quota-a", []CompiledTarget{target("Pod")}) value, err := c.GetOrBuild("quota-a", func() ([]CompiledTarget, error) { return nil, errors.New("should not be called") }) Expect(err).NotTo(HaveOccurred()) Expect(value).To(HaveLen(1)) Expect(value[0].Kind).To(Equal("Pod")) }) It("does not store value when build fails", func() { expectedErr := errors.New("compile failed") value, err := c.GetOrBuild("quota-a", func() ([]CompiledTarget, error) { return nil, expectedErr }) Expect(err).To(MatchError(expectedErr)) Expect(value).To(BeNil()) _, ok := c.Get("quota-a") Expect(ok).To(BeFalse()) Expect(c.Stats()).To(Equal(0)) }) It("returns a copy from GetOrBuild", func() { value, err := c.GetOrBuild("quota-a", func() ([]CompiledTarget, error) { return []CompiledTarget{target("Pod")}, nil }) Expect(err).NotTo(HaveOccurred()) value[0].Kind = "Mutated" value = append(value, target("Service")) again, err := c.GetOrBuild("quota-a", func() ([]CompiledTarget, error) { return []CompiledTarget{target("ShouldNotBuild")}, nil }) Expect(err).NotTo(HaveOccurred()) Expect(again).To(HaveLen(1)) Expect(again[0].Kind).To(Equal("Pod")) }) It("deletes existing values", func() { c.Set("quota-a", []CompiledTarget{target("Pod")}) Expect(c.Delete("quota-a")).To(BeTrue()) Expect(c.Delete("quota-a")).To(BeFalse()) _, ok := c.Get("quota-a") Expect(ok).To(BeFalse()) Expect(c.Stats()).To(Equal(0)) }) It("resets all values", func() { c.Set("quota-a", []CompiledTarget{target("Pod")}) c.Set("quota-b", []CompiledTarget{target("Service")}) Expect(c.Stats()).To(Equal(2)) c.Reset() Expect(c.Stats()).To(Equal(0)) _, ok := c.Get("quota-a") Expect(ok).To(BeFalse()) }) It("prunes inactive keys", func() { c.Set("quota-a", []CompiledTarget{target("Pod")}) c.Set("quota-b", []CompiledTarget{target("Service")}) c.Set("quota-c", []CompiledTarget{target("PersistentVolumeClaim")}) pruned := c.PruneActive(map[string]struct{}{ "quota-a": {}, "quota-c": {}, }) Expect(pruned).To(Equal(1)) Expect(c.Stats()).To(Equal(2)) _, ok := c.Get("quota-a") Expect(ok).To(BeTrue()) _, ok = c.Get("quota-b") Expect(ok).To(BeFalse()) _, ok = c.Get("quota-c") Expect(ok).To(BeTrue()) }) It("prunes all keys when active set is empty", func() { c.Set("quota-a", []CompiledTarget{target("Pod")}) c.Set("quota-b", []CompiledTarget{target("Service")}) Expect(c.PruneActive(map[string]struct{}{})).To(Equal(2)) Expect(c.Stats()).To(Equal(0)) }) It("is safe under concurrent GetOrBuild calls for the same key", func() { var calls atomic.Int32 const workers = 32 start := make(chan struct{}) var wg sync.WaitGroup wg.Add(workers) for i := 0; i < workers; i++ { go func() { defer GinkgoRecover() defer wg.Done() <-start value, err := c.GetOrBuild("quota-a", func() ([]CompiledTarget, error) { calls.Add(1) return []CompiledTarget{target("Pod")}, nil }) Expect(err).NotTo(HaveOccurred()) Expect(value).To(HaveLen(1)) Expect(value[0].Kind).To(Equal("Pod")) }() } close(start) wg.Wait() Expect(calls.Load()).To(Equal(int32(1))) Expect(c.Stats()).To(Equal(1)) }) })