Skip to content

Commit 384eff0

Browse files
committed
more tests
1 parent d6528e3 commit 384eff0

1 file changed

Lines changed: 117 additions & 0 deletions

File tree

py/src/braintrust/integrations/test_utils.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
1+
import unittest.mock
2+
3+
import pytest
14
from braintrust import Attachment
25
from braintrust.integrations.utils import (
36
_camel_to_snake,
47
_convert_data_url_to_attachment,
58
_is_supported_metric_value,
9+
_log_and_end_span,
10+
_log_error_and_end_span,
611
_merge_timing_and_usage_metrics,
712
_parse_openai_usage_metrics,
813
_prettify_response_params,
14+
_serialize_response_format,
15+
_timing_metrics,
16+
_try_to_dict,
917
)
1018

1119

@@ -27,6 +35,40 @@ def test_is_supported_metric_value_excludes_booleans():
2735
assert not _is_supported_metric_value("1")
2836

2937

38+
def test_try_to_dict_uses_to_dict_when_available():
39+
class ToDictOnly:
40+
__slots__ = ("_payload",)
41+
42+
def __init__(self, payload):
43+
self._payload = payload
44+
45+
def to_dict(self):
46+
return self._payload
47+
48+
result = _try_to_dict(ToDictOnly({"tokens": 3}))
49+
50+
assert result == {"tokens": 3}
51+
52+
53+
def test_try_to_dict_falls_back_to_vars_for_plain_objects():
54+
class PlainObject:
55+
def __init__(self):
56+
self.foo = "bar"
57+
self.count = 2
58+
59+
result = _try_to_dict(PlainObject())
60+
61+
assert result == {"foo": "bar", "count": 2}
62+
63+
64+
def test_try_to_dict_returns_original_when_no_conversion_is_possible():
65+
obj = object()
66+
67+
result = _try_to_dict(obj)
68+
69+
assert result is obj
70+
71+
3072
def test_parse_openai_usage_metrics_handles_nested_token_details():
3173
usage = {
3274
"prompt_tokens": 10,
@@ -88,6 +130,81 @@ def test_convert_data_url_to_attachment_preserves_invalid_base64():
88130
assert converted == data_url
89131

90132

133+
def test_convert_data_url_to_attachment_preserves_non_data_urls():
134+
value = "https://example.com/image.png"
135+
136+
converted = _convert_data_url_to_attachment(value)
137+
138+
assert converted == value
139+
140+
141+
def test_serialize_response_format_with_pydantic_basemodel_subclass():
142+
pydantic = pytest.importorskip("pydantic")
143+
144+
class ResponseFormat(pydantic.BaseModel):
145+
answer: str
146+
147+
serialized = _serialize_response_format(ResponseFormat)
148+
149+
assert serialized["type"] == "json_schema"
150+
assert serialized["json_schema"]["name"] == "ResponseFormat"
151+
assert serialized["json_schema"]["schema"]["properties"]["answer"]["title"] == "Answer"
152+
153+
154+
def test_timing_metrics_includes_time_to_first_token_when_present():
155+
assert _timing_metrics(10.0, 15.0, 12.0) == {
156+
"start": 10.0,
157+
"end": 15.0,
158+
"duration": 5.0,
159+
"time_to_first_token": 2.0,
160+
}
161+
162+
163+
def test_timing_metrics_omits_time_to_first_token_when_absent():
164+
assert _timing_metrics(10.0, 15.0) == {
165+
"start": 10.0,
166+
"end": 15.0,
167+
"duration": 5.0,
168+
}
169+
170+
171+
def test_log_and_end_span_logs_populated_event_then_ends():
172+
span = unittest.mock.Mock()
173+
174+
_log_and_end_span(
175+
span,
176+
output={"answer": "4"},
177+
metrics={"tokens": 2},
178+
metadata={"provider": "test"},
179+
)
180+
181+
span.log.assert_called_once_with(
182+
output={"answer": "4"},
183+
metrics={"tokens": 2},
184+
metadata={"provider": "test"},
185+
)
186+
span.end.assert_called_once_with()
187+
188+
189+
def test_log_and_end_span_skips_log_for_empty_event():
190+
span = unittest.mock.Mock()
191+
192+
_log_and_end_span(span)
193+
194+
span.log.assert_not_called()
195+
span.end.assert_called_once_with()
196+
197+
198+
def test_log_error_and_end_span_logs_error_then_ends():
199+
span = unittest.mock.Mock()
200+
error = RuntimeError("boom")
201+
202+
_log_error_and_end_span(span, error)
203+
204+
span.log.assert_called_once_with(error=error)
205+
span.end.assert_called_once_with()
206+
207+
91208
def test_merge_timing_and_usage_metrics(monkeypatch):
92209
monkeypatch.setattr("braintrust.integrations.utils.time.time", lambda: 15.0)
93210

0 commit comments

Comments
 (0)