From ae14c892fbe4c6670536a8686c7c0121c24651c0 Mon Sep 17 00:00:00 2001 From: "nadine.loepfe" Date: Thu, 2 Jan 2025 14:55:08 +0100 Subject: [PATCH] topic message query test added --- examples/topic_message_submit.py | 4 +- .../consensus/topic_message.py | 23 ++++--- .../query/topic_message_query.py | 5 +- tests/test_topic_message_query.py | 68 +++++++++++++++++++ uv.lock | 38 +++++------ 5 files changed, 108 insertions(+), 30 deletions(-) create mode 100644 tests/test_topic_message_query.py diff --git a/examples/topic_message_submit.py b/examples/topic_message_submit.py index b0cf4af..89a4a1d 100644 --- a/examples/topic_message_submit.py +++ b/examples/topic_message_submit.py @@ -17,7 +17,7 @@ def submit_message(message): operator_id = AccountId.from_string(os.getenv('OPERATOR_ID')) operator_key = PrivateKey.from_string(os.getenv('OPERATOR_KEY')) - topic_id = TopicId.from_string("0.0.5337028") + topic_id = TopicId.from_string(os.getenv('TOPIC_ID')) client.set_operator(operator_id, operator_key) @@ -35,4 +35,4 @@ def submit_message(message): sys.exit(1) if __name__ == "__main__": - submit_message("four") \ No newline at end of file + submit_message("Hello, Hedera!") \ No newline at end of file diff --git a/src/hedera_sdk_python/consensus/topic_message.py b/src/hedera_sdk_python/consensus/topic_message.py index 4d3849e..8eddb8b 100644 --- a/src/hedera_sdk_python/consensus/topic_message.py +++ b/src/hedera_sdk_python/consensus/topic_message.py @@ -1,4 +1,5 @@ from hedera_sdk_python.hapi.mirror import consensus_service_pb2 as mirror_proto +from datetime import datetime class TopicMessage: """ @@ -6,7 +7,7 @@ class TopicMessage: """ def __init__(self, consensus_timestamp, message, running_hash, sequence_number): - self.consensus_timestamp = consensus_timestamp + self.consensus_timestamp = consensus_timestamp self.message = message or b"" self.running_hash = running_hash or b"" self.sequence_number = sequence_number or 0 @@ -20,15 +21,21 @@ def from_proto(cls, response: mirror_proto.ConsensusTopicResponse) -> "TopicMess consensus_timestamp=response.consensusTimestamp, message=response.message, running_hash=response.runningHash, - sequence_number=response.sequenceNumber + sequence_number=response.sequenceNumber, ) def __str__(self): + """ + Returns a nicely formatted string representation of the topic message. + """ + timestamp = datetime.utcfromtimestamp(self.consensus_timestamp.seconds).strftime('%Y-%m-%d %H:%M:%S UTC') + message = self.message.decode('utf-8', errors='ignore') + running_hash = self.running_hash.hex() + return ( - f"TopicMessage(" - f"timestamp={self.consensus_timestamp}, " - f"sequence={self.sequence_number}, " - f"message={self.message}, " - f"running_hash={self.running_hash}" - f")" + f"Received Topic Message:\n" + f" - Timestamp: {timestamp}\n" + f" - Sequence Number: {self.sequence_number}\n" + f" - Message: {message}\n" + f" - Running Hash: {running_hash}\n" ) diff --git a/src/hedera_sdk_python/query/topic_message_query.py b/src/hedera_sdk_python/query/topic_message_query.py index 791e5c4..e7559f0 100644 --- a/src/hedera_sdk_python/query/topic_message_query.py +++ b/src/hedera_sdk_python/query/topic_message_query.py @@ -6,6 +6,7 @@ from hedera_sdk_python.hapi.mirror import consensus_service_pb2 as mirror_proto from hedera_sdk_python.hapi.services import basic_types_pb2, timestamp_pb2 from hedera_sdk_python.consensus.topic_message import TopicMessage +from hedera_sdk_python.consensus.topic_id import TopicId class TopicMessageQuery: """ @@ -25,7 +26,9 @@ def set_topic_id(self, shard_or_str: Union[int, str], realm: int = None, topic: parse it into (shard, realm, topic). Otherwise, accept (shard, realm, topic) as three separate ints. """ - if isinstance(shard_or_str, str): + if isinstance(shard_or_str, TopicId): + self._topic_id = shard_or_str.to_proto() + elif isinstance(shard_or_str, str): parts = shard_or_str.strip().split(".") if len(parts) != 3: raise ValueError(f"Invalid topic ID string: {shard_or_str}") diff --git a/tests/test_topic_message_query.py b/tests/test_topic_message_query.py new file mode 100644 index 0000000..6a70553 --- /dev/null +++ b/tests/test_topic_message_query.py @@ -0,0 +1,68 @@ +import pytest +from unittest.mock import MagicMock, patch +from datetime import datetime +from hedera_sdk_python.query.topic_message_query import TopicMessageQuery +from hedera_sdk_python.client.client import Client +from hedera_sdk_python.consensus.topic_id import TopicId +from google.protobuf.timestamp_pb2 import Timestamp +from hedera_sdk_python.hapi.mirror import consensus_service_pb2 as mirror_proto +from hedera_sdk_python.hapi.services import timestamp_pb2 as hapi_timestamp_pb2 + +@pytest.fixture +def mock_client(): + """Fixture to provide a mock Client instance.""" + client = MagicMock(spec=Client) + client.operator_account_id = "0.0.12345" + return client + + +@pytest.fixture +def mock_topic_id(): + """Fixture to provide a mock TopicId instance.""" + return TopicId(0, 0, 1234) + +@pytest.fixture +def mock_subscription_response(): + """Fixture to provide a mock response from a topic subscription.""" + response = mirror_proto.ConsensusTopicResponse( + consensusTimestamp=hapi_timestamp_pb2.Timestamp(seconds=12345, nanos=67890), + message=b"Hello, world!", + runningHash=b"\x00" * 48, + sequenceNumber=1, + ) + return response + + +def test_topic_message_query_subscription(mock_client, mock_topic_id, mock_subscription_response): + """ + Test subscribing to topic messages using TopicMessageQuery. + """ + query = TopicMessageQuery().set_topic_id(mock_topic_id).set_start_time(datetime.utcnow()) + + # Mock the gRPC subscribe functionality + with patch("hedera_sdk_python.query.topic_message_query.TopicMessageQuery.subscribe") as mock_subscribe: + # Set up the mock to call the on_message callback with the mock response + def side_effect(client, on_message, on_error): + on_message(mock_subscription_response) + + mock_subscribe.side_effect = side_effect + + # Define the handlers + on_message = MagicMock() + on_error = MagicMock() + + # Execute the subscription + query.subscribe(mock_client, on_message=on_message, on_error=on_error) + + # Validate that the on_message handler was called with the correct response + on_message.assert_called_once() + called_args = on_message.call_args[0][0] + assert called_args.consensusTimestamp.seconds == 12345 + assert called_args.consensusTimestamp.nanos == 67890 + assert called_args.message == b"Hello, world!" + assert called_args.sequenceNumber == 1 + + # Ensure on_error was not called + on_error.assert_not_called() + + print("Test passed: Subscription handled messages correctly.") diff --git a/uv.lock b/uv.lock index 7507627..f0fa3be 100644 --- a/uv.lock +++ b/uv.lock @@ -458,27 +458,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.8.4" +version = "0.8.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/34/37/9c02181ef38d55b77d97c68b78e705fd14c0de0e5d085202bb2b52ce5be9/ruff-0.8.4.tar.gz", hash = "sha256:0d5f89f254836799af1615798caa5f80b7f935d7a670fad66c5007928e57ace8", size = 3402103 } +sdist = { url = "https://files.pythonhosted.org/packages/25/5d/4b5403f3e89837decfd54c51bea7f94b7d3fae77e08858603d0e04d7ad17/ruff-0.8.5.tar.gz", hash = "sha256:1098d36f69831f7ff2a1da3e6407d5fbd6dfa2559e4f74ff2d260c5588900317", size = 3454835 } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/67/f480bf2f2723b2e49af38ed2be75ccdb2798fca7d56279b585c8f553aaab/ruff-0.8.4-py3-none-linux_armv6l.whl", hash = "sha256:58072f0c06080276804c6a4e21a9045a706584a958e644353603d36ca1eb8a60", size = 10546415 }, - { url = "https://files.pythonhosted.org/packages/eb/7a/5aba20312c73f1ce61814e520d1920edf68ca3b9c507bd84d8546a8ecaa8/ruff-0.8.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ffb60904651c00a1e0b8df594591770018a0f04587f7deeb3838344fe3adabac", size = 10346113 }, - { url = "https://files.pythonhosted.org/packages/76/f4/c41de22b3728486f0aa95383a44c42657b2db4062f3234ca36fc8cf52d8b/ruff-0.8.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6ddf5d654ac0d44389f6bf05cee4caeefc3132a64b58ea46738111d687352296", size = 9943564 }, - { url = "https://files.pythonhosted.org/packages/0e/f0/afa0d2191af495ac82d4cbbfd7a94e3df6f62a04ca412033e073b871fc6d/ruff-0.8.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e248b1f0fa2749edd3350a2a342b67b43a2627434c059a063418e3d375cfe643", size = 10805522 }, - { url = "https://files.pythonhosted.org/packages/12/57/5d1e9a0fd0c228e663894e8e3a8e7063e5ee90f8e8e60cf2085f362bfa1a/ruff-0.8.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf197b98ed86e417412ee3b6c893f44c8864f816451441483253d5ff22c0e81e", size = 10306763 }, - { url = "https://files.pythonhosted.org/packages/04/df/f069fdb02e408be8aac6853583572a2873f87f866fe8515de65873caf6b8/ruff-0.8.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c41319b85faa3aadd4d30cb1cffdd9ac6b89704ff79f7664b853785b48eccdf3", size = 11359574 }, - { url = "https://files.pythonhosted.org/packages/d3/04/37c27494cd02e4a8315680debfc6dfabcb97e597c07cce0044db1f9dfbe2/ruff-0.8.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9f8402b7c4f96463f135e936d9ab77b65711fcd5d72e5d67597b543bbb43cf3f", size = 12094851 }, - { url = "https://files.pythonhosted.org/packages/81/b1/c5d7fb68506cab9832d208d03ea4668da9a9887a4a392f4f328b1bf734ad/ruff-0.8.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4e56b3baa9c23d324ead112a4fdf20db9a3f8f29eeabff1355114dd96014604", size = 11655539 }, - { url = "https://files.pythonhosted.org/packages/ef/38/8f8f2c8898dc8a7a49bc340cf6f00226917f0f5cb489e37075bcb2ce3671/ruff-0.8.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:736272574e97157f7edbbb43b1d046125fce9e7d8d583d5d65d0c9bf2c15addf", size = 12912805 }, - { url = "https://files.pythonhosted.org/packages/06/dd/fa6660c279f4eb320788876d0cff4ea18d9af7d9ed7216d7bd66877468d0/ruff-0.8.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5fe710ab6061592521f902fca7ebcb9fabd27bc7c57c764298b1c1f15fff720", size = 11205976 }, - { url = "https://files.pythonhosted.org/packages/a8/d7/de94cc89833b5de455750686c17c9e10f4e1ab7ccdc5521b8fe911d1477e/ruff-0.8.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:13e9ec6d6b55f6da412d59953d65d66e760d583dd3c1c72bf1f26435b5bfdbae", size = 10792039 }, - { url = "https://files.pythonhosted.org/packages/6d/15/3e4906559248bdbb74854af684314608297a05b996062c9d72e0ef7c7097/ruff-0.8.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:97d9aefef725348ad77d6db98b726cfdb075a40b936c7984088804dfd38268a7", size = 10400088 }, - { url = "https://files.pythonhosted.org/packages/a2/21/9ed4c0e8133cb4a87a18d470f534ad1a8a66d7bec493bcb8bda2d1a5d5be/ruff-0.8.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ab78e33325a6f5374e04c2ab924a3367d69a0da36f8c9cb6b894a62017506111", size = 10900814 }, - { url = "https://files.pythonhosted.org/packages/0d/5d/122a65a18955bd9da2616b69bc839351f8baf23b2805b543aa2f0aed72b5/ruff-0.8.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8ef06f66f4a05c3ddbc9121a8b0cecccd92c5bf3dd43b5472ffe40b8ca10f0f8", size = 11268828 }, - { url = "https://files.pythonhosted.org/packages/43/a9/1676ee9106995381e3d34bccac5bb28df70194167337ed4854c20f27c7ba/ruff-0.8.4-py3-none-win32.whl", hash = "sha256:552fb6d861320958ca5e15f28b20a3d071aa83b93caee33a87b471f99a6c0835", size = 8805621 }, - { url = "https://files.pythonhosted.org/packages/10/98/ed6b56a30ee76771c193ff7ceeaf1d2acc98d33a1a27b8479cbdb5c17a23/ruff-0.8.4-py3-none-win_amd64.whl", hash = "sha256:f21a1143776f8656d7f364bd264a9d60f01b7f52243fbe90e7670c0dfe0cf65d", size = 9660086 }, - { url = "https://files.pythonhosted.org/packages/13/9f/026e18ca7d7766783d779dae5e9c656746c6ede36ef73c6d934aaf4a6dec/ruff-0.8.4-py3-none-win_arm64.whl", hash = "sha256:9183dd615d8df50defa8b1d9a074053891ba39025cf5ae88e8bcb52edcc4bf08", size = 9074500 }, + { url = "https://files.pythonhosted.org/packages/73/f8/03391745a703ce11678eb37c48ae89ec60396ea821e9d0bcea7c8e88fd91/ruff-0.8.5-py3-none-linux_armv6l.whl", hash = "sha256:5ad11a5e3868a73ca1fa4727fe7e33735ea78b416313f4368c504dbeb69c0f88", size = 10626889 }, + { url = "https://files.pythonhosted.org/packages/55/74/83bb74a44183b904216f3edfb9995b89830c83aaa6ce84627f74da0e0cf8/ruff-0.8.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f69ab37771ea7e0715fead8624ec42996d101269a96e31f4d31be6fc33aa19b7", size = 10398233 }, + { url = "https://files.pythonhosted.org/packages/e8/7a/a162a4feb3ef85d594527165e366dde09d7a1e534186ff4ba5d127eda850/ruff-0.8.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b5462d7804558ccff9c08fe8cbf6c14b7efe67404316696a2dde48297b1925bb", size = 10001843 }, + { url = "https://files.pythonhosted.org/packages/e7/9f/5ee5dcd135411402e35b6ec6a8dfdadbd31c5cd1c36a624d356a38d76090/ruff-0.8.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d56de7220a35607f9fe59f8a6d018e14504f7b71d784d980835e20fc0611cd50", size = 10872507 }, + { url = "https://files.pythonhosted.org/packages/b6/67/db2df2dd4a34b602d7f6ebb1b3744c8157f0d3579973ffc58309c9c272e8/ruff-0.8.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9d99cf80b0429cbebf31cbbf6f24f05a29706f0437c40413d950e67e2d4faca4", size = 10377200 }, + { url = "https://files.pythonhosted.org/packages/fe/ff/fe3a6a73006bced73e60d171d154a82430f61d97e787f511a24bd6302611/ruff-0.8.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b75ac29715ac60d554a049dbb0ef3b55259076181c3369d79466cb130eb5afd", size = 11433155 }, + { url = "https://files.pythonhosted.org/packages/e3/95/c1d1a1fe36658c1f3e1b47e1cd5f688b72d5786695b9e621c2c38399a95e/ruff-0.8.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c9d526a62c9eda211b38463528768fd0ada25dad524cb33c0e99fcff1c67b5dc", size = 12139227 }, + { url = "https://files.pythonhosted.org/packages/1b/fe/644b70d473a27b5112ac7a3428edcc1ce0db775c301ff11aa146f71886e0/ruff-0.8.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:587c5e95007612c26509f30acc506c874dab4c4abbacd0357400bd1aa799931b", size = 11697941 }, + { url = "https://files.pythonhosted.org/packages/00/39/4f83e517ec173e16a47c6d102cd22a1aaebe80e1208a1f2e83ab9a0e4134/ruff-0.8.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:622b82bf3429ff0e346835ec213aec0a04d9730480cbffbb6ad9372014e31bbd", size = 12967686 }, + { url = "https://files.pythonhosted.org/packages/1a/f6/52a2973ff108d74b5da706a573379eea160bece098f7cfa3f35dc4622710/ruff-0.8.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f99be814d77a5dac8a8957104bdd8c359e85c86b0ee0e38dca447cb1095f70fb", size = 11253788 }, + { url = "https://files.pythonhosted.org/packages/ce/1f/3b30f3c65b1303cb8e268ec3b046b77ab21ed8e26921cfc7e8232aa57f2c/ruff-0.8.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c01c048f9c3385e0fd7822ad0fd519afb282af9cf1778f3580e540629df89725", size = 10860360 }, + { url = "https://files.pythonhosted.org/packages/a5/a8/2a3ea6bacead963f7aeeba0c61815d9b27b0d638e6a74984aa5cc5d27733/ruff-0.8.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7512e8cb038db7f5db6aae0e24735ff9ea03bb0ed6ae2ce534e9baa23c1dc9ea", size = 10457922 }, + { url = "https://files.pythonhosted.org/packages/17/47/8f9514b670969aab57c5fc826fb500a16aee8feac1bcf8a91358f153a5ba/ruff-0.8.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:762f113232acd5b768d6b875d16aad6b00082add40ec91c927f0673a8ec4ede8", size = 10958347 }, + { url = "https://files.pythonhosted.org/packages/0d/d6/78a9af8209ad99541816d74f01ce678fc01ebb3f37dd7ab8966646dcd92b/ruff-0.8.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:03a90200c5dfff49e4c967b405f27fdfa81594cbb7c5ff5609e42d7fe9680da5", size = 11328882 }, + { url = "https://files.pythonhosted.org/packages/54/77/5c8072ec7afdfdf42c7a4019044486a2b6c85ee73617f8875ec94b977fed/ruff-0.8.5-py3-none-win32.whl", hash = "sha256:8710ffd57bdaa6690cbf6ecff19884b8629ec2a2a2a2f783aa94b1cc795139ed", size = 8802515 }, + { url = "https://files.pythonhosted.org/packages/bc/b6/47d2b06784de8ae992c45cceb2a30f3f205b3236a629d7ca4c0c134839a2/ruff-0.8.5-py3-none-win_amd64.whl", hash = "sha256:4020d8bf8d3a32325c77af452a9976a9ad6455773bcb94991cf15bd66b347e47", size = 9684231 }, + { url = "https://files.pythonhosted.org/packages/bf/5e/ffee22bf9f9e4b2669d1f0179ae8804584939fb6502b51f2401e26b1e028/ruff-0.8.5-py3-none-win_arm64.whl", hash = "sha256:134ae019ef13e1b060ab7136e7828a6d83ea727ba123381307eb37c6bd5e01cb", size = 9124741 }, ] [[package]]