diff --git a/tests/core/pubsub/test_gossipsub.py b/tests/core/pubsub/test_gossipsub.py index 704f8f4b..5c341d0b 100644 --- a/tests/core/pubsub/test_gossipsub.py +++ b/tests/core/pubsub/test_gossipsub.py @@ -851,3 +851,83 @@ async def test_handle_iwant(monkeypatch): called_msg_id = mock_mcache_get.call_args[0][0] assert isinstance(called_msg_id, tuple) assert called_msg_id == (test_seqno, test_from) + + +@pytest.mark.trio +async def test_handle_iwant_invalid_msg_id(monkeypatch): + """ + Test that handle_iwant raises ValueError for malformed message IDs. + """ + async with PubsubFactory.create_batch_with_gossipsub(2) as pubsubs_gsub: + gossipsub_routers = [] + for pubsub in pubsubs_gsub: + if isinstance(pubsub.router, GossipSub): + gossipsub_routers.append(pubsub.router) + gossipsubs = tuple(gossipsub_routers) + + index_alice = 0 + index_bob = 1 + id_alice = pubsubs_gsub[index_alice].my_id + + await connect(pubsubs_gsub[index_alice].host, pubsubs_gsub[index_bob].host) + await trio.sleep(0.1) + + # Malformed message ID (not a tuple string) + malformed_msg_id = "not_a_valid_msg_id" + iwant_msg = rpc_pb2.ControlIWant(messageIDs=[malformed_msg_id]) + + # Mock mcache.get and write_msg to ensure they are not called + mock_mcache_get = MagicMock() + monkeypatch.setattr(gossipsubs[index_bob].mcache, "get", mock_mcache_get) + mock_write_msg = AsyncMock() + monkeypatch.setattr(gossipsubs[index_bob].pubsub, "write_msg", mock_write_msg) + + with pytest.raises(ValueError): + await gossipsubs[index_bob].handle_iwant(iwant_msg, id_alice) + mock_mcache_get.assert_not_called() + mock_write_msg.assert_not_called() + + # Message ID that's a tuple string but not (bytes, bytes) + invalid_tuple_msg_id = "('abc', 123)" + iwant_msg = rpc_pb2.ControlIWant(messageIDs=[invalid_tuple_msg_id]) + with pytest.raises(ValueError): + await gossipsubs[index_bob].handle_iwant(iwant_msg, id_alice) + mock_mcache_get.assert_not_called() + mock_write_msg.assert_not_called() + + +@pytest.mark.trio +async def test_handle_ihave_empty_message_ids(monkeypatch): + """ + Test that handle_ihave with an empty messageIDs list does not call emit_iwant. + """ + async with PubsubFactory.create_batch_with_gossipsub(2) as pubsubs_gsub: + gossipsub_routers = [] + for pubsub in pubsubs_gsub: + if isinstance(pubsub.router, GossipSub): + gossipsub_routers.append(pubsub.router) + gossipsubs = tuple(gossipsub_routers) + + index_alice = 0 + index_bob = 1 + id_bob = pubsubs_gsub[index_bob].my_id + + # Connect Alice and Bob + await connect(pubsubs_gsub[index_alice].host, pubsubs_gsub[index_bob].host) + await trio.sleep(0.1) # Allow connections to establish + + # Mock emit_iwant to capture calls + mock_emit_iwant = AsyncMock() + monkeypatch.setattr(gossipsubs[index_alice], "emit_iwant", mock_emit_iwant) + + # Empty messageIDs list + ihave_msg = rpc_pb2.ControlIHave(messageIDs=[]) + + # Mock seen_messages.cache to avoid false positives + monkeypatch.setattr(pubsubs_gsub[index_alice].seen_messages, "cache", {}) + + # Simulate Bob sending IHAVE to Alice + await gossipsubs[index_alice].handle_ihave(ihave_msg, id_bob) + + # emit_iwant should not be called since there are no message IDs + mock_emit_iwant.assert_not_called()