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  }