diff --git a/pytest_tests/testsuites/object/test_object_lock.py b/pytest_tests/testsuites/object/test_object_lock.py index 91bcb7e..0c1585a 100755 --- a/pytest_tests/testsuites/object/test_object_lock.py +++ b/pytest_tests/testsuites/object/test_object_lock.py @@ -126,6 +126,27 @@ def locked_storage_object( logger.debug(ex_message) +@wait_for_success(datetime_utils.parse_time(STORAGE_GC_TIME)) +def check_object_not_found(wallet_file_path: str, cid: str, oid: str, shell: Shell, rpc_endpoint: str): + with pytest.raises(Exception, match=OBJECT_NOT_FOUND): + head_object( + wallet_file_path, + cid, + oid, + shell, + rpc_endpoint, + ) + +def verify_object_available(wallet_file_path: str, cid: str, oid: str, shell: Shell, rpc_endpoint: str): + with expect_not_raises(): + head_object( + wallet_file_path, + cid, + oid, + shell, + rpc_endpoint, + ) + @pytest.mark.sanity @pytest.mark.grpc_object_lock class TestObjectLockWithGrpc(ClusterTestBase): @@ -328,20 +349,13 @@ class TestObjectLockWithGrpc(ClusterTestBase): self.cluster.default_rpc_endpoint, ) - @wait_for_success(datetime_utils.parse_time(STORAGE_GC_TIME)) - def check_object_not_found(): - with pytest.raises(Exception, match=OBJECT_NOT_FOUND): - head_object( - storage_object.wallet_file_path, - storage_object.cid, - storage_object.oid, - self.shell, - self.cluster.default_rpc_endpoint, - ) - with allure.step("Wait for object to be deleted after third epoch"): self.tick_epoch() - check_object_not_found() + check_object_not_found(storage_object.wallet_file_path, + storage_object.cid, + storage_object.oid, + self.shell, + self.cluster.default_rpc_endpoint) @allure.title("Should be possible to lock multiple objects at once for {object_size}") @pytest.mark.parametrize( @@ -658,3 +672,156 @@ class TestObjectLockWithGrpc(ClusterTestBase): self.shell, self.cluster.default_rpc_endpoint, ) + + @allure.title("Expired object should be removed after all locks were expired for {object_size}") + @pytest.mark.parametrize( + "object_size", + [pytest.lazy_fixture("simple_object_size"), pytest.lazy_fixture("complex_object_size")], + ids=["simple object size", "complex object size"], + ) + def test_expired_object_should_be_removed_after_relocks_expare_at( + self, + request: FixtureRequest, + user_container: StorageContainer, + object_size: ObjectSize, + ): + + allure.dynamic.title( + f"Expired object should be removed after all locks were expired for {request.node.callspec.id}" + ) + + current_epoch = self.ensure_fresh_epoch() + storage_object = user_container.generate_object(object_size.value, expire_at=current_epoch + 1) + + with allure.step("Apply first lock to object for 3 epochs"): + lock_object_id_0 = lock_object( + storage_object.wallet_file_path, + storage_object.cid, + storage_object.oid, + self.shell, + self.cluster.default_rpc_endpoint, + expire_at=current_epoch + 3, + ) + + self.tick_epochs(2) + + with allure.step("Check first lock is still available"): + verify_object_available( + storage_object.wallet_file_path, + storage_object.cid, + lock_object_id_0, + self.shell, + self.cluster.default_rpc_endpoint) + + with allure.step("Apply second lock to object for 3 more epochs"): + lock_object_id_1 = lock_object( + storage_object.wallet_file_path, + storage_object.cid, + storage_object.oid, + self.shell, + self.cluster.default_rpc_endpoint, + expire_at=current_epoch + 5, + ) + + self.tick_epochs(2) + + with allure.step("Verify first lock is expired and removed"): + check_object_not_found( + storage_object.wallet_file_path, + storage_object.cid, + lock_object_id_0, + self.shell, + self.cluster.default_rpc_endpoint + ) + + with allure.step("Verify second lock is still available"): + verify_object_available( + storage_object.wallet_file_path, + storage_object.cid, + lock_object_id_1, + self.shell, + self.cluster.default_rpc_endpoint + ) + + with allure.step("Apply third lock to object for 3 more epochs"): + lock_object( + storage_object.wallet_file_path, + storage_object.cid, + storage_object.oid, + self.shell, + self.cluster.default_rpc_endpoint, + expire_at=current_epoch + 7, + ) + + with allure.step("Verify object is deleted after all locks are expired"): + self.tick_epochs(4) + check_object_not_found( + storage_object.wallet_file_path, + storage_object.cid, + storage_object.oid, + self.shell, + self.cluster.default_rpc_endpoint + ) + + @allure.title("Two expired objects with one lock should be deleted after lock expiration for {object_size}") + @pytest.mark.parametrize( + "object_size", + [pytest.lazy_fixture("simple_object_size"), pytest.lazy_fixture("complex_object_size")], + ids=["simple object size", "complex object size"], + ) + def test_two_objects_expiration_with_one_lock( + self, + request: FixtureRequest, + user_container: StorageContainer, + object_size: ObjectSize, + ): + + allure.dynamic.title( + f"Two expired objects with one lock should be deleted after lock expiration for {request.node.callspec.id}" + ) + + current_epoch = self.ensure_fresh_epoch() + storage_objects: list[StorageObjectInfo] = [] + + with allure.step("Generate two objects"): + for epoch_i in range(2): + storage_objects.append( + user_container.generate_object(object_size.value, expire_at=current_epoch + epoch_i + 3) + ) + + self.tick_epoch() + + with allure.step("Lock objects for 4 epochs"): + lock_object( + storage_objects[0].wallet_file_path, + storage_objects[0].cid, + ",".join([storage_object.oid for storage_object in storage_objects]), + self.shell, + self.cluster.default_rpc_endpoint, + expire_at=current_epoch + 4, + ) + + with allure.step("Verify objects are available during next three epochs"): + for epoch_i in range(3): + self.tick_epoch() + with allure.step(f"Check objects at epoch {current_epoch + epoch_i + 2}"): + for storage_object in storage_objects: + verify_object_available( + storage_object.wallet_file_path, + storage_object.cid, + storage_object.oid, + self.shell, + self.cluster.default_rpc_endpoint + ) + + with allure.step("Verify objects are deleted after lock was expired"): + self.tick_epoch() + for storage_object in storage_objects: + check_object_not_found( + storage_object.wallet_file_path, + storage_object.cid, + storage_object.oid, + self.shell, + self.cluster.default_rpc_endpoint + ) + \ No newline at end of file