package engine import ( "os" "strconv" "testing" "time" objectV2 "github.com/nspcc-dev/neofs-api-go/v2/object" objectcore "github.com/nspcc-dev/neofs-node/pkg/core/object" "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/shard" "github.com/nspcc-dev/neofs-node/pkg/util" apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status" cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test" "github.com/nspcc-dev/neofs-sdk-go/object" oid "github.com/nspcc-dev/neofs-sdk-go/object/id" oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test" "github.com/panjf2000/ants/v2" "github.com/stretchr/testify/require" ) func TestLockUserScenario(t *testing.T) { t.Skip("posted bug neofs-node#1227") // Tested user actions: // 1. stores some object // 2. locks the object // 3. tries to inhume the object with tombstone and expects failure // 4. saves tombstone for LOCK-object and inhumes the LOCK-object using it // 5. waits for an epoch after the tombstone expiration one // 6. tries to inhume the object and expects success chEvents := make([]chan shard.Event, 2) for i := range chEvents { chEvents[i] = make(chan shard.Event, 1) } e := testEngineFromShardOpts(t, 2, func(i int) []shard.Option { return []shard.Option{ shard.WithGCEventChannel(chEvents[i]), shard.WithGCWorkerPoolInitializer(func(sz int) util.WorkerPool { pool, err := ants.NewPool(sz) require.NoError(t, err) return pool }), } }) t.Cleanup(func() { _ = e.Close() _ = os.RemoveAll(t.Name()) }) const lockerTombExpiresAfter = 13 lockerID := oidtest.ID() tombForLockID := oidtest.ID() tombID := oidtest.ID() cnr := cidtest.ID() var err error var objAddr oid.Address objAddr.SetContainer(cnr) var tombAddr oid.Address tombAddr.SetContainer(cnr) tombAddr.SetObject(tombID) var lockerAddr oid.Address lockerAddr.SetContainer(cnr) lockerAddr.SetObject(lockerID) var tombForLockAddr oid.Address tombForLockAddr.SetContainer(cnr) tombForLockAddr.SetObject(tombForLockID) // 1. obj := generateObjectWithCID(t, cnr) id, _ := obj.ID() objAddr.SetObject(id) err = Put(e, obj) require.NoError(t, err) // 2. err = e.Lock(cnr, lockerID, []oid.ID{id}) require.NoError(t, err) // 3. _, err = e.Inhume(new(InhumePrm).WithTarget(tombAddr, objAddr)) require.ErrorAs(t, err, new(apistatus.ObjectLocked)) // 4. var a object.Attribute a.SetKey(objectV2.SysAttributeExpEpoch) a.SetValue(strconv.Itoa(lockerTombExpiresAfter)) tombObj := generateObjectWithCID(t, cnr) tombObj.SetType(object.TypeTombstone) tombObj.SetID(tombForLockID) tombObj.SetAttributes(a) err = Put(e, tombObj) require.NoError(t, err) _, err = e.Inhume(new(InhumePrm).WithTarget(tombForLockAddr, lockerAddr)) require.NoError(t, err, new(apistatus.ObjectLocked)) // 5. for i := range chEvents { chEvents[i] <- shard.EventNewEpoch(lockerTombExpiresAfter + 1) } // delay for GC time.Sleep(time.Second) _, err = e.Inhume(new(InhumePrm).WithTarget(tombAddr, objAddr)) require.NoError(t, err) } func TestLockExpiration(t *testing.T) { // Tested scenario: // 1. some object is stored // 2. lock object for it is stored, and the object is locked // 3. lock expiration epoch is coming // 4. after some delay the object is not locked anymore chEvents := make([]chan shard.Event, 2) for i := range chEvents { chEvents[i] = make(chan shard.Event, 1) } e := testEngineFromShardOpts(t, 2, func(i int) []shard.Option { return []shard.Option{ shard.WithGCEventChannel(chEvents[i]), shard.WithGCWorkerPoolInitializer(func(sz int) util.WorkerPool { pool, err := ants.NewPool(sz) require.NoError(t, err) return pool }), } }) t.Cleanup(func() { _ = e.Close() _ = os.RemoveAll(t.Name()) }) const lockerExpiresAfter = 13 cnr := cidtest.ID() var err error // 1. obj := generateObjectWithCID(t, cnr) err = Put(e, obj) require.NoError(t, err) // 2. var a object.Attribute a.SetKey(objectV2.SysAttributeExpEpoch) a.SetValue(strconv.Itoa(lockerExpiresAfter)) lock := generateObjectWithCID(t, cnr) lock.SetType(object.TypeLock) lock.SetAttributes(a) err = Put(e, lock) require.NoError(t, err) id, _ := obj.ID() idLock, _ := lock.ID() err = e.Lock(cnr, idLock, []oid.ID{id}) require.NoError(t, err) _, err = e.Inhume(new(InhumePrm).WithTarget(oidtest.Address(), objectcore.AddressOf(obj))) require.ErrorAs(t, err, new(apistatus.ObjectLocked)) // 3. for i := range chEvents { chEvents[i] <- shard.EventNewEpoch(lockerExpiresAfter + 1) } // delay for GC processing. It can't be estimated, but making it bigger // will slow down test time.Sleep(time.Second) // 4. _, err = e.Inhume(new(InhumePrm).WithTarget(oidtest.Address(), objectcore.AddressOf(obj))) require.NoError(t, err) }