github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/controller/registry/resolver/cache/cache_test.go (about) 1 package cache 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "math/rand" 8 "strconv" 9 "testing" 10 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 ) 14 15 func TestOperatorCacheConcurrency(t *testing.T) { 16 const ( 17 NWorkers = 64 18 ) 19 20 sp := make(StaticSourceProvider) 21 var keys []SourceKey 22 for i := 0; i < 128; i++ { 23 for j := 0; j < 8; j++ { 24 key := SourceKey{Namespace: strconv.Itoa(i), Name: strconv.Itoa(j)} 25 keys = append(keys, key) 26 sp[key] = &Snapshot{ 27 Entries: []*Entry{ 28 {Name: fmt.Sprintf("%s/%s", key.Namespace, key.Name)}, 29 }, 30 } 31 } 32 } 33 34 c := New(sp) 35 36 errs := make(chan error) 37 for w := 0; w < NWorkers; w++ { 38 go func(w int) (result error) { 39 defer func() { errs <- result }() 40 41 rand := rand.New(rand.NewSource(int64(w))) 42 indices := rand.Perm(len(keys))[:8] 43 namespaces := make([]string, len(indices)) 44 for i, index := range indices { 45 namespaces[i] = keys[index].Namespace 46 } 47 48 nc := c.Namespaced(namespaces...) 49 for _, index := range indices { 50 name := fmt.Sprintf("%s/%s", keys[index].Namespace, keys[index].Name) 51 operators := nc.Find(CSVNamePredicate(name)) 52 if len(operators) != 1 { 53 return fmt.Errorf("expected 1 operator, got %d", len(operators)) 54 } 55 } 56 57 return nil 58 }(w) 59 } 60 61 for w := 0; w < NWorkers; w++ { 62 assert.NoError(t, <-errs) 63 } 64 } 65 66 func TestOperatorCacheExpiration(t *testing.T) { 67 key := SourceKey{Namespace: "dummynamespace", Name: "dummyname"} 68 ssp := make(StaticSourceProvider) 69 c := New(ssp) 70 71 ssp[key] = &Snapshot{ 72 Entries: []*Entry{ 73 {Name: "v1"}, 74 }, 75 Valid: ValidOnce(), 76 } 77 require.Len(t, c.Namespaced("dummynamespace").Catalog(key).Find(CSVNamePredicate("v1")), 1) 78 79 ssp[key] = &Snapshot{ 80 Entries: []*Entry{ 81 {Name: "v2"}, 82 }, 83 } 84 require.Len(t, c.Namespaced("dummynamespace").Catalog(key).Find(CSVNamePredicate("v1")), 0) 85 } 86 87 func TestOperatorCacheReuse(t *testing.T) { 88 key := SourceKey{Namespace: "dummynamespace", Name: "dummyname"} 89 ssp := make(StaticSourceProvider) 90 c := New(ssp) 91 92 ssp[key] = &Snapshot{ 93 Entries: []*Entry{ 94 {Name: "v1"}, 95 }, 96 } 97 require.Len(t, c.Namespaced("dummynamespace").Catalog(key).Find(CSVNamePredicate("v1")), 1) 98 99 ssp[key] = &Snapshot{ 100 Entries: []*Entry{ 101 {Name: "v2"}, 102 }, 103 } 104 require.Len(t, c.Namespaced("dummynamespace").Catalog(key).Find(CSVNamePredicate("v1")), 1) 105 } 106 107 func TestCatalogSnapshotValid(t *testing.T) { 108 type tc struct { 109 Name string 110 Snapshot *Snapshot 111 Error error 112 Expected bool 113 } 114 115 for _, tt := range []tc{ 116 { 117 Name: "invalidated", 118 Snapshot: &Snapshot{ 119 Valid: ValidOnce(), 120 }, 121 Error: nil, 122 Expected: false, 123 }, 124 { 125 Name: "valid", 126 Snapshot: &Snapshot{}, // valid forever 127 Error: nil, 128 Expected: true, 129 }, 130 { 131 Name: "nil snapshot and non-nil error", 132 Snapshot: nil, 133 Error: errors.New(""), 134 Expected: false, 135 }, 136 { 137 Name: "non-nil snapshot and non-nil error", 138 Snapshot: &Snapshot{}, 139 Error: errors.New(""), 140 Expected: false, 141 }, 142 { 143 Name: "nil snapshot and nil error", 144 Snapshot: nil, 145 Error: nil, 146 Expected: false, 147 }, 148 } { 149 t.Run(tt.Name, func(t *testing.T) { 150 s := snapshotHeader{ 151 snapshot: tt.Snapshot, 152 err: tt.Error, 153 } 154 assert.Equal(t, tt.Expected, s.Valid()) 155 }) 156 } 157 } 158 159 func TestCatalogSnapshotFind(t *testing.T) { 160 type tc struct { 161 Name string 162 Predicate Predicate 163 Operators []*Entry 164 Expected []*Entry 165 } 166 167 for _, tt := range []tc{ 168 { 169 Name: "nothing satisfies predicate", 170 Predicate: OperatorPredicateTestFunc(func(*Entry) bool { 171 return false 172 }), 173 Operators: []*Entry{ 174 {Name: "a"}, 175 {Name: "b"}, 176 {Name: "c"}, 177 }, 178 Expected: nil, 179 }, 180 { 181 Name: "no operators in snapshot", 182 Predicate: OperatorPredicateTestFunc(func(*Entry) bool { 183 return true 184 }), 185 Operators: nil, 186 Expected: nil, 187 }, 188 { 189 Name: "everything satisfies predicate", 190 Predicate: OperatorPredicateTestFunc(func(*Entry) bool { 191 return true 192 }), 193 Operators: []*Entry{ 194 {Name: "a"}, 195 {Name: "b"}, 196 {Name: "c"}, 197 }, 198 Expected: []*Entry{ 199 {Name: "a"}, 200 {Name: "b"}, 201 {Name: "c"}, 202 }, 203 }, 204 { 205 Name: "some satisfy predicate", 206 Predicate: OperatorPredicateTestFunc(func(o *Entry) bool { 207 return o.Name != "a" 208 }), 209 Operators: []*Entry{ 210 {Name: "a"}, 211 {Name: "b"}, 212 {Name: "c"}, 213 }, 214 Expected: []*Entry{ 215 {Name: "b"}, 216 {Name: "c"}, 217 }, 218 }, 219 } { 220 t.Run(tt.Name, func(t *testing.T) { 221 s := snapshotHeader{snapshot: &Snapshot{Entries: tt.Operators}} 222 assert.Equal(t, tt.Expected, s.Find(tt.Predicate)) 223 }) 224 } 225 } 226 227 type ErrorSource struct { 228 Error error 229 } 230 231 func (s ErrorSource) Snapshot(context.Context) (*Snapshot, error) { 232 return nil, s.Error 233 } 234 235 func TestNamespaceOperatorCacheError(t *testing.T) { 236 key := SourceKey{Namespace: "dummynamespace", Name: "dummyname"} 237 c := New(StaticSourceProvider{ 238 key: ErrorSource{Error: errors.New("testing")}, 239 }) 240 241 require.EqualError(t, c.Namespaced("dummynamespace").Error(), "error using catalogsource dummynamespace/dummyname: testing") 242 }