|
16 | 16 | AirbyteStreamStatus, |
17 | 17 | AirbyteStreamStatusTraceMessage, |
18 | 18 | AirbyteTraceMessage, |
| 19 | + FailureType, |
19 | 20 | StreamDescriptor, |
20 | 21 | SyncMode, |
21 | 22 | TraceType, |
@@ -576,7 +577,11 @@ def test_on_exception_return_trace_message_and_on_stream_complete_return_stream_ |
576 | 577 |
|
577 | 578 | exception_messages = list(handler.on_exception(exception)) |
578 | 579 | assert len(exception_messages) == 1 |
579 | | - assert "StreamThreadException" in exception_messages[0].trace.error.stack_trace |
| 580 | + assert "RuntimeError" in exception_messages[0].trace.error.stack_trace |
| 581 | + assert ( |
| 582 | + exception_messages[0].trace.error.message |
| 583 | + == f"An unexpected error occurred in stream {_STREAM_NAME}: RuntimeError" |
| 584 | + ) |
580 | 585 |
|
581 | 586 | assert list( |
582 | 587 | handler.on_partition_complete_sentinel( |
@@ -761,6 +766,52 @@ def test_is_done_is_true_if_all_partitions_are_closed_and_no_streams_are_generat |
761 | 766 |
|
762 | 767 | assert handler.is_done() |
763 | 768 |
|
| 769 | + @freezegun.freeze_time("2020-01-01T00:00:00") |
| 770 | + def test_on_exception_non_ate_uses_templated_message_with_correct_failure_type(self): |
| 771 | + """Regression test: non-ATE exceptions on Path B produce a safe templated message, not the generic fallback.""" |
| 772 | + stream_instances_to_read_from = [self._stream, self._another_stream] |
| 773 | + |
| 774 | + handler = ConcurrentReadProcessor( |
| 775 | + stream_instances_to_read_from, |
| 776 | + self._partition_enqueuer, |
| 777 | + self._thread_pool_manager, |
| 778 | + self._logger, |
| 779 | + self._slice_logger, |
| 780 | + self._message_repository, |
| 781 | + self._partition_reader, |
| 782 | + ) |
| 783 | + |
| 784 | + handler.start_next_partition_generator() |
| 785 | + handler.on_partition(self._an_open_partition) |
| 786 | + list( |
| 787 | + handler.on_partition_generation_completed( |
| 788 | + PartitionGenerationCompletedSentinel(self._stream) |
| 789 | + ) |
| 790 | + ) |
| 791 | + list( |
| 792 | + handler.on_partition_generation_completed( |
| 793 | + PartitionGenerationCompletedSentinel(self._another_stream) |
| 794 | + ) |
| 795 | + ) |
| 796 | + |
| 797 | + inner_exception = ValueError("some internal detail: SELECT * FROM secrets") |
| 798 | + exception = StreamThreadException(inner_exception, _STREAM_NAME) |
| 799 | + |
| 800 | + exception_messages = list(handler.on_exception(exception)) |
| 801 | + assert len(exception_messages) == 1 |
| 802 | + |
| 803 | + trace_error = exception_messages[0].trace.error |
| 804 | + # User-facing message uses safe template (no raw exception text) |
| 805 | + assert ( |
| 806 | + trace_error.message |
| 807 | + == f"An unexpected error occurred in stream {_STREAM_NAME}: ValueError" |
| 808 | + ) |
| 809 | + # Stack trace comes from the inner exception, not the StreamThreadException wrapper |
| 810 | + assert "ValueError" in trace_error.stack_trace |
| 811 | + assert "StreamThreadException" not in trace_error.stack_trace |
| 812 | + # failure_type defaults to system_error for unhandled exceptions |
| 813 | + assert trace_error.failure_type == FailureType.system_error |
| 814 | + |
764 | 815 | @freezegun.freeze_time("2020-01-01T00:00:00") |
765 | 816 | def test_start_next_partition_generator(self): |
766 | 817 | stream_instances_to_read_from = [self._stream] |
|
0 commit comments