@@ -368,3 +368,116 @@ async def collect():
368368 assert result == [1 , 2 , 3 ]
369369 assert span .output == '[2, 4, 6]'
370370 assert span .finished is True
371+
372+
373+ class TestCozeLoopDecoratorExceptionHandling :
374+ """测试 CozeLoopDecorator 是否正确处理并重新抛出异常,而不是将其掩盖"""
375+
376+ def test_sync_func_exception (self , monkeypatch ):
377+ span = SpanMock ()
378+ monkeypatch .setattr (decorator_module , "start_span" , lambda * args , ** kwargs : span )
379+
380+ @decorator_module .CozeLoopDecorator ().observe ()
381+ def risky_func ():
382+ raise ValueError ("Sync error" )
383+
384+ with pytest .raises (ValueError , match = "Sync error" ):
385+ risky_func ()
386+
387+ assert span .error == "Sync error"
388+ assert span .finished is True
389+
390+ def test_async_func_exception (self , monkeypatch ):
391+ span = SpanMock ()
392+ monkeypatch .setattr (decorator_module , "start_span" , lambda * args , ** kwargs : span )
393+
394+ @decorator_module .CozeLoopDecorator ().observe ()
395+ async def risky_async_func ():
396+ raise ValueError ("Async error" )
397+
398+ with pytest .raises (ValueError , match = "Async error" ):
399+ asyncio .run (risky_async_func ())
400+
401+ assert span .error == "Async error"
402+ assert span .finished is True
403+
404+ def test_generator_func_exception (self , monkeypatch ):
405+ span = SpanMock ()
406+ monkeypatch .setattr (decorator_module , "start_span" , lambda * args , ** kwargs : span )
407+
408+ @decorator_module .CozeLoopDecorator ().observe ()
409+ def risky_gen ():
410+ yield 1
411+ raise ValueError ("Gen error" )
412+
413+ gen = risky_gen ()
414+ assert next (gen ) == 1
415+ with pytest .raises (ValueError , match = "Gen error" ):
416+ next (gen )
417+
418+ assert span .error == "Gen error"
419+ assert span .finished is True
420+
421+ def test_async_generator_func_exception (self , monkeypatch ):
422+ span = SpanMock ()
423+ monkeypatch .setattr (decorator_module , "start_span" , lambda * args , ** kwargs : span )
424+
425+ @decorator_module .CozeLoopDecorator ().observe ()
426+ async def risky_agen ():
427+ yield 1
428+ raise ValueError ("Async gen error" )
429+
430+ async def collect ():
431+ agen = risky_agen ()
432+ assert (await agen .__anext__ ()) == 1
433+ with pytest .raises (ValueError , match = "Async gen error" ):
434+ await agen .__anext__ ()
435+
436+ asyncio .run (collect ())
437+
438+ assert span .error == "Async gen error"
439+ assert span .finished is True
440+
441+ def test_sync_stream_wrapper_exception (self , monkeypatch ):
442+ span = SpanMock ()
443+ monkeypatch .setattr (decorator_module , "start_span" , lambda * args , ** kwargs : span )
444+
445+ def risky_iter_func ():
446+ class RiskyIter :
447+ def __iter__ (self ):
448+ return self
449+ def __next__ (self ):
450+ raise ValueError ("Stream error" )
451+ return RiskyIter ()
452+
453+ decorated = decorator_module .CozeLoopDecorator ().observe (process_iterator_outputs = lambda x : x )(risky_iter_func )
454+ stream = decorated ()
455+ with pytest .raises (ValueError , match = "Stream error" ):
456+ list (stream )
457+
458+ assert span .error == "Stream error"
459+ assert span .finished is True
460+
461+ def test_async_stream_wrapper_exception (self , monkeypatch ):
462+ span = SpanMock ()
463+ monkeypatch .setattr (decorator_module , "start_span" , lambda * args , ** kwargs : span )
464+
465+ async def risky_aiter_func ():
466+ class RiskyAIter :
467+ def __aiter__ (self ):
468+ return self
469+ async def __anext__ (self ):
470+ raise ValueError ("Async stream error" )
471+ return RiskyAIter ()
472+
473+ async def collect ():
474+ decorated = decorator_module .CozeLoopDecorator ().observe (process_iterator_outputs = lambda x : x )(risky_aiter_func )
475+ stream = await decorated ()
476+ with pytest .raises (ValueError , match = "Async stream error" ):
477+ async for _ in stream :
478+ pass
479+
480+ asyncio .run (collect ())
481+
482+ assert span .error == "Async stream error"
483+ assert span .finished is True
0 commit comments