11"""Tests for the StrandsA2AExecutor class."""
22
3+ import base64
34from unittest .mock import AsyncMock , MagicMock , patch
45
56import pytest
6- from a2a .types import InternalError , UnsupportedOperationError
7+ from a2a .types import DataPart , FilePart , InternalError , TextPart , UnsupportedOperationError
78from a2a .utils .errors import ServerError
89
910from strands .agent .agent_result import AgentResult as SAAgentResult
1011from strands .multiagent .a2a .executor import StrandsA2AExecutor
1112from strands .types .content import ContentBlock
1213
14+ # Test data constants
15+ VALID_PNG_BYTES = b"fake_png_data"
16+ VALID_MP4_BYTES = b"fake_mp4_data"
17+ VALID_DOCUMENT_BYTES = b"fake_document_data"
18+
1319
1420def test_executor_initialization (mock_strands_agent ):
1521 """Test that StrandsA2AExecutor initializes correctly."""
@@ -96,18 +102,15 @@ def test_convert_a2a_parts_to_content_blocks_text_part():
96102
97103def test_convert_a2a_parts_to_content_blocks_file_part_image_bytes ():
98104 """Test conversion of FilePart with image bytes to ContentBlock."""
99- from a2a .types import FilePart
100-
101105 executor = StrandsA2AExecutor (MagicMock ())
102106
103- # Create test image bytes (no base64 encoding needed)
104- test_bytes = b"fake_image_data"
107+ base64_bytes = base64 .b64encode (VALID_PNG_BYTES ).decode ("utf-8" )
105108
106109 # Mock file object
107110 file_obj = MagicMock ()
108- file_obj .name = "test_image.jpeg "
109- file_obj .mime_type = "image/jpeg "
110- file_obj .bytes = test_bytes
111+ file_obj .name = "test_image.png "
112+ file_obj .mime_type = "image/png "
113+ file_obj .bytes = base64_bytes
111114 file_obj .uri = None
112115
113116 # Mock FilePart with proper spec
@@ -123,24 +126,21 @@ def test_convert_a2a_parts_to_content_blocks_file_part_image_bytes():
123126 assert len (result ) == 1
124127 content_block = result [0 ]
125128 assert "image" in content_block
126- assert content_block ["image" ]["format" ] == "jpeg "
127- assert content_block ["image" ]["source" ]["bytes" ] == test_bytes
129+ assert content_block ["image" ]["format" ] == "png "
130+ assert content_block ["image" ]["source" ]["bytes" ] == VALID_PNG_BYTES
128131
129132
130133def test_convert_a2a_parts_to_content_blocks_file_part_video_bytes ():
131134 """Test conversion of FilePart with video bytes to ContentBlock."""
132- from a2a .types import FilePart
133-
134135 executor = StrandsA2AExecutor (MagicMock ())
135136
136- # Create test video bytes (no base64 encoding needed)
137- test_bytes = b"fake_video_data"
137+ base64_bytes = base64 .b64encode (VALID_MP4_BYTES ).decode ("utf-8" )
138138
139139 # Mock file object
140140 file_obj = MagicMock ()
141141 file_obj .name = "test_video.mp4"
142142 file_obj .mime_type = "video/mp4"
143- file_obj .bytes = test_bytes
143+ file_obj .bytes = base64_bytes
144144 file_obj .uri = None
145145
146146 # Mock FilePart with proper spec
@@ -157,23 +157,20 @@ def test_convert_a2a_parts_to_content_blocks_file_part_video_bytes():
157157 content_block = result [0 ]
158158 assert "video" in content_block
159159 assert content_block ["video" ]["format" ] == "mp4"
160- assert content_block ["video" ]["source" ]["bytes" ] == test_bytes
160+ assert content_block ["video" ]["source" ]["bytes" ] == VALID_MP4_BYTES
161161
162162
163163def test_convert_a2a_parts_to_content_blocks_file_part_document_bytes ():
164164 """Test conversion of FilePart with document bytes to ContentBlock."""
165- from a2a .types import FilePart
166-
167165 executor = StrandsA2AExecutor (MagicMock ())
168166
169- # Create test document bytes (no base64 encoding needed)
170- test_bytes = b"fake_document_data"
167+ base64_bytes = base64 .b64encode (VALID_DOCUMENT_BYTES ).decode ("utf-8" )
171168
172169 # Mock file object
173170 file_obj = MagicMock ()
174171 file_obj .name = "test_document.pdf"
175172 file_obj .mime_type = "application/pdf"
176- file_obj .bytes = test_bytes
173+ file_obj .bytes = base64_bytes
177174 file_obj .uri = None
178175
179176 # Mock FilePart with proper spec
@@ -191,7 +188,7 @@ def test_convert_a2a_parts_to_content_blocks_file_part_document_bytes():
191188 assert "document" in content_block
192189 assert content_block ["document" ]["format" ] == "pdf"
193190 assert content_block ["document" ]["name" ] == "test_document"
194- assert content_block ["document" ]["source" ]["bytes" ] == test_bytes
191+ assert content_block ["document" ]["source" ]["bytes" ] == VALID_DOCUMENT_BYTES
195192
196193
197194def test_convert_a2a_parts_to_content_blocks_file_part_uri ():
@@ -226,15 +223,15 @@ def test_convert_a2a_parts_to_content_blocks_file_part_uri():
226223
227224def test_convert_a2a_parts_to_content_blocks_file_part_with_bytes ():
228225 """Test conversion of FilePart with bytes data."""
229- from a2a .types import FilePart
230-
231226 executor = StrandsA2AExecutor (MagicMock ())
232227
228+ base64_bytes = base64 .b64encode (VALID_PNG_BYTES ).decode ("utf-8" )
229+
233230 # Mock file object with bytes (no validation needed since no decoding)
234231 file_obj = MagicMock ()
235232 file_obj .name = "test_image.png"
236233 file_obj .mime_type = "image/png"
237- file_obj .bytes = b"some_binary_data"
234+ file_obj .bytes = base64_bytes
238235 file_obj .uri = None
239236
240237 # Mock FilePart with proper spec
@@ -250,7 +247,34 @@ def test_convert_a2a_parts_to_content_blocks_file_part_with_bytes():
250247 assert len (result ) == 1
251248 content_block = result [0 ]
252249 assert "image" in content_block
253- assert content_block ["image" ]["source" ]["bytes" ] == b"some_binary_data"
250+ assert content_block ["image" ]["source" ]["bytes" ] == VALID_PNG_BYTES
251+
252+
253+ def test_convert_a2a_parts_to_content_blocks_file_part_invalid_base64 ():
254+ """Test conversion of FilePart with invalid base64 data raises ValueError."""
255+ executor = StrandsA2AExecutor (MagicMock ())
256+
257+ # Invalid base64 string - contains invalid characters
258+ invalid_base64 = "SGVsbG8gV29ybGQ@#$%"
259+
260+ # Mock file object with invalid base64 bytes
261+ file_obj = MagicMock ()
262+ file_obj .name = "test.txt"
263+ file_obj .mime_type = "text/plain"
264+ file_obj .bytes = invalid_base64
265+ file_obj .uri = None
266+
267+ # Mock FilePart
268+ file_part = MagicMock (spec = FilePart )
269+ file_part .file = file_obj
270+ part = MagicMock ()
271+ part .root = file_part
272+
273+ # Should handle the base64 decode error gracefully and return empty list
274+ result = executor ._convert_a2a_parts_to_content_blocks ([part ])
275+ assert isinstance (result , list )
276+ # The part should be skipped due to base64 decode error
277+ assert len (result ) == 0
254278
255279
256280def test_convert_a2a_parts_to_content_blocks_data_part ():
@@ -704,15 +728,15 @@ def test_convert_a2a_parts_to_content_blocks_empty_list():
704728
705729def test_convert_a2a_parts_to_content_blocks_file_part_no_name ():
706730 """Test conversion of FilePart with no file name."""
707- from a2a .types import FilePart
708-
709731 executor = StrandsA2AExecutor (MagicMock ())
710732
733+ base64_bytes = base64 .b64encode (VALID_DOCUMENT_BYTES ).decode ("utf-8" )
734+
711735 # Mock file object without name
712736 file_obj = MagicMock ()
713737 delattr (file_obj , "name" ) # Remove name attribute
714738 file_obj .mime_type = "text/plain"
715- file_obj .bytes = b"test content"
739+ file_obj .bytes = base64_bytes
716740 file_obj .uri = None
717741
718742 # Mock FilePart with proper spec
@@ -733,15 +757,15 @@ def test_convert_a2a_parts_to_content_blocks_file_part_no_name():
733757
734758def test_convert_a2a_parts_to_content_blocks_file_part_no_mime_type ():
735759 """Test conversion of FilePart with no MIME type."""
736- from a2a .types import FilePart
737-
738760 executor = StrandsA2AExecutor (MagicMock ())
739761
762+ base64_bytes = base64 .b64encode (VALID_DOCUMENT_BYTES ).decode ("utf-8" )
763+
740764 # Mock file object without MIME type
741765 file_obj = MagicMock ()
742766 file_obj .name = "test_file"
743767 delattr (file_obj , "mime_type" )
744- file_obj .bytes = b"test content"
768+ file_obj .bytes = base64_bytes
745769 file_obj .uri = None
746770
747771 # Mock FilePart with proper spec
@@ -837,7 +861,6 @@ async def test_execute_streaming_mode_raises_error_for_empty_content_blocks(
837861@pytest .mark .asyncio
838862async def test_execute_with_mixed_part_types (mock_strands_agent , mock_request_context , mock_event_queue ):
839863 """Test execute with a message containing mixed A2A part types."""
840- from a2a .types import DataPart , FilePart , TextPart
841864
842865 async def mock_stream (content_blocks ):
843866 """Mock streaming function."""
@@ -866,7 +889,7 @@ async def mock_stream(content_blocks):
866889 file_obj = MagicMock ()
867890 file_obj .name = "image.png"
868891 file_obj .mime_type = "image/png"
869- file_obj .bytes = b"fake_image"
892+ file_obj .bytes = base64 . b64encode ( VALID_PNG_BYTES ). decode ( "utf-8" )
870893 file_obj .uri = None
871894 file_part = MagicMock (spec = FilePart )
872895 file_part .file = file_obj
@@ -907,8 +930,6 @@ def test_integration_example():
907930
908931 This test serves as documentation for the conversion functionality.
909932 """
910- from a2a .types import DataPart , FilePart , TextPart
911-
912933 executor = StrandsA2AExecutor (MagicMock ())
913934
914935 # Example 1: Text content
@@ -918,7 +939,7 @@ def test_integration_example():
918939 text_part_mock .root = text_part
919940
920941 # Example 2: Image file
921- image_bytes = b"fake_image_content"
942+ image_bytes = base64 . b64encode ( VALID_PNG_BYTES ). decode ( "utf-8" )
922943 image_file = MagicMock ()
923944 image_file .name = "photo.jpg"
924945 image_file .mime_type = "image/jpeg"
@@ -931,7 +952,7 @@ def test_integration_example():
931952 image_part_mock .root = image_part
932953
933954 # Example 3: Document file
934- doc_bytes = b"PDF document content"
955+ doc_bytes = base64 . b64encode ( VALID_DOCUMENT_BYTES ). decode ( "utf-8" )
935956 doc_file = MagicMock ()
936957 doc_file .name = "report.pdf"
937958 doc_file .mime_type = "application/pdf"
@@ -962,13 +983,13 @@ def test_integration_example():
962983 # Image part becomes image ContentBlock with proper format and bytes
963984 assert "image" in content_blocks [1 ]
964985 assert content_blocks [1 ]["image" ]["format" ] == "jpeg"
965- assert content_blocks [1 ]["image" ]["source" ]["bytes" ] == image_bytes
986+ assert content_blocks [1 ]["image" ]["source" ]["bytes" ] == VALID_PNG_BYTES
966987
967988 # Document part becomes document ContentBlock
968989 assert "document" in content_blocks [2 ]
969990 assert content_blocks [2 ]["document" ]["format" ] == "pdf"
970991 assert content_blocks [2 ]["document" ]["name" ] == "report" # Extension stripped
971- assert content_blocks [2 ]["document" ]["source" ]["bytes" ] == doc_bytes
992+ assert content_blocks [2 ]["document" ]["source" ]["bytes" ] == VALID_DOCUMENT_BYTES
972993
973994 # Data part becomes text ContentBlock with JSON representation
974995 assert "text" in content_blocks [3 ]
0 commit comments