@@ -125,8 +125,9 @@ async def mock_aiter_bytes() -> AsyncIterable[bytes]:
125125 r for r in results if r .metadata .get ("fetch_status" ) == "success"
126126 ]
127127
128- assert len (status_parts ) == 1
129- assert "Fetched successfully" in status_parts [0 ].text
128+ assert len (status_parts ) == 2 # Processing + success status
129+ assert any ("Processing 1 URL(s)" in part .text for part in status_parts )
130+ assert any ("Fetched successfully" in part .text for part in status_parts )
130131 assert len (content_parts ) == 1
131132 assert content_parts [0 ].text == "Test Content"
132133 assert content_parts [0 ].metadata ["source_url" ] == "https://example.com"
@@ -159,23 +160,26 @@ async def test_failed_fetch_with_mocking(self) -> None:
159160 part = processor .ProcessorPart ("Visit https://notfound.com" )
160161 results = [r async for r in p .call (part )]
161162
162- # Should have status and failure parts
163+ # Should have status parts and exception part
163164 status_parts = [
164165 r for r in results if r .substream_name == processor .STATUS_STREAM
165166 ]
166- failure_parts = [
167- r for r in results if r .metadata .get ("fetch_status " ) == "failure "
167+ exception_parts = [
168+ r for r in results if r .metadata .get ("exception_type " ) == "RuntimeError "
168169 ]
169170
170- assert len (status_parts ) == 1
171- assert "Fetch failed" in status_parts [0 ].text
172- assert len (failure_parts ) == 1
173- assert failure_parts [0 ].text == "" # Empty content for failures
174- assert "404" in failure_parts [0 ].metadata ["fetch_error" ]
171+ assert (
172+ len (status_parts ) == 3
173+ ) # Processing + failure status + exception part
174+ assert any ("Processing 1 URL(s)" in part .text for part in status_parts )
175+ assert any ("Fetch failed" in part .text for part in status_parts )
176+ assert len (exception_parts ) == 1
177+ assert "An unexpected error occurred" in exception_parts [0 ].text
178+ assert "404" in exception_parts [0 ].metadata ["original_exception" ]
175179
176180 @pytest .mark .anyio
177181 async def test_fail_on_error_config (self ) -> None :
178- """Test that fail_on_error configuration raises exceptions ."""
182+ """Test that errors are converted to exception parts by the decorator ."""
179183 config = FetchConfig (fail_on_error = True )
180184 p = UrlFetchProcessor (config )
181185
@@ -194,10 +198,14 @@ async def test_fail_on_error_config(self) -> None:
194198 mock_client .get .side_effect = error
195199
196200 part = processor .ProcessorPart ("Visit https://error.com" )
201+ results = [r async for r in p .call (part )]
197202
198- with pytest .raises (RuntimeError ):
199- async for _ in p .call (part ):
200- pass
203+ # With decorator, exceptions are converted to exception parts
204+ exception_parts = [
205+ r for r in results if r .metadata .get ("exception_type" ) == "RuntimeError"
206+ ]
207+ assert len (exception_parts ) == 1
208+ assert "Request Error" in exception_parts [0 ].metadata ["original_exception" ]
201209
202210 @pytest .mark .anyio
203211 async def test_content_processor_raw_config (self ) -> None :
@@ -470,19 +478,22 @@ async def test_url_validation_integration(self) -> None:
470478 part = processor .ProcessorPart ("Visit https://malicious.com" )
471479 results = [r async for r in p .call (part )]
472480
473- # Should have status and failure parts
481+ # Should have status parts and exception part
474482 status_parts = [
475483 r for r in results if r .substream_name == processor .STATUS_STREAM
476484 ]
477- failure_parts = [
478- r for r in results if r .metadata .get ("fetch_status " ) == "failure "
485+ exception_parts = [
486+ r for r in results if r .metadata .get ("exception_type " ) == "RuntimeError "
479487 ]
480488
481- assert len (status_parts ) == 1
482- assert "Fetch failed" in status_parts [0 ].text
483- assert len (failure_parts ) == 1
484- error_msg = failure_parts [0 ].metadata ["fetch_error" ]
485- assert "Security validation failed" in error_msg
489+ assert len (status_parts ) == 3 # Processing + failure status + exception part
490+ assert any ("Processing 1 URL(s)" in part .text for part in status_parts )
491+ assert any ("Fetch failed" in part .text for part in status_parts )
492+ assert len (exception_parts ) == 1
493+ assert (
494+ "Domain 'malicious.com' not in allowed list"
495+ in exception_parts [0 ].metadata ["original_exception" ]
496+ )
486497
487498 @pytest .mark .anyio
488499 async def test_create_success_part_validation_errors (self ) -> None :
@@ -558,13 +569,15 @@ async def test_response_size_limits(self) -> None:
558569 part = processor .ProcessorPart ("Visit https://example.com" )
559570 results = [r async for r in p .call (part )]
560571
561- # Should have failure parts due to size limit
562- failure_parts = [
563- r for r in results if r .metadata .get ("fetch_status " ) == "failure "
572+ # Should have exception parts due to size limit
573+ exception_parts = [
574+ r for r in results if r .metadata .get ("exception_type " ) == "RuntimeError "
564575 ]
565- assert len (failure_parts ) == 1
566- error_msg = failure_parts [0 ].metadata ["fetch_error" ]
567- assert "Response too large" in error_msg
576+ assert len (exception_parts ) == 1
577+ assert (
578+ "Response too large"
579+ in exception_parts [0 ].metadata ["original_exception" ]
580+ )
568581
569582 @pytest .mark .anyio
570583 async def test_streaming_response_size_exceeded (self ) -> None :
@@ -595,10 +608,11 @@ async def mock_aiter_bytes() -> AsyncIterable[bytes]:
595608 part = processor .ProcessorPart ("Visit https://example.com" )
596609 results = [r async for r in p .call (part )]
597610
598- # Should have failure parts due to size limit during streaming
599- failure_parts = [
600- r for r in results if r .metadata .get ("fetch_status " ) == "failure "
611+ # Should have exception parts due to size limit during streaming
612+ exception_parts = [
613+ r for r in results if r .metadata .get ("exception_type " ) == "RuntimeError "
601614 ]
602- assert len (failure_parts ) == 1
603- error_msg = failure_parts [0 ].metadata ["fetch_error" ]
604- assert "Response exceeded" in error_msg
615+ assert len (exception_parts ) == 1
616+ assert (
617+ "Response exceeded" in exception_parts [0 ].metadata ["original_exception" ]
618+ )
0 commit comments