Skip to content

Commit e19060c

Browse files
devin-ai-integration[bot]bot_apk
andcommitted
test: add comprehensive tests proving AirbyteTracedException.__str__ fix
Add 12 tests in TestAirbyteTracedExceptionStr covering: - str() returns user-facing message when both message and internal_message set - str() falls back to internal_message when message is None - str() returns empty string when both are None - str() works in f-strings and %-formatting - args[0] still contains internal_message for backward compat - Subclasses inherit the __str__ override - from_exception() factory method works correctly - Stack trace and internal_message preserved in trace error Co-Authored-By: bot_apk <apk@cognition.ai>
1 parent 07d84bc commit e19060c

1 file changed

Lines changed: 89 additions & 0 deletions

File tree

unit_tests/utils/test_traced_exception.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,3 +166,92 @@ def test_given_both_from_exception_and_as_sanitized_airbyte_message_with_stream_
166166
)
167167
message = traced_exc.as_sanitized_airbyte_message(stream_descriptor=_ANOTHER_STREAM_DESCRIPTOR)
168168
assert message.trace.error.stream_descriptor == _A_STREAM_DESCRIPTOR
169+
170+
171+
class TestAirbyteTracedExceptionStr:
172+
"""Tests proving that __str__ returns user-facing message instead of internal_message."""
173+
174+
def test_str_returns_user_facing_message_when_both_set(self) -> None:
175+
exc = AirbyteTracedException(
176+
internal_message="raw API error: 401 Unauthorized",
177+
message="Authentication credentials are invalid.",
178+
)
179+
assert str(exc) == "Authentication credentials are invalid."
180+
181+
def test_str_falls_back_to_internal_message_when_message_is_none(self) -> None:
182+
exc = AirbyteTracedException(internal_message="an internal error")
183+
assert str(exc) == "an internal error"
184+
185+
def test_str_returns_empty_string_when_both_none(self) -> None:
186+
exc = AirbyteTracedException()
187+
assert str(exc) == ""
188+
189+
def test_str_returns_message_when_internal_message_is_none(self) -> None:
190+
exc = AirbyteTracedException(message="A user-friendly error occurred.")
191+
assert str(exc) == "A user-friendly error occurred."
192+
193+
def test_str_used_in_fstring_returns_user_facing_message(self) -> None:
194+
exc = AirbyteTracedException(
195+
internal_message="internal detail",
196+
message="Connection timed out.",
197+
)
198+
assert f"Error: {exc}" == "Error: Connection timed out."
199+
200+
def test_str_used_in_logging_format_returns_user_facing_message(self) -> None:
201+
exc = AirbyteTracedException(
202+
internal_message="socket.timeout: read timed out",
203+
message="Request timed out.",
204+
)
205+
assert "Error: %s" % exc == "Error: Request timed out."
206+
207+
def test_args_still_contains_internal_message(self) -> None:
208+
"""Verify args[0] is still internal_message for traceback formatting."""
209+
exc = AirbyteTracedException(
210+
internal_message="internal detail",
211+
message="user-facing message",
212+
)
213+
assert exc.args[0] == "internal detail"
214+
215+
def test_str_on_subclass_inherits_behavior(self) -> None:
216+
"""Verify subclasses inherit the __str__ override without needing their own."""
217+
218+
class CustomTracedException(AirbyteTracedException):
219+
pass
220+
221+
exc = CustomTracedException(
222+
internal_message="raw error",
223+
message="User-friendly error.",
224+
)
225+
assert str(exc) == "User-friendly error."
226+
227+
def test_str_with_from_exception_factory(self) -> None:
228+
original = ValueError("original error")
229+
exc = AirbyteTracedException.from_exception(
230+
original, message="A validation error occurred."
231+
)
232+
assert str(exc) == "A validation error occurred."
233+
assert exc.internal_message == "original error"
234+
235+
def test_str_with_from_exception_without_message(self) -> None:
236+
original = RuntimeError("runtime failure")
237+
exc = AirbyteTracedException.from_exception(original)
238+
assert str(exc) == "runtime failure"
239+
240+
def test_stack_trace_uses_str_representation(self) -> None:
241+
"""Verify traceback one-liner uses __str__ (user-facing message)."""
242+
exc = AirbyteTracedException(
243+
internal_message="internal detail for traceback",
244+
message="User sees this.",
245+
)
246+
airbyte_message = exc.as_airbyte_message()
247+
assert "User sees this." in airbyte_message.trace.error.stack_trace
248+
249+
def test_internal_message_preserved_in_trace_error(self) -> None:
250+
"""Verify internal_message is still available in the trace error for debugging."""
251+
exc = AirbyteTracedException(
252+
internal_message="raw API error: 401",
253+
message="Authentication failed.",
254+
)
255+
airbyte_message = exc.as_airbyte_message()
256+
assert airbyte_message.trace.error.internal_message == "raw API error: 401"
257+
assert airbyte_message.trace.error.message == "Authentication failed."

0 commit comments

Comments
 (0)