From 8a41fa78678d59cfb5d859650d7fe694c79a19a4 Mon Sep 17 00:00:00 2001 From: hweawer Date: Wed, 18 Sep 2024 11:04:24 +0200 Subject: [PATCH 1/8] Fix flaky test --- tests/modules/ejector/test_iterator_v2.py | 27 +++++++++++++---------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/tests/modules/ejector/test_iterator_v2.py b/tests/modules/ejector/test_iterator_v2.py index 57fd087cb..cce6d4935 100644 --- a/tests/modules/ejector/test_iterator_v2.py +++ b/tests/modules/ejector/test_iterator_v2.py @@ -78,7 +78,8 @@ def test_calculate_validators_age(iterator, monkeypatch): assert age == 20 -def test_eject_validator(iterator): +@pytest.mark.parametrize('execution_number', range(7)) +def test_eject_validator(execution_number, iterator): sk_1 = StakingModuleFactory.build( id=1, priority_exit_share_threshold=1 * 10000, @@ -95,12 +96,14 @@ def test_eject_validator(iterator): ) no_1 = NodeOperatorFactory.build( + id=1, staking_module=sk_1, total_deposited_validators=3, target_validators_count=1, is_target_limit_active=NodeOperatorLimitMode.FORCE, ) no_2 = NodeOperatorFactory.build( + id=2, staking_module=sk_1, total_deposited_validators=2, is_target_limit_active=NodeOperatorLimitMode.SOFT, @@ -162,7 +165,7 @@ def test_eject_validator(iterator): assert iterator.total_lido_validators == 6 assert iterator.module_stats[1].predictable_validators == 4 assert iterator.node_operators_stats[(1, 1)].predictable_validators == 2 - assert iterator.node_operators_stats[(1, 1)].total_age < prev_total_age + assert iterator.node_operators_stats[(1, 1)].total_age <= prev_total_age iterator.max_validators_to_exit = 3 iterator.no_penetration_threshold = 0.1 @@ -243,19 +246,19 @@ def test_no_force_and_soft_predicate(iterator): # Last two elements have same weight assert [ - nos[0].node_operator.id, - nos[3].node_operator.id, - ] == [ - no.node_operator.id for no in sorted_nos - ][:2] + nos[0].node_operator.id, + nos[3].node_operator.id, + ] == [ + no.node_operator.id for no in sorted_nos + ][:2] sorted_nos = sorted(nos, key=lambda x: -iterator._no_soft_predicate(x)) assert [ - nos[2].node_operator.id, - nos[1].node_operator.id, - ] == [ - no.node_operator.id for no in sorted_nos - ][:2] + nos[2].node_operator.id, + nos[1].node_operator.id, + ] == [ + no.node_operator.id for no in sorted_nos + ][:2] @pytest.mark.unit From 05ef0b62ea06f9b7af97816d156f59bece4a2bd9 Mon Sep 17 00:00:00 2001 From: hweawer Date: Wed, 18 Sep 2024 11:19:11 +0200 Subject: [PATCH 2/8] Reformat with black --- tests/modules/ejector/test_iterator_v2.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/modules/ejector/test_iterator_v2.py b/tests/modules/ejector/test_iterator_v2.py index cce6d4935..d90e8c281 100644 --- a/tests/modules/ejector/test_iterator_v2.py +++ b/tests/modules/ejector/test_iterator_v2.py @@ -246,19 +246,19 @@ def test_no_force_and_soft_predicate(iterator): # Last two elements have same weight assert [ - nos[0].node_operator.id, - nos[3].node_operator.id, - ] == [ - no.node_operator.id for no in sorted_nos - ][:2] + nos[0].node_operator.id, + nos[3].node_operator.id, + ] == [ + no.node_operator.id for no in sorted_nos + ][:2] sorted_nos = sorted(nos, key=lambda x: -iterator._no_soft_predicate(x)) assert [ - nos[2].node_operator.id, - nos[1].node_operator.id, - ] == [ - no.node_operator.id for no in sorted_nos - ][:2] + nos[2].node_operator.id, + nos[1].node_operator.id, + ] == [ + no.node_operator.id for no in sorted_nos + ][:2] @pytest.mark.unit From c77253f7e27b8eb34f0d7e379b93409f37d3eb10 Mon Sep 17 00:00:00 2001 From: hweawer Date: Wed, 18 Sep 2024 13:07:39 +0200 Subject: [PATCH 3/8] Generation with bound --- tests/modules/ejector/test_iterator_v2.py | 46 +++++++++++++---------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/tests/modules/ejector/test_iterator_v2.py b/tests/modules/ejector/test_iterator_v2.py index d90e8c281..e6a34e639 100644 --- a/tests/modules/ejector/test_iterator_v2.py +++ b/tests/modules/ejector/test_iterator_v2.py @@ -2,13 +2,16 @@ from unittest.mock import Mock import pytest +from faker import Faker from src.services.exit_order_v2.iterator import ValidatorExitIteratorV2, NodeOperatorStats, StakingModuleStats from src.web3py.extensions.lido_validators import NodeOperatorLimitMode from tests.factory.blockstamp import ReferenceBlockStampFactory -from tests.factory.no_registry import NodeOperatorFactory, StakingModuleFactory, LidoValidatorFactory +from tests.factory.no_registry import NodeOperatorFactory, StakingModuleFactory, LidoValidatorFactory, ValidatorStateFactory from tests.factory.web3_factory import Web3Factory +faker = Faker() + class ModuleStatsFactory(Web3Factory): __model__ = StakingModuleStats @@ -78,8 +81,7 @@ def test_calculate_validators_age(iterator, monkeypatch): assert age == 20 -@pytest.mark.parametrize('execution_number', range(7)) -def test_eject_validator(execution_number, iterator): +def test_eject_validator(iterator): sk_1 = StakingModuleFactory.build( id=1, priority_exit_share_threshold=1 * 10000, @@ -123,14 +125,20 @@ def test_eject_validator(execution_number, iterator): ] ) + def generate_validator_state_with_activation_epoch_bound(): + return ValidatorStateFactory.build(activation_epoch=faker.pyint(max_value=iterator.blockstamp.ref_epoch - 1)) + iterator.w3.lido_validators.get_lido_validators_by_node_operators = Mock( return_value={ - (1, 1): [LidoValidatorFactory.build(), LidoValidatorFactory.build(), LidoValidatorFactory.build()], - (1, 2): [LidoValidatorFactory.build(), LidoValidatorFactory.build()], + (1, 1): [LidoValidatorFactory.build(validator=generate_validator_state_with_activation_epoch_bound()), + LidoValidatorFactory.build(validator=generate_validator_state_with_activation_epoch_bound()), + LidoValidatorFactory.build(validator=generate_validator_state_with_activation_epoch_bound())], + (1, 2): [LidoValidatorFactory.build(validator=generate_validator_state_with_activation_epoch_bound()), + LidoValidatorFactory.build(validator=generate_validator_state_with_activation_epoch_bound())], (2, 1): [ - LidoValidatorFactory.build(index='8'), - LidoValidatorFactory.build(index='7'), - LidoValidatorFactory.build(index='6'), + LidoValidatorFactory.build(index='8', validator=generate_validator_state_with_activation_epoch_bound()), + LidoValidatorFactory.build(index='7', validator=generate_validator_state_with_activation_epoch_bound()), + LidoValidatorFactory.build(index='6', validator=generate_validator_state_with_activation_epoch_bound()), ], } ) @@ -165,7 +173,7 @@ def test_eject_validator(execution_number, iterator): assert iterator.total_lido_validators == 6 assert iterator.module_stats[1].predictable_validators == 4 assert iterator.node_operators_stats[(1, 1)].predictable_validators == 2 - assert iterator.node_operators_stats[(1, 1)].total_age <= prev_total_age + assert iterator.node_operators_stats[(1, 1)].total_age < prev_total_age iterator.max_validators_to_exit = 3 iterator.no_penetration_threshold = 0.1 @@ -246,19 +254,19 @@ def test_no_force_and_soft_predicate(iterator): # Last two elements have same weight assert [ - nos[0].node_operator.id, - nos[3].node_operator.id, - ] == [ - no.node_operator.id for no in sorted_nos - ][:2] + nos[0].node_operator.id, + nos[3].node_operator.id, + ] == [ + no.node_operator.id for no in sorted_nos + ][:2] sorted_nos = sorted(nos, key=lambda x: -iterator._no_soft_predicate(x)) assert [ - nos[2].node_operator.id, - nos[1].node_operator.id, - ] == [ - no.node_operator.id for no in sorted_nos - ][:2] + nos[2].node_operator.id, + nos[1].node_operator.id, + ] == [ + no.node_operator.id for no in sorted_nos + ][:2] @pytest.mark.unit From 223197ff44e845b88ee6b25eb0e7fbe376adf944 Mon Sep 17 00:00:00 2001 From: hweawer Date: Wed, 18 Sep 2024 13:10:59 +0200 Subject: [PATCH 4/8] Reformat with black --- tests/modules/ejector/test_iterator_v2.py | 41 ++++++++++++++--------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/tests/modules/ejector/test_iterator_v2.py b/tests/modules/ejector/test_iterator_v2.py index e6a34e639..48e2109a9 100644 --- a/tests/modules/ejector/test_iterator_v2.py +++ b/tests/modules/ejector/test_iterator_v2.py @@ -7,7 +7,12 @@ from src.services.exit_order_v2.iterator import ValidatorExitIteratorV2, NodeOperatorStats, StakingModuleStats from src.web3py.extensions.lido_validators import NodeOperatorLimitMode from tests.factory.blockstamp import ReferenceBlockStampFactory -from tests.factory.no_registry import NodeOperatorFactory, StakingModuleFactory, LidoValidatorFactory, ValidatorStateFactory +from tests.factory.no_registry import ( + NodeOperatorFactory, + StakingModuleFactory, + LidoValidatorFactory, + ValidatorStateFactory, +) from tests.factory.web3_factory import Web3Factory faker = Faker() @@ -130,11 +135,15 @@ def generate_validator_state_with_activation_epoch_bound(): iterator.w3.lido_validators.get_lido_validators_by_node_operators = Mock( return_value={ - (1, 1): [LidoValidatorFactory.build(validator=generate_validator_state_with_activation_epoch_bound()), - LidoValidatorFactory.build(validator=generate_validator_state_with_activation_epoch_bound()), - LidoValidatorFactory.build(validator=generate_validator_state_with_activation_epoch_bound())], - (1, 2): [LidoValidatorFactory.build(validator=generate_validator_state_with_activation_epoch_bound()), - LidoValidatorFactory.build(validator=generate_validator_state_with_activation_epoch_bound())], + (1, 1): [ + LidoValidatorFactory.build(validator=generate_validator_state_with_activation_epoch_bound()), + LidoValidatorFactory.build(validator=generate_validator_state_with_activation_epoch_bound()), + LidoValidatorFactory.build(validator=generate_validator_state_with_activation_epoch_bound()), + ], + (1, 2): [ + LidoValidatorFactory.build(validator=generate_validator_state_with_activation_epoch_bound()), + LidoValidatorFactory.build(validator=generate_validator_state_with_activation_epoch_bound()), + ], (2, 1): [ LidoValidatorFactory.build(index='8', validator=generate_validator_state_with_activation_epoch_bound()), LidoValidatorFactory.build(index='7', validator=generate_validator_state_with_activation_epoch_bound()), @@ -254,19 +263,19 @@ def test_no_force_and_soft_predicate(iterator): # Last two elements have same weight assert [ - nos[0].node_operator.id, - nos[3].node_operator.id, - ] == [ - no.node_operator.id for no in sorted_nos - ][:2] + nos[0].node_operator.id, + nos[3].node_operator.id, + ] == [ + no.node_operator.id for no in sorted_nos + ][:2] sorted_nos = sorted(nos, key=lambda x: -iterator._no_soft_predicate(x)) assert [ - nos[2].node_operator.id, - nos[1].node_operator.id, - ] == [ - no.node_operator.id for no in sorted_nos - ][:2] + nos[2].node_operator.id, + nos[1].node_operator.id, + ] == [ + no.node_operator.id for no in sorted_nos + ][:2] @pytest.mark.unit From 569701c7d5a11f788dae09bb75c34e863ea33b52 Mon Sep 17 00:00:00 2001 From: hweawer Date: Wed, 18 Sep 2024 13:26:12 +0200 Subject: [PATCH 5/8] Convert to string --- tests/modules/ejector/test_iterator_v2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/modules/ejector/test_iterator_v2.py b/tests/modules/ejector/test_iterator_v2.py index 48e2109a9..6a9654191 100644 --- a/tests/modules/ejector/test_iterator_v2.py +++ b/tests/modules/ejector/test_iterator_v2.py @@ -131,7 +131,7 @@ def test_eject_validator(iterator): ) def generate_validator_state_with_activation_epoch_bound(): - return ValidatorStateFactory.build(activation_epoch=faker.pyint(max_value=iterator.blockstamp.ref_epoch - 1)) + return ValidatorStateFactory.build(activation_epoch=str(faker.pyint(max_value=iterator.blockstamp.ref_epoch - 1))) iterator.w3.lido_validators.get_lido_validators_by_node_operators = Mock( return_value={ From 87ee53fa5e2563abfe9972c103911d255005538e Mon Sep 17 00:00:00 2001 From: hweawer Date: Wed, 18 Sep 2024 13:28:22 +0200 Subject: [PATCH 6/8] Black formatting --- tests/modules/ejector/test_iterator_v2.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/modules/ejector/test_iterator_v2.py b/tests/modules/ejector/test_iterator_v2.py index 6a9654191..5847b9fb7 100644 --- a/tests/modules/ejector/test_iterator_v2.py +++ b/tests/modules/ejector/test_iterator_v2.py @@ -131,7 +131,9 @@ def test_eject_validator(iterator): ) def generate_validator_state_with_activation_epoch_bound(): - return ValidatorStateFactory.build(activation_epoch=str(faker.pyint(max_value=iterator.blockstamp.ref_epoch - 1))) + return ValidatorStateFactory.build( + activation_epoch=str(faker.pyint(max_value=iterator.blockstamp.ref_epoch - 1)) + ) iterator.w3.lido_validators.get_lido_validators_by_node_operators = Mock( return_value={ From 919f8da515e0d755997a8dd70662226bbab3ccc9 Mon Sep 17 00:00:00 2001 From: hweawer Date: Wed, 18 Sep 2024 13:48:55 +0200 Subject: [PATCH 7/8] Move generation to the model --- tests/factory/no_registry.py | 8 +++++++- tests/modules/ejector/test_iterator_v2.py | 21 ++++++++------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/tests/factory/no_registry.py b/tests/factory/no_registry.py index 140fdf44b..6e23eb701 100644 --- a/tests/factory/no_registry.py +++ b/tests/factory/no_registry.py @@ -1,5 +1,6 @@ import random from itertools import count +from typing import Any from faker import Faker from pydantic_factories import Use @@ -10,7 +11,6 @@ from tests.factory.web3_factory import Web3Factory from src.web3py.extensions.lido_validators import StakingModule, LidoValidator, NodeOperator - faker = Faker() @@ -47,6 +47,12 @@ class LidoValidatorFactory(Web3Factory): index: str = Use(lambda x: str(next(x)), count(1)) balance: str = Use(lambda x: str(x), random.randrange(1, 10**9)) + @classmethod + def build_with_activation_epoch_bound(cls, max_value: int, **kwargs: Any): + return cls.build( + validator=ValidatorStateFactory.build(activation_epoch=str(faker.pyint(max_value=max_value - 1))), **kwargs + ) + class NodeOperatorFactory(Web3Factory): __model__ = NodeOperator diff --git a/tests/modules/ejector/test_iterator_v2.py b/tests/modules/ejector/test_iterator_v2.py index 5847b9fb7..6260f5ec3 100644 --- a/tests/modules/ejector/test_iterator_v2.py +++ b/tests/modules/ejector/test_iterator_v2.py @@ -130,26 +130,21 @@ def test_eject_validator(iterator): ] ) - def generate_validator_state_with_activation_epoch_bound(): - return ValidatorStateFactory.build( - activation_epoch=str(faker.pyint(max_value=iterator.blockstamp.ref_epoch - 1)) - ) - iterator.w3.lido_validators.get_lido_validators_by_node_operators = Mock( return_value={ (1, 1): [ - LidoValidatorFactory.build(validator=generate_validator_state_with_activation_epoch_bound()), - LidoValidatorFactory.build(validator=generate_validator_state_with_activation_epoch_bound()), - LidoValidatorFactory.build(validator=generate_validator_state_with_activation_epoch_bound()), + LidoValidatorFactory.build_with_activation_epoch_bound(iterator.blockstamp.ref_epoch), + LidoValidatorFactory.build_with_activation_epoch_bound(iterator.blockstamp.ref_epoch), + LidoValidatorFactory.build_with_activation_epoch_bound(iterator.blockstamp.ref_epoch), ], (1, 2): [ - LidoValidatorFactory.build(validator=generate_validator_state_with_activation_epoch_bound()), - LidoValidatorFactory.build(validator=generate_validator_state_with_activation_epoch_bound()), + LidoValidatorFactory.build_with_activation_epoch_bound(iterator.blockstamp.ref_epoch), + LidoValidatorFactory.build_with_activation_epoch_bound(iterator.blockstamp.ref_epoch), ], (2, 1): [ - LidoValidatorFactory.build(index='8', validator=generate_validator_state_with_activation_epoch_bound()), - LidoValidatorFactory.build(index='7', validator=generate_validator_state_with_activation_epoch_bound()), - LidoValidatorFactory.build(index='6', validator=generate_validator_state_with_activation_epoch_bound()), + LidoValidatorFactory.build_with_activation_epoch_bound(iterator.blockstamp.ref_epoch, index='8'), + LidoValidatorFactory.build_with_activation_epoch_bound(iterator.blockstamp.ref_epoch, index='7'), + LidoValidatorFactory.build_with_activation_epoch_bound(iterator.blockstamp.ref_epoch, index='6'), ], } ) From 7a2d395b378b7b38375d32208ebf70b5ee2c48b1 Mon Sep 17 00:00:00 2001 From: hweawer Date: Wed, 18 Sep 2024 13:50:11 +0200 Subject: [PATCH 8/8] Remove the imports --- tests/modules/ejector/test_iterator_v2.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/modules/ejector/test_iterator_v2.py b/tests/modules/ejector/test_iterator_v2.py index 6260f5ec3..877538841 100644 --- a/tests/modules/ejector/test_iterator_v2.py +++ b/tests/modules/ejector/test_iterator_v2.py @@ -2,7 +2,6 @@ from unittest.mock import Mock import pytest -from faker import Faker from src.services.exit_order_v2.iterator import ValidatorExitIteratorV2, NodeOperatorStats, StakingModuleStats from src.web3py.extensions.lido_validators import NodeOperatorLimitMode @@ -11,12 +10,9 @@ NodeOperatorFactory, StakingModuleFactory, LidoValidatorFactory, - ValidatorStateFactory, ) from tests.factory.web3_factory import Web3Factory -faker = Faker() - class ModuleStatsFactory(Web3Factory): __model__ = StakingModuleStats