github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/graveler/settings/manager_test.go (about) 1 package settings_test 2 3 import ( 4 "context" 5 "testing" 6 7 "github.com/go-openapi/swag" 8 "github.com/go-test/deep" 9 "github.com/golang/mock/gomock" 10 "github.com/stretchr/testify/require" 11 "github.com/treeverse/lakefs/pkg/cache" 12 "github.com/treeverse/lakefs/pkg/graveler" 13 "github.com/treeverse/lakefs/pkg/graveler/mock" 14 "github.com/treeverse/lakefs/pkg/graveler/settings" 15 "github.com/treeverse/lakefs/pkg/kv/kvtest" 16 "github.com/treeverse/lakefs/pkg/testutil" 17 ) 18 19 type mockCache struct { 20 c map[interface{}]interface{} 21 } 22 23 var repository = &graveler.RepositoryRecord{ 24 RepositoryID: "example-repo", 25 Repository: &graveler.Repository{ 26 StorageNamespace: "mem://my-storage", 27 DefaultBranchID: "main", 28 }, 29 } 30 31 func (m *mockCache) GetOrSet(k interface{}, setFn cache.SetFn) (v interface{}, err error) { 32 if val, ok := m.c[k]; ok { 33 return val, nil 34 } 35 val, err := setFn() 36 if err != nil { 37 return nil, err 38 } 39 m.c[k] = val 40 return val, nil 41 } 42 43 func (m *mockCache) GetOrSetWithExpiry(k interface{}, setFn cache.SetFnWithExpiry) (v interface{}, err error) { 44 // Settings does not use expiry. 45 panic("Not implemented.") 46 } 47 48 func TestNonExistent(t *testing.T) { 49 ctx := context.Background() 50 m := prepareTest(t, ctx, nil, nil) 51 setting := &settings.ExampleSettings{} 52 err := m.Get(ctx, repository, "settingKey", setting) 53 require.NoError(t, err) 54 // should return empty string as checksum without an error 55 checksum, err := m.GetLatest(ctx, repository, "settingKey", setting) 56 require.NoError(t, err) 57 require.NotNil(t, checksum) 58 require.Equal(t, "", *checksum) 59 } 60 61 func TestSaveAndGet(t *testing.T) { 62 ctx := context.Background() 63 mc := &mockCache{ 64 c: make(map[interface{}]interface{}), 65 } 66 m := prepareTest(t, ctx, mc, nil) 67 firstSettings := newSetting(5, 6, "hello") 68 err := m.Save(ctx, repository, "settingKey", firstSettings, nil) 69 testutil.Must(t, err) 70 gotSettings := &settings.ExampleSettings{} 71 err = m.Get(ctx, repository, "settingKey", gotSettings) 72 testutil.Must(t, err) 73 if diff := deep.Equal(firstSettings, gotSettings); diff != nil { 74 t.Fatal("got unexpected settings:", diff) 75 } 76 secondSettings := newSetting(15, 16, "hi") 77 err = m.Save(ctx, repository, "settingKey", secondSettings, nil) 78 testutil.Must(t, err) 79 // the result should be cached, and we should get the first settings: 80 gotSettings = &settings.ExampleSettings{} 81 err = m.Get(ctx, repository, "settingKey", gotSettings) 82 testutil.Must(t, err) 83 if diff := deep.Equal(firstSettings, gotSettings); diff != nil { 84 t.Fatal("got unexpected settings:", diff) 85 } 86 // after clearing the mc, we should get the new settings: 87 mc.c = make(map[interface{}]interface{}) 88 gotSettings = &settings.ExampleSettings{} 89 err = m.Get(ctx, repository, "settingKey", gotSettings) 90 testutil.Must(t, err) 91 if diff := deep.Equal(secondSettings, gotSettings); diff != nil { 92 t.Fatal("got unexpected settings:", diff) 93 } 94 } 95 96 func TestGetLatest(t *testing.T) { 97 ctx := context.Background() 98 m := prepareTest(t, ctx, nil, nil) 99 err := m.Save(ctx, repository, "settingKey", newSetting(5, 6, "hello"), nil) 100 testutil.Must(t, err) 101 setting := &settings.ExampleSettings{} 102 eTag, err := m.GetLatest(ctx, repository, "settingKey", setting) 103 testutil.Must(t, err) 104 if diff := deep.Equal(newSetting(5, 6, "hello"), setting); diff != nil { 105 t.Fatal("got unexpected settings:", diff) 106 } 107 if eTag == nil || *eTag == "" { 108 t.Fatal("got empty eTag") 109 } 110 } 111 112 func TestConditionalUpdate(t *testing.T) { 113 ctx := context.Background() 114 mc := &mockCache{ 115 c: make(map[interface{}]interface{}), 116 } 117 m := prepareTest(t, ctx, mc, nil) 118 firstSettings := newSetting(5, 6, "hello") 119 err := m.Save(ctx, repository, "settingKey", firstSettings, swag.String("WRONG_CHECKSUM")) 120 require.ErrorIs(t, err, graveler.ErrPreconditionFailed) 121 err = m.Save(ctx, repository, "settingKey", firstSettings, swag.String("")) 122 testutil.Must(t, err) 123 gotSettings := &settings.ExampleSettings{} 124 checksum, err := m.GetLatest(ctx, repository, "settingKey", gotSettings) 125 testutil.Must(t, err) 126 if diff := deep.Equal(firstSettings, gotSettings); diff != nil { 127 t.Fatal("got unexpected settings:", diff) 128 } 129 secondSettings := newSetting(15, 16, "hi") 130 err = m.Save(ctx, repository, "settingKey", secondSettings, checksum) 131 testutil.Must(t, err) 132 err = m.Save(ctx, repository, "settingKey", secondSettings, swag.String("WRONG_CHECKSUM")) 133 require.ErrorIs(t, err, graveler.ErrPreconditionFailed) 134 } 135 136 func prepareTest(t *testing.T, ctx context.Context, refCache cache.Cache, branchLockCallback func(context.Context, *graveler.RepositoryRecord, graveler.BranchID, func() (interface{}, error)) (interface{}, error)) *settings.Manager { 137 ctrl := gomock.NewController(t) 138 refManager := mock.NewMockRefManager(ctrl) 139 140 branchLock := mock.NewMockBranchLocker(ctrl) 141 cb := func(_ context.Context, _ *graveler.RepositoryRecord, _ graveler.BranchID, f func() (interface{}, error)) (interface{}, error) { 142 return f() 143 } 144 if branchLockCallback != nil { 145 cb = branchLockCallback 146 } 147 var opts []settings.ManagerOption 148 if refCache == nil { 149 refCache = cache.NoCache 150 } 151 opts = append(opts, settings.WithCache(refCache)) 152 branchLock.EXPECT().MetadataUpdater(ctx, gomock.Eq(repository), graveler.BranchID("main"), gomock.Any()).DoAndReturn(cb).AnyTimes() 153 kvStore := kvtest.GetStore(ctx, t) 154 m := settings.NewManager(refManager, kvStore, opts...) 155 156 refManager.EXPECT().GetRepository(ctx, gomock.Eq(repository.RepositoryID)).AnyTimes().Return(repository, nil) 157 return m 158 } 159 160 func newSetting(a int32, b int32, c string) *settings.ExampleSettings { 161 return &settings.ExampleSettings{ 162 ExampleInt: a, 163 ExampleStr: c, 164 ExampleMap: map[string]int32{"boo": b}, 165 } 166 }