free-claude-code/tests/api/test_response_models.py
2026-02-18 04:13:41 -08:00

199 lines
6.5 KiB
Python

"""Tests for api/models/responses.py Pydantic response models."""
from api.models.anthropic import (
ContentBlockText,
ContentBlockThinking,
ContentBlockToolUse,
)
from api.models.responses import MessagesResponse, TokenCountResponse, Usage
class TestUsage:
"""Tests for Usage model."""
def test_required_fields(self):
usage = Usage(input_tokens=10, output_tokens=20)
assert usage.input_tokens == 10
assert usage.output_tokens == 20
def test_cache_defaults_zero(self):
usage = Usage(input_tokens=1, output_tokens=2)
assert usage.cache_creation_input_tokens == 0
assert usage.cache_read_input_tokens == 0
def test_cache_fields_set(self):
usage = Usage(
input_tokens=10,
output_tokens=20,
cache_creation_input_tokens=5,
cache_read_input_tokens=3,
)
assert usage.cache_creation_input_tokens == 5
assert usage.cache_read_input_tokens == 3
def test_serialization(self):
usage = Usage(input_tokens=10, output_tokens=20)
data = usage.model_dump()
assert data == {
"input_tokens": 10,
"output_tokens": 20,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
}
class TestTokenCountResponse:
"""Tests for TokenCountResponse model."""
def test_basic(self):
resp = TokenCountResponse(input_tokens=42)
assert resp.input_tokens == 42
def test_serialization(self):
resp = TokenCountResponse(input_tokens=100)
data = resp.model_dump()
assert data == {"input_tokens": 100}
class TestMessagesResponse:
"""Tests for MessagesResponse model."""
def test_minimum_fields(self):
resp = MessagesResponse(
id="msg_001",
model="test-model",
content=[ContentBlockText(type="text", text="Hello")],
usage=Usage(input_tokens=10, output_tokens=5),
)
assert resp.id == "msg_001"
assert resp.model == "test-model"
assert resp.role == "assistant"
assert resp.type == "message"
assert resp.stop_reason is None
assert resp.stop_sequence is None
def test_with_text_content(self):
resp = MessagesResponse(
id="msg_002",
model="model",
content=[ContentBlockText(type="text", text="response")],
usage=Usage(input_tokens=1, output_tokens=1),
)
assert len(resp.content) == 1
block = resp.content[0]
assert isinstance(block, ContentBlockText)
assert block.type == "text"
assert block.text == "response"
def test_with_tool_use_content(self):
resp = MessagesResponse(
id="msg_003",
model="model",
content=[
ContentBlockToolUse(
type="tool_use",
id="tool_1",
name="Read",
input={"path": "test.py"},
)
],
usage=Usage(input_tokens=1, output_tokens=1),
stop_reason="tool_use",
)
block = resp.content[0]
assert isinstance(block, ContentBlockToolUse)
assert block.type == "tool_use"
assert block.name == "Read"
assert resp.stop_reason == "tool_use"
def test_with_thinking_content(self):
resp = MessagesResponse(
id="msg_004",
model="model",
content=[
ContentBlockThinking(type="thinking", thinking="Let me reason..."),
ContentBlockText(type="text", text="Answer"),
],
usage=Usage(input_tokens=5, output_tokens=10),
)
assert len(resp.content) == 2
block0 = resp.content[0]
assert isinstance(block0, ContentBlockThinking)
assert block0.type == "thinking"
assert block0.thinking == "Let me reason..."
block1 = resp.content[1]
assert isinstance(block1, ContentBlockText)
assert block1.type == "text"
def test_with_all_content_types(self):
resp = MessagesResponse(
id="msg_005",
model="model",
content=[
ContentBlockThinking(type="thinking", thinking="hmm"),
ContentBlockText(type="text", text="result"),
ContentBlockToolUse(
type="tool_use", id="t1", name="Bash", input={"command": "ls"}
),
],
usage=Usage(input_tokens=10, output_tokens=20),
stop_reason="tool_use",
)
assert len(resp.content) == 3
def test_with_dict_content(self):
"""Dict content (unknown block type) should be accepted."""
resp = MessagesResponse(
id="msg_006",
model="model",
content=[{"type": "custom", "data": "value"}],
usage=Usage(input_tokens=1, output_tokens=1),
)
block = resp.content[0]
assert isinstance(block, dict)
assert block["type"] == "custom"
def test_stop_reason_values(self):
"""All valid stop_reason values should be accepted."""
from typing import Literal
reasons: list[
Literal["end_turn", "max_tokens", "stop_sequence", "tool_use"]
] = [
"end_turn",
"max_tokens",
"stop_sequence",
"tool_use",
]
for reason in reasons:
resp = MessagesResponse(
id="msg",
model="model",
content=[ContentBlockText(type="text", text="x")],
usage=Usage(input_tokens=1, output_tokens=1),
stop_reason=reason,
)
assert resp.stop_reason == reason
def test_serialization_round_trip(self):
resp = MessagesResponse(
id="msg_rt",
model="model-v1",
content=[ContentBlockText(type="text", text="hello")],
usage=Usage(input_tokens=10, output_tokens=5),
stop_reason="end_turn",
)
data = resp.model_dump()
restored = MessagesResponse(**data)
assert restored.id == resp.id
assert restored.model == resp.model
assert restored.stop_reason == resp.stop_reason
def test_empty_content_list(self):
resp = MessagesResponse(
id="msg_empty",
model="model",
content=[],
usage=Usage(input_tokens=0, output_tokens=0),
)
assert resp.content == []