Skip to content

Commit

Permalink
topic message query test added
Browse files Browse the repository at this point in the history
  • Loading branch information
nadineloepfe committed Jan 2, 2025
1 parent 475d063 commit ae14c89
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 30 deletions.
4 changes: 2 additions & 2 deletions examples/topic_message_submit.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -35,4 +35,4 @@ def submit_message(message):
sys.exit(1)

if __name__ == "__main__":
submit_message("four")
submit_message("Hello, Hedera!")
23 changes: 15 additions & 8 deletions src/hedera_sdk_python/consensus/topic_message.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from hedera_sdk_python.hapi.mirror import consensus_service_pb2 as mirror_proto
from datetime import datetime

class TopicMessage:
"""
Represents a single message returned from a Hedera Mirror Node subscription.
"""

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
Expand All @@ -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"
)
5 changes: 4 additions & 1 deletion src/hedera_sdk_python/query/topic_message_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
"""
Expand All @@ -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}")
Expand Down
68 changes: 68 additions & 0 deletions tests/test_topic_message_query.py
Original file line number Diff line number Diff line change
@@ -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.")
38 changes: 19 additions & 19 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit ae14c89

Please sign in to comment.