Files
client-go/tools/cache/store_test.go
Min Jin d5cd2dd65f Adding batch handling for popping items from RealFIFO
Signed-off-by: Min Jin <minkimzz@amazon.com>

Update staging/src/k8s.io/client-go/tools/cache/the_real_fifo.go

optimizing fifo loop

Co-authored-by: Marek Siarkowicz <marek.siarkowicz@protonmail.com>
Signed-off-by: Min Jin <minkimzz@amazon.com>

refactoring PopBatch to accept []Delta

Signed-off-by: Min Jin <minkimzz@amazon.com>

Kubernetes-commit: 611b4c1408f529de4d4e94e6dd33be2ed1df9276
2025-10-24 18:07:32 -07:00

249 lines
6.0 KiB
Go

/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cache
import (
"errors"
"sync/atomic"
"testing"
"github.com/stretchr/testify/assert"
"k8s.io/apimachinery/pkg/util/sets"
)
// Test public interface
func doTestStore(t *testing.T, store Store) {
mkObj := func(id string, val string) testStoreObject {
return testStoreObject{id: id, val: val}
}
store.Add(mkObj("foo", "bar"))
if item, ok, _ := store.Get(mkObj("foo", "")); !ok {
t.Errorf("didn't find inserted item")
} else {
if e, a := "bar", item.(testStoreObject).val; e != a {
t.Errorf("expected %v, got %v", e, a)
}
}
store.Update(mkObj("foo", "baz"))
if item, ok, _ := store.Get(mkObj("foo", "")); !ok {
t.Errorf("didn't find inserted item")
} else {
if e, a := "baz", item.(testStoreObject).val; e != a {
t.Errorf("expected %v, got %v", e, a)
}
}
store.Delete(mkObj("foo", ""))
if _, ok, _ := store.Get(mkObj("foo", "")); ok {
t.Errorf("found deleted item??")
}
// Test List.
store.Add(mkObj("a", "b"))
store.Add(mkObj("c", "d"))
store.Add(mkObj("e", "e"))
{
found := sets.Set[string]{}
for _, item := range store.List() {
found.Insert(item.(testStoreObject).val)
}
if !found.HasAll("b", "d", "e") {
t.Errorf("missing items, found: %v", found)
}
if len(found) != 3 {
t.Errorf("extra items")
}
}
// Test Replace.
store.Replace([]interface{}{
mkObj("foo", "foo"),
mkObj("bar", "bar"),
}, "0")
{
found := sets.Set[string]{}
for _, item := range store.List() {
found.Insert(item.(testStoreObject).val)
}
if !found.HasAll("foo", "bar") {
t.Errorf("missing items")
}
if len(found) != 2 {
t.Errorf("extra items")
}
}
}
// Test public interface
func doTestIndex(t *testing.T, indexer Indexer) {
mkObj := func(id string, val string) testStoreObject {
return testStoreObject{id: id, val: val}
}
// Test Index
expected := map[string]sets.Set[string]{}
expected["b"] = sets.New("a", "c")
expected["f"] = sets.New("e")
expected["h"] = sets.New("g")
indexer.Add(mkObj("a", "b"))
indexer.Add(mkObj("c", "b"))
indexer.Add(mkObj("e", "f"))
indexer.Add(mkObj("g", "h"))
{
for k, v := range expected {
found := sets.Set[string]{}
indexResults, err := indexer.Index("by_val", mkObj("", k))
if err != nil {
t.Errorf("Unexpected error %v", err)
}
for _, item := range indexResults {
found.Insert(item.(testStoreObject).id)
}
items := sets.List(v)
if !found.HasAll(items...) {
t.Errorf("missing items, index %s, expected %v but found %v", k, items, sets.List(found))
}
}
}
}
func testStoreKeyFunc(obj interface{}) (string, error) {
return obj.(testStoreObject).id, nil
}
func testStoreIndexFunc(obj interface{}) ([]string, error) {
return []string{obj.(testStoreObject).val}, nil
}
func testStoreIndexers() Indexers {
indexers := Indexers{}
indexers["by_val"] = testStoreIndexFunc
return indexers
}
type testStoreObject struct {
id string
val string
}
func TestCache(t *testing.T) {
doTestStore(t, NewStore(testStoreKeyFunc))
}
func TestCacheWithTransformer(t *testing.T) {
transformerCalled := &atomic.Bool{}
doTestStore(t, NewStore(testStoreKeyFunc, WithTransformer(func(i interface{}) (interface{}, error) {
transformerCalled.Store(true)
obj, ok := i.(testStoreObject)
if !ok {
return nil, errors.New("wrong object type")
}
return obj, nil
})))
if !transformerCalled.Load() {
t.Error("informer was not called")
}
}
func TestFIFOCache(t *testing.T) {
doTestStore(t, NewFIFO(testStoreKeyFunc))
}
func TestUndeltaStore(t *testing.T) {
nop := func([]interface{}) {}
doTestStore(t, NewUndeltaStore(nop, testStoreKeyFunc))
}
func TestIndex(t *testing.T) {
doTestIndex(t, NewIndexer(testStoreKeyFunc, testStoreIndexers()))
}
func TestKeyError(t *testing.T) {
obj := 100
err := errors.New("error")
keyErr := KeyError{obj, err}
if errors.Unwrap(keyErr) != err {
t.Errorf("expected unwrap error: %v", err)
}
nestedKeyErr := KeyError{obj, keyErr}
if !errors.Is(keyErr, err) || !errors.Is(nestedKeyErr, err) {
t.Errorf("not match target error: %v", err)
}
}
func TestCacheTransactionShouldIndexErrors(t *testing.T) {
successObj1 := 1
successObj2 := 2
failObj1 := 3
failObj2 := 4
testTxnType := TransactionTypeAdd
testCases := []struct {
name string
objs []interface{}
assertError func(*TransactionError) bool
}{
{
name: "txn all success objects should work",
objs: []interface{}{successObj1, successObj2},
},
{
name: "txn all fail objects should work",
objs: []interface{}{failObj1, failObj2},
assertError: func(err *TransactionError) bool {
return assert.Equal(t, []int{}, err.SuccessfulIndices)
},
},
{
name: "txn mix success and fail objects should work",
objs: []interface{}{successObj1, failObj1, successObj2, failObj2},
assertError: func(err *TransactionError) bool {
return assert.Equal(t, []int{0, 2}, err.SuccessfulIndices)
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
testKeyFunc := func(obj interface{}) (string, error) {
if obj == successObj1 || obj == successObj2 {
return "", nil
}
return "", errors.New("test error")
}
testStore := NewStore(testKeyFunc)
txnStore := testStore.(TransactionStore)
txns := make([]Transaction, len(tc.objs))
for i := range tc.objs {
txns[i] = Transaction{
Object: tc.objs[i],
Type: testTxnType,
}
}
txnErr := txnStore.Transaction(txns...)
if tc.assertError != nil {
tc.assertError(txnErr)
return
}
if txnErr != nil {
t.Errorf("unexpected error: %v", txnErr)
}
})
}
}