"""Tests for common schemas — FailedUrl, PageImage, make_error_detail.""" import pytest from pydantic import ValidationError from web.schemas.common import FailedUrl, PageImage, make_error_detail class TestFailedUrl: """Tests for FailedUrl schema.""" def test_round_trip(self) -> None: """Test serialization round-trip.""" obj = FailedUrl(url="https://example.com", error="Timeout") data = obj.model_dump() assert data == {"url": "https://example.com", "error": "Timeout"} restored = FailedUrl.model_validate(data) assert restored == obj def test_extra_fields_rejected(self) -> None: """Test that extra fields are rejected.""" with pytest.raises(ValidationError): FailedUrl(url="https://example.com", error="Timeout", extra="bad") def test_json_round_trip(self) -> None: """Test JSON serialization round-trip.""" obj = FailedUrl(url="https://a.com", error="Connection refused") json_str = obj.model_dump_json() restored = FailedUrl.model_validate_json(json_str) assert restored == obj class TestPageImage: """Tests for PageImage schema.""" def test_round_trip(self) -> None: """Test serialization round-trip.""" obj = PageImage(url="https://img.example.com/1.jpg", alt="Photo") data = obj.model_dump() assert data == {"url": "https://img.example.com/1.jpg", "alt": "Photo"} restored = PageImage.model_validate(data) assert restored == obj def test_alt_defaults_to_none(self) -> None: """Test that alt text defaults to None.""" obj = PageImage(url="https://img.example.com/1.jpg") assert obj.alt is None def test_extra_fields_rejected(self) -> None: """Test that extra fields are rejected.""" with pytest.raises(ValidationError): PageImage(url="https://img.example.com/1.jpg", width=100) class TestMakeErrorDetail: """Tests for make_error_detail helper.""" def test_basic(self) -> None: """Test basic error detail.""" result = make_error_detail("not_found", "Resource not found") assert result == {"error": "not_found", "message": "Resource not found"} def test_with_request_id(self) -> None: """Test error detail with request_id.""" result = make_error_detail("timeout", "Request timed out", request_id="abc-123") assert result == { "error": "timeout", "message": "Request timed out", "request_id": "abc-123", } def test_with_extra_fields(self) -> None: """Test error detail with extra fields.""" result = make_error_detail( "rate_limit", "Too many requests", request_id="xyz", retry_after=30.0, ) assert result == { "error": "rate_limit", "message": "Too many requests", "request_id": "xyz", "retry_after": 30.0, } def test_no_request_id_when_none(self) -> None: """Test that request_id is omitted when None.""" result = make_error_detail("error", "msg", request_id=None) assert "request_id" not in result