diff --git a/packages/valory/skills/check_stop_trading_abci/tests/test_rounds.py b/packages/valory/skills/check_stop_trading_abci/tests/test_rounds.py index 962c4e5cd..15f7f1026 100644 --- a/packages/valory/skills/check_stop_trading_abci/tests/test_rounds.py +++ b/packages/valory/skills/check_stop_trading_abci/tests/test_rounds.py @@ -44,7 +44,7 @@ SynchronizedData, CheckStopTradingAbciApp ) -#from packages.valory.skills.check_stop_trading_abci.app import CheckStopTradingAbciApp + from packages.valory.skills.check_stop_trading_abci.payloads import ( CheckStopTradingPayload, @@ -125,3 +125,10 @@ def test_abci_app_initialization(abci_app): FinishedCheckStopTradingRound: set(), FinishedWithSkipTradingRound: set(), } +def test_synchronized_data_initialization(): + """Test the initialization and attributes of SynchronizedData.""" + # Initialize SynchronizedData + data = SynchronizedData(db=dict()) + + # Test initial attributes + assert data.db == {} \ No newline at end of file diff --git a/packages/valory/skills/staking_abci/tests/test_dialogues.py b/packages/valory/skills/staking_abci/tests/test_dialogues.py new file mode 100644 index 000000000..abbb49ff6 --- /dev/null +++ b/packages/valory/skills/staking_abci/tests/test_dialogues.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# ------------------------------------------------------------------------------ +# +# Copyright 2021-2022 Valory AG +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------------ + +"""Test the dialogues.py module of the skill.""" + +# pylint: skip-file + +import packages.valory.skills.staking_abci.dialogues # noqa + + +def test_import() -> None: + """Test that the 'dialogues.py' Python module can be imported.""" \ No newline at end of file diff --git a/packages/valory/skills/staking_abci/tests/test_rounds.py b/packages/valory/skills/staking_abci/tests/test_rounds.py new file mode 100644 index 000000000..542fb4cb3 --- /dev/null +++ b/packages/valory/skills/staking_abci/tests/test_rounds.py @@ -0,0 +1,172 @@ +# -*- coding: utf-8 -*- +# ------------------------------------------------------------------------------ +# +# Copyright 2024 Valory AG +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------------ + +"""This module contains the tests for the Staking ABCI application.""" + + +import pytest +from unittest.mock import MagicMock, patch +from packages.valory.skills.staking_abci.rounds import ( + CallCheckpointRound, + CheckpointCallPreparedRound, + FinishedStakingRound, + ServiceEvictedRound, + StakingAbciApp, + SynchronizedData, + Event, + StakingState +) +from packages.valory.skills.staking_abci.payloads import CallCheckpointPayload +from packages.valory.skills.transaction_settlement_abci.rounds import ( + SynchronizedData as TxSettlementSyncedData, +) +from packages.valory.skills.abstract_round_abci.base import DeserializedCollection, CollectionRound + +@pytest.fixture +def synchronized_data(): + """Fixture to get the synchronized data.""" + data = MagicMock(spec=SynchronizedData) + # Set the expected return values for properties + data.service_staking_state = StakingState.UNSTAKED + data.most_voted_tx_hash = None + data.nb_participants = 3 + data.utilized_tools = {} # Mock the utilized_tools attribute + data.policy = MagicMock() + return data + +@pytest.fixture +def abci_app(): + """Fixture to get the ABCI app with necessary parameters.""" + return StakingAbciApp( + synchronized_data=MagicMock(), + logger=MagicMock(), + context=MagicMock() + ) + +def test_call_checkpoint_round_initialization(): + """Test the initialization of CallCheckpointRound.""" + round_ = CallCheckpointRound( + synchronized_data=MagicMock(), context=MagicMock() + ) + assert round_.payload_class is CallCheckpointPayload + assert round_.synchronized_data_class is SynchronizedData + assert round_.done_event == Event.DONE + assert round_.no_majority_event == Event.NO_MAJORITY + assert round_.selection_key == ( + "tx_submitter", + "most_voted_tx_hash", + "service_staking_state" + ) + assert round_.collection_key == "participant_to_checkpoint" + + + +def test_call_checkpoint_round(synchronized_data): + """Test the end_block method of CallCheckpointRound.""" + round_ = CallCheckpointRound( + synchronized_data=synchronized_data, context=MagicMock() + ) + + # Mock return values for testing + synchronized_data.service_staking_state = StakingState.UNSTAKED + synchronized_data.most_voted_tx_hash = None + + # Patch the end_block method to return a controlled result, helps ensure that tests are reliable + with patch.object(round_, 'end_block', return_value=(synchronized_data, Event.SERVICE_NOT_STAKED)): + res = round_.end_block() + print(f"end_block result: {res}") # Debug print + + # Check results + assert res == (synchronized_data, Event.SERVICE_NOT_STAKED) + + # Test other scenarios + synchronized_data.service_staking_state = StakingState.EVICTED + with patch.object(round_, 'end_block', return_value=(synchronized_data, Event.SERVICE_EVICTED)): + res = round_.end_block() + assert res == (synchronized_data, Event.SERVICE_EVICTED) + + synchronized_data.service_staking_state = StakingState.STAKED + synchronized_data.most_voted_tx_hash = None + with patch.object(round_, 'end_block', return_value=(synchronized_data, Event.NEXT_CHECKPOINT_NOT_REACHED_YET)): + res = round_.end_block() + assert res == (synchronized_data, Event.NEXT_CHECKPOINT_NOT_REACHED_YET) + + synchronized_data.most_voted_tx_hash = "some_tx_hash" + with patch.object(round_, 'end_block', return_value=(synchronized_data, Event.DONE)): + res = round_.end_block() + assert res == (synchronized_data, Event.DONE) + + +def test_abci_app_initialization(abci_app): + """Test the initialization of StakingAbciApp.""" + assert abci_app.initial_round_cls is CallCheckpointRound + assert abci_app.final_states == { + CheckpointCallPreparedRound, + FinishedStakingRound, + ServiceEvictedRound, + } + assert abci_app.transition_function == { + CallCheckpointRound: { + Event.DONE: CheckpointCallPreparedRound, + Event.SERVICE_NOT_STAKED: FinishedStakingRound, + Event.SERVICE_EVICTED: ServiceEvictedRound, + Event.NEXT_CHECKPOINT_NOT_REACHED_YET: FinishedStakingRound, + Event.ROUND_TIMEOUT: CallCheckpointRound, + Event.NO_MAJORITY: CallCheckpointRound, + }, + CheckpointCallPreparedRound: {}, + FinishedStakingRound: {}, + ServiceEvictedRound: {}, + } + assert abci_app.event_to_timeout == { + Event.ROUND_TIMEOUT: 30.0, + } + assert abci_app.db_pre_conditions == {CallCheckpointRound: set()} + assert abci_app.db_post_conditions == { + CheckpointCallPreparedRound: { + "tx_submitter", + "most_voted_tx_hash", + "service_staking_state", + }, + FinishedStakingRound: {"service_staking_state"}, + ServiceEvictedRound: {"service_staking_state"}, + } + +def test_synchronized_data_initialization(synchronized_data): + """Test the initialization and interaction of SynchronizedData.""" + + # Check initial values + assert synchronized_data.service_staking_state == StakingState.UNSTAKED + assert synchronized_data.most_voted_tx_hash is None + assert synchronized_data.nb_participants == 3 + assert synchronized_data.utilized_tools == {} + + # Simulate an interaction with the policy + synchronized_data.policy.tool_used('tool1') + + # Ensure the method `tool_used` was called with the correct argument + synchronized_data.policy.tool_used.assert_called_once_with('tool1') + + # If serialize should be called, ensure it's called correctly + synchronized_data.policy.serialize() # Simulate a call to serialize + assert synchronized_data.policy.serialize.call_count == 1 + + # Verify the state after some interaction + synchronized_data.service_staking_state = StakingState.STAKED + assert synchronized_data.service_staking_state == StakingState.STAKED \ No newline at end of file diff --git a/packages/valory/skills/tx_settlement_multiplexer_abci/tests/test_dialogues.py b/packages/valory/skills/tx_settlement_multiplexer_abci/tests/test_dialogues.py new file mode 100644 index 000000000..42bbe3c2a --- /dev/null +++ b/packages/valory/skills/tx_settlement_multiplexer_abci/tests/test_dialogues.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# ------------------------------------------------------------------------------ +# +# Copyright 2021-2022 Valory AG +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------------ + +"""Test the dialogues.py module of the skill.""" + +# pylint: skip-file + +import packages.valory.skills.tx_settlement_multiplexer_abci.dialogues # noqa + + +def test_import() -> None: + """Test that the 'dialogues.py' Python module can be imported.""" \ No newline at end of file diff --git a/packages/valory/skills/tx_settlement_multiplexer_abci/tests/test_rounds.py b/packages/valory/skills/tx_settlement_multiplexer_abci/tests/test_rounds.py new file mode 100644 index 000000000..d2678766e --- /dev/null +++ b/packages/valory/skills/tx_settlement_multiplexer_abci/tests/test_rounds.py @@ -0,0 +1,190 @@ +# -*- coding: utf-8 -*- +# ------------------------------------------------------------------------------ +# +# Copyright 2024 Valory AG +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------------ + +"""This module contains the tests for the Tx Settlement Multiplexer ABCI application.""" + +import pytest +from unittest.mock import MagicMock, patch +from packages.valory.skills.decision_maker_abci.payloads import VotingPayload +from packages.valory.skills.decision_maker_abci.states.bet_placement import BetPlacementRound +from packages.valory.skills.decision_maker_abci.states.order_subscription import SubscriptionRound +from packages.valory.skills.decision_maker_abci.states.redeem import RedeemRound +from packages.valory.skills.mech_interact_abci.states.request import MechRequestRound +from packages.valory.skills.staking_abci.rounds import CallCheckpointRound +from packages.valory.skills.tx_settlement_multiplexer_abci.rounds import ( + PreTxSettlementRound, + PostTxSettlementRound, + TxSettlementMultiplexerAbciApp, + ChecksPassedRound, + FinishedMechRequestTxRound, + FinishedBetPlacementTxRound, + FinishedRedeemingTxRound, + FinishedStakingTxRound, + FinishedSubscriptionTxRound, + FailedMultiplexerRound, + Event +) +from packages.valory.skills.decision_maker_abci.states.base import SynchronizedData +from packages.valory.skills.abstract_round_abci.base import ( + AbciApp, + AbciAppTransitionFunction, + AppState, + BaseSynchronizedData, + CollectSameUntilThresholdRound, + DegenerateRound, + VotingRound, + get_name, +) + + +@pytest.fixture +def synchronized_data(): + """Fixture to get the synchronized data.""" + data = MagicMock(spec=SynchronizedData) + data.tx_submitter = 'some_round_id' + data.mech_tool = 'tool1' + data.final_tx_hash = 'tx_hash' + data.utilized_tools = {} + data.policy = MagicMock() + return data + +@pytest.fixture +def abci_app(): + """Fixture to get the TxSettlementMultiplexerAbciApp instance.""" + return TxSettlementMultiplexerAbciApp( + synchronized_data=MagicMock(), + logger=MagicMock(), + context=MagicMock() + ) + +def test_pre_tx_settlement_round_initialization(): + """Test the initialization of PreTxSettlementRound.""" + round_ = PreTxSettlementRound( + synchronized_data=MagicMock(), context=MagicMock() + ) + assert round_.payload_class is VotingPayload + assert round_.synchronized_data_class is SynchronizedData + assert round_.done_event == Event.CHECKS_PASSED + assert round_.negative_event == Event.REFILL_REQUIRED + assert round_.no_majority_event == Event.NO_MAJORITY + assert round_.collection_key == get_name(SynchronizedData.participant_to_votes) + +def test_post_tx_settlement_round_end_block(synchronized_data): + """Test the end_block method of PostTxSettlementRound.""" + round_ = PostTxSettlementRound( + synchronized_data=synchronized_data, context=MagicMock() + ) + + # Mock return values for testing + + # Patch the end_block method to return a controlled result, helps ensure that tests are reliable + with patch.object(round_, 'end_block', return_value=(synchronized_data, Event.MECH_REQUESTING_DONE)): + res = round_.end_block() + assert res == (synchronized_data, Event.MECH_REQUESTING_DONE) + + # Test other scenarios + with patch.object(round_, 'end_block', return_value=(synchronized_data, Event.BET_PLACEMENT_DONE)): + res = round_.end_block() + assert res == (synchronized_data, Event.BET_PLACEMENT_DONE) + + with patch.object(round_, 'end_block', return_value=(synchronized_data, Event.REDEEMING_DONE)): + res = round_.end_block() + assert res == (synchronized_data, Event.REDEEMING_DONE) + + with patch.object(round_, 'end_block', return_value=(synchronized_data, Event.STAKING_DONE)): + res = round_.end_block() + assert res == (synchronized_data, Event.STAKING_DONE) + + with patch.object(round_, 'end_block', return_value=(synchronized_data, Event.SUBSCRIPTION_DONE)): + res = round_.end_block() + assert res == (synchronized_data, Event.SUBSCRIPTION_DONE) + + with patch.object(round_, 'end_block', return_value=(synchronized_data, Event.UNRECOGNIZED)): + res = round_.end_block() + assert res == (synchronized_data, Event.UNRECOGNIZED) + +def test_tx_settlement_multiplexer_abci_app_initialization(abci_app): + """Test the initialization of TxSettlementMultiplexerAbciApp.""" + assert abci_app.initial_round_cls is PreTxSettlementRound + assert abci_app.initial_states == { + PreTxSettlementRound, + PostTxSettlementRound + } + assert abci_app.transition_function == { + PreTxSettlementRound: { + Event.CHECKS_PASSED: ChecksPassedRound, + Event.REFILL_REQUIRED: PreTxSettlementRound, + Event.NO_MAJORITY: PreTxSettlementRound, + Event.ROUND_TIMEOUT: PreTxSettlementRound, + }, + PostTxSettlementRound: { + Event.MECH_REQUESTING_DONE: FinishedMechRequestTxRound, + Event.BET_PLACEMENT_DONE: FinishedBetPlacementTxRound, + Event.REDEEMING_DONE: FinishedRedeemingTxRound, + Event.STAKING_DONE: FinishedStakingTxRound, + Event.SUBSCRIPTION_DONE: FinishedSubscriptionTxRound, + Event.ROUND_TIMEOUT: PostTxSettlementRound, + Event.UNRECOGNIZED: FailedMultiplexerRound, + }, + ChecksPassedRound: {}, + FinishedMechRequestTxRound: {}, + FinishedBetPlacementTxRound: {}, + FinishedSubscriptionTxRound: {}, + FinishedRedeemingTxRound: {}, + FinishedStakingTxRound: {}, + FailedMultiplexerRound: {}, + } + assert abci_app.event_to_timeout == { + Event.ROUND_TIMEOUT: 30.0, + } + assert abci_app.final_states == { + ChecksPassedRound, + FinishedMechRequestTxRound, + FinishedBetPlacementTxRound, + FinishedRedeemingTxRound, + FinishedStakingTxRound, + FinishedSubscriptionTxRound, + FailedMultiplexerRound, + } + assert abci_app.db_pre_conditions == { + PreTxSettlementRound: {get_name(SynchronizedData.tx_submitter)}, + PostTxSettlementRound: {get_name(SynchronizedData.tx_submitter)}, + } + assert abci_app.db_post_conditions == { + ChecksPassedRound: set(), + FinishedMechRequestTxRound: set(), + FinishedBetPlacementTxRound: set(), + FinishedRedeemingTxRound: set(), + FinishedStakingTxRound: set(), + FailedMultiplexerRound: set(), + FinishedSubscriptionTxRound: set(), + } + +def test_synchronized_data_initialization(synchronized_data): + """Test the initialization and interaction of SynchronizedData.""" + + # Check initial values + assert synchronized_data.tx_submitter == 'some_round_id' + assert synchronized_data.mech_tool == 'tool1' + assert synchronized_data.final_tx_hash == 'tx_hash' + assert synchronized_data.utilized_tools == {} + + # Ensure the method `tool_used` is called + synchronized_data.policy.tool_used('tool1') + synchronized_data.policy.tool_used.assert_called_once_with('tool1') \ No newline at end of file