diff --git a/pkg/local_object_storage/metabase/lock_test.go b/pkg/local_object_storage/metabase/lock_test.go index 1aaabf874..6ce41f64e 100644 --- a/pkg/local_object_storage/metabase/lock_test.go +++ b/pkg/local_object_storage/metabase/lock_test.go @@ -326,3 +326,46 @@ func TestGetLocks(t *testing.T) { require.ElementsMatch(t, locks, gotLocks) }) } + +func TestAddMissingExpirationEpochOnLock(t *testing.T) { + t.Parallel() + + db := newDB(t) + defer func() { require.NoError(t, db.Close(context.Background())) }() + + locked := oidtest.Address() + + locks := []meta.Lock{ + {oidtest.ID(), rand.Uint64()}, + {oidtest.ID(), meta.NoExpirationEpoch}, + } + + for _, lock := range locks { + require.NoError(t, db.Lock( + context.Background(), + locked.Container(), + lock.ID, + []oid.ID{locked.Object()}, + lock.ExpEpoch, + )) + } + gotLocks, err := db.GetLocks(context.Background(), locked) + require.NoError(t, err) + require.ElementsMatch(t, locks, gotLocks) + + for i := range locks { + require.NoError(t, db.Lock( + context.Background(), + locked.Container(), + locks[i].ID, []oid.ID{locked.Object()}, + locks[i].ExpEpoch+100, + )) + if locks[i].ExpEpoch == meta.NoExpirationEpoch { + locks[i].ExpEpoch += 100 + } + } + gotLocks, err = db.GetLocks(context.Background(), locked) + require.NoError(t, err) + + require.ElementsMatch(t, locks, gotLocks) +} diff --git a/pkg/local_object_storage/metabase/util_test.go b/pkg/local_object_storage/metabase/util_test.go new file mode 100644 index 000000000..676adef43 --- /dev/null +++ b/pkg/local_object_storage/metabase/util_test.go @@ -0,0 +1,134 @@ +package meta + +import ( + "crypto/rand" + "testing" + + "github.com/stretchr/testify/require" +) + +func Test_lockWithExpEpoch(t *testing.T) { + t.Parallel() + + id1 := make([]byte, objectKeySize) + id2 := make([]byte, objectKeySize) + epoch1 := make([]byte, epochSize) + epoch2 := make([]byte, epochSize) + + noEpoch := make([]byte, epochSize) + + _, _ = rand.Read(id1) + _, _ = rand.Read(id2) + _, _ = rand.Read(epoch1) + _, _ = rand.Read(epoch2) + + for _, testCase := range []struct { + name string + rawLockList [][]byte + ids [][]byte + expEpochs [][]byte + shouldFail bool + }{ + { + name: "nil lock", + }, + { + name: "empty lock", + rawLockList: [][]byte{}, + }, + { + name: "all locks have epochs", + rawLockList: [][]byte{ + id1, epoch1, + id2, epoch2, + id1, epoch2, + }, + ids: [][]byte{id1, id2, id1}, + expEpochs: [][]byte{epoch1, epoch2, epoch2}, + }, + { + name: "some locks have epochs", + rawLockList: [][]byte{ + id1, + id2, epoch1, + id1, epoch2, + id2, + id2, epoch2, + }, + ids: [][]byte{id1, id2, id1, id2, id2}, + expEpochs: [][]byte{noEpoch, epoch1, epoch2, noEpoch, epoch2}, + }, + + { + name: "locks have no epochs", + rawLockList: [][]byte{ + id1, + id2, + id1, + id2, + }, + ids: [][]byte{id1, id2, id1, id2}, + expEpochs: [][]byte{noEpoch, noEpoch, noEpoch, noEpoch}, + }, + { + name: "got an epoch when want an id (in the beginning)", + rawLockList: [][]byte{ + epoch1, + id1, + id2, epoch2, + }, + shouldFail: true, + }, + { + name: "got an epoch when want an id (in the middle)", + rawLockList: [][]byte{ + id1, + id2, epoch2, + epoch1, + id1, epoch1, + id2, epoch2, + }, + shouldFail: true, + }, + { + name: "got an epoch when want an id (in the end)", + rawLockList: [][]byte{ + id1, epoch1, + id2, + id1, + id2, epoch2, + epoch1, + }, + shouldFail: true, + }, + } { + t.Run(testCase.name, func(t *testing.T) { + require.Equal(t, len(testCase.ids), len(testCase.expEpochs), "not a valid test case") + + rawLocks, err := encodeList(testCase.rawLockList) + require.NoError(t, err) + + locks, err := decodeLockWithExpEpochList(rawLocks) + if testCase.shouldFail { + require.Error(t, err) + return + } + + require.NoError(t, err) + require.Equal(t, len(testCase.ids), len(locks)) + + for i := range testCase.ids { + require.Equal(t, testCase.ids[i], locks[i].id[:]) + require.Equal(t, testCase.expEpochs[i], locks[i].expEpoch[:]) + } + + data, err := encodeLockWithExpEpochList(locks) + require.NoError(t, err) + require.Equal(t, rawLocks, data) + + data, err = encodeLockWithExpEpochList(locks) + require.NoError(t, err) + require.Equal(t, rawLocks, data) + }) + } +}