diff --git a/tests/test_recorder.py b/tests/test_recorder.py new file mode 100644 index 0000000..7c8c474 --- /dev/null +++ b/tests/test_recorder.py @@ -0,0 +1,304 @@ +"""Tests for the recorder module.""" + +import pytest +from unittest.mock import MagicMock, patch + +from api_snapshot.recorder.recorder import ( + RecordedRequest, + RecordedResponse, + RequestResponsePair, + RecordingSession, + record_session, + record_multiple +) + + +class TestRecordedRequest: + """Tests for RecordedRequest class.""" + + def test_create_request(self): + """Test creating a recorded request.""" + req = RecordedRequest( + method="GET", + url="https://api.example.com/users", + headers={"Accept": "application/json"}, + body=None + ) + + assert req.method == "GET" + assert req.url == "https://api.example.com/users" + assert req.headers == {"Accept": "application/json"} + assert req.body is None + assert req.timestamp is not None + + def test_request_to_dict(self): + """Test converting request to dictionary.""" + req = RecordedRequest( + method="POST", + url="https://api.example.com/data", + headers={"Content-Type": "application/json"}, + body='{"key": "value"}' + ) + + data = req.to_dict() + + assert data["method"] == "POST" + assert data["url"] == "https://api.example.com/data" + assert data["headers"] == {"Content-Type": "application/json"} + assert data["body"] == '{"key": "value"}' + assert "timestamp" in data + + def test_request_from_dict(self): + """Test creating request from dictionary.""" + data = { + "method": "PUT", + "url": "https://api.example.com/resource/1", + "headers": {"Authorization": "Bearer token"}, + "body": "updated data", + "timestamp": "2024-01-01T12:00:00" + } + + req = RecordedRequest.from_dict(data) + + assert req.method == "PUT" + assert req.url == "https://api.example.com/resource/1" + assert req.headers == {"Authorization": "Bearer token"} + assert req.body == "updated data" + assert req.timestamp == "2024-01-01T12:00:00" + + +class TestRecordedResponse: + """Tests for RecordedResponse class.""" + + def test_create_response(self): + """Test creating a recorded response.""" + resp = RecordedResponse( + status_code=200, + headers={"Content-Type": "application/json"}, + body='{"success": true}', + latency_ms=150 + ) + + assert resp.status_code == 200 + assert resp.headers == {"Content-Type": "application/json"} + assert resp.body == '{"success": true}' + assert resp.latency_ms == 150 + + def test_response_to_dict(self): + """Test converting response to dictionary.""" + resp = RecordedResponse( + status_code=404, + headers={"Content-Type": "text/plain"}, + body="Not Found", + latency_ms=50 + ) + + data = resp.to_dict() + + assert data["status_code"] == 404 + assert data["headers"] == {"Content-Type": "text/plain"} + assert data["body"] == "Not Found" + assert data["latency_ms"] == 50 + + def test_response_from_dict(self): + """Test creating response from dictionary.""" + data = { + "status_code": 500, + "headers": {"Content-Type": "application/json"}, + "body": '{"error": "internal"}', + "latency_ms": 300 + } + + resp = RecordedResponse.from_dict(data) + + assert resp.status_code == 500 + assert resp.headers == {"Content-Type": "application/json"} + assert resp.body == '{"error": "internal"}' + assert resp.latency_ms == 300 + + +class TestRequestResponsePair: + """Tests for RequestResponsePair class.""" + + def test_create_pair(self, sample_request, sample_response): + """Test creating a request-response pair.""" + pair = RequestResponsePair( + request=sample_request, + response=sample_response + ) + + assert pair.request == sample_request + assert pair.response == sample_response + + def test_pair_to_dict(self, sample_pair): + """Test converting pair to dictionary.""" + data = sample_pair.to_dict() + + assert "request" in data + assert "response" in data + assert data["request"]["method"] == "GET" + assert data["response"]["status_code"] == 200 + + def test_pair_from_dict(self, sample_pair): + """Test creating pair from dictionary.""" + data = sample_pair.to_dict() + restored = RequestResponsePair.from_dict(data) + + assert restored.request.method == sample_pair.request.method + assert restored.response.status_code == sample_pair.response.status_code + + +class TestRecordingSession: + """Tests for RecordingSession class.""" + + def test_init_session(self): + """Test initializing a recording session.""" + session = RecordingSession() + + assert session.base_url is None + assert session.on_request is None + assert session.recordings == [] + + def test_init_with_base_url(self): + """Test initializing with base URL.""" + session = RecordingSession(base_url="https://api.example.com") + + assert session.base_url == "https://api.example.com" + + def test_build_url_absolute(self): + """Test building URL for absolute URL.""" + session = RecordingSession() + + url = session._build_url("https://other.com/api") + + assert url == "https://other.com/api" + + def test_build_url_relative(self): + """Test building URL for relative URL.""" + session = RecordingSession(base_url="https://api.example.com") + + url = session._build_url("/users") + + assert url == "https://api.example.com/users" + + def test_build_url_relative_no_base(self): + """Test building URL for relative URL without base.""" + session = RecordingSession() + + url = session._build_url("/users") + + assert url == "/users" + + def test_record_request_adds_to_recordings(self): + """Test that record_request adds to recordings.""" + session = RecordingSession() + + with patch('api_snapshot.recorder.recorder.requests.Session.request') as mock_request: + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.headers = {"Content-Type": "application/json"} + mock_response.text = '{"success": true}' + mock_request.return_value = mock_response + + session.record_request("GET", "https://api.example.com/users") + + assert len(session.recordings) == 1 + assert session.recordings[0].request.method == "GET" + assert session.recordings[0].response.status_code == 200 + + def test_on_request_callback(self): + """Test on_request callback is called.""" + callback = MagicMock() + session = RecordingSession(on_request=callback) + + with patch('api_snapshot.recorder.recorder.requests.Session.request') as mock_request: + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.headers = {} + mock_response.text = '{}' + mock_request.return_value = mock_response + + session.record_request("GET", "https://api.example.com") + + callback.assert_called_once() + + def test_clear_recordings(self): + """Test clearing recordings.""" + session = RecordingSession() + + with patch('api_snapshot.recorder.recorder.requests.Session.request') as mock_request: + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.headers = {} + mock_response.text = '{}' + mock_request.return_value = mock_response + + session.record_request("GET", "https://api.example.com") + assert len(session.recordings) == 1 + + session.clear() + assert len(session.recordings) == 0 + + def test_get_recordings(self): + """Test getting recordings.""" + session = RecordingSession() + + with patch('api_snapshot.recorder.recorder.requests.Session.request') as mock_request: + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.headers = {} + mock_response.text = '{}' + mock_request.return_value = mock_response + + session.record_request("GET", "https://api.example.com/1") + session.record_request("GET", "https://api.example.com/2") + + recordings = session.get_recordings() + + assert len(recordings) == 2 + assert recordings[0].request.url.endswith("/1") + assert recordings[1].request.url.endswith("/2") + + +class TestRecordFunctions: + """Tests for record_session and record_multiple functions.""" + + def test_record_session(self): + """Test record_session function.""" + with patch('api_snapshot.recorder.recorder.requests.Session.request') as mock_request: + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.headers = {"Content-Type": "application/json"} + mock_response.text = '{"data": "test"}' + mock_request.return_value = mock_response + + pairs = record_session( + url="https://api.example.com/api", + method="GET" + ) + + assert len(pairs) == 1 + assert pairs[0].request.method == "GET" + assert pairs[0].response.status_code == 200 + + def test_record_multiple(self): + """Test record_multiple function.""" + with patch('api_snapshot.recorder.recorder.requests.Session.request') as mock_request: + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.headers = {} + mock_response.text = '{}' + mock_request.return_value = mock_response + + config = [ + {"method": "GET", "url": "https://api.example.com/1"}, + {"method": "POST", "url": "https://api.example.com/2"}, + {"method": "DELETE", "url": "https://api.example.com/3"} + ] + + pairs = record_multiple(requests_config=config) + + assert len(pairs) == 3 + assert pairs[0].request.method == "GET" + assert pairs[1].request.method == "POST" + assert pairs[2].request.method == "DELETE"