Skip to content

Commit 68c6c51

Browse files
committed
Add a basic test file
1 parent 7b09e48 commit 68c6c51

1 file changed

Lines changed: 186 additions & 0 deletions

File tree

tests/test_client.py

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
# tests/test_client.py
2+
3+
import pytest
4+
from unittest import mock # For mocking requests
5+
import requests # Need this to mock its methods and exceptions
6+
import json # For JSONDecodeError simulation if needed (though requests.exceptions handles it)
7+
8+
# --- Corrected Imports ---
9+
# Import directly from the client module where they are defined
10+
from freerouting.client import (
11+
FreeroutingClient,
12+
FreeroutingError,
13+
FreeroutingAPIError,
14+
FreeroutingAuthError
15+
)
16+
17+
18+
# --- Fixtures ---
19+
20+
@pytest.fixture
21+
def api_key():
22+
"""Provides a dummy API key for tests."""
23+
return "test_api_key_123"
24+
25+
@pytest.fixture
26+
def client(api_key):
27+
"""Provides a FreeroutingClient instance for tests."""
28+
# Using a non-standard base_url ensures we definitely don't hit the real API
29+
return FreeroutingClient(api_key=api_key, base_url="http://test.invalid")
30+
31+
# --- Test Cases ---
32+
33+
def test_client_initialization(client, api_key):
34+
"""Test if the client initializes correctly with mandatory args."""
35+
assert client.api_key == api_key
36+
assert client.base_url == "http://test.invalid/v1" # Default version is v1
37+
assert client.session_id is None
38+
assert isinstance(client.profile_id, str)
39+
40+
def test_client_initialization_custom_params(api_key):
41+
"""Test client initialization with custom parameters."""
42+
client = FreeroutingClient(
43+
api_key=api_key,
44+
base_url="http://localhost:8080",
45+
version="dev",
46+
profile_id="custom-profile-id",
47+
host_name="pytest-runner/1.0"
48+
)
49+
assert client.base_url == "http://localhost:8080/dev"
50+
assert client.profile_id == "custom-profile-id"
51+
assert client.host_name == "pytest-runner/1.0"
52+
53+
def test_client_initialization_no_api_key():
54+
"""Test that ValueError is raised if no API key is provided."""
55+
with pytest.raises(ValueError, match="API key must be provided"):
56+
FreeroutingClient(api_key="")
57+
58+
59+
# --- Mocking API Calls ---
60+
61+
@mock.patch('requests.get') # Patch where it's used by the client
62+
def test_get_system_status_success(mock_get, client):
63+
"""Test a successful GET request (e.g., get_system_status)."""
64+
mock_response = mock.Mock()
65+
mock_response.status_code = 200
66+
mock_response.json.return_value = {"status": "OK", "message": "Service is running"}
67+
mock_get.return_value = mock_response
68+
69+
status = client.get_system_status()
70+
71+
assert status == {"status": "OK", "message": "Service is running"}
72+
mock_get.assert_called_once()
73+
args, kwargs = mock_get.call_args
74+
assert args[0] == "http://test.invalid/v1/system/status"
75+
assert "Authorization" in kwargs['headers']
76+
assert kwargs['headers']['Authorization'] == f"Bearer {client.api_key}"
77+
78+
@mock.patch('requests.post')
79+
def test_create_session_success(mock_post, client):
80+
"""Test a successful POST request (e.g., create_session)."""
81+
session_id = "session-xyz-789"
82+
mock_response = mock.Mock()
83+
mock_response.status_code = 201
84+
mock_response.json.return_value = {"id": session_id, "status": "created"}
85+
mock_post.return_value = mock_response
86+
87+
session_details = client.create_session()
88+
89+
assert session_details == {"id": session_id, "status": "created"}
90+
assert client.session_id == session_id
91+
mock_post.assert_called_once_with(
92+
"http://test.invalid/v1/sessions/create",
93+
headers=client._get_headers(),
94+
data=None # No data payload expected for this call
95+
)
96+
97+
@mock.patch('requests.put')
98+
def test_start_job_success(mock_put, client):
99+
"""Test a successful PUT request returning 202 Accepted (e.g., start_job)."""
100+
job_id = "job-abc-123"
101+
mock_response = mock.Mock()
102+
mock_response.status_code = 202
103+
mock_response.content = b'' # Simulate empty response body for 202
104+
# Make .json() raise an error if called, as per client._make_request handling
105+
mock_response.json.side_effect = requests.exceptions.JSONDecodeError("Expecting value", "doc", 0)
106+
mock_put.return_value = mock_response
107+
108+
result = client.start_job(job_id)
109+
110+
assert result == {} # Expect empty dict for 202 with no body
111+
mock_put.assert_called_once_with(
112+
f"http://test.invalid/v1/jobs/{job_id}/start",
113+
headers=client._get_headers(),
114+
data=None
115+
)
116+
117+
@mock.patch('requests.get')
118+
def test_make_request_api_error(mock_get, client):
119+
"""Test handling of a non-2xx API error response (e.g., 404)."""
120+
mock_response = mock.Mock()
121+
mock_response.status_code = 404
122+
mock_response.text = "Resource Not Found"
123+
mock_response.json.side_effect = requests.exceptions.JSONDecodeError("Expecting value", "doc", 0)
124+
mock_get.return_value = mock_response
125+
126+
with pytest.raises(FreeroutingAPIError) as excinfo:
127+
client.get_system_status() # Any method using _make_request with GET
128+
129+
assert excinfo.value.status_code == 404
130+
assert "Resource Not Found" in str(excinfo.value)
131+
assert "API request failed: 404" in str(excinfo.value)
132+
133+
@mock.patch('requests.get')
134+
def test_make_request_auth_error(mock_get, client):
135+
"""Test handling of a 401 Unauthorized error."""
136+
mock_response = mock.Mock()
137+
mock_response.status_code = 401
138+
mock_response.text = "Invalid credentials provided"
139+
mock_response.json.side_effect = requests.exceptions.JSONDecodeError("Expecting value", "doc", 0)
140+
mock_get.return_value = mock_response
141+
142+
with pytest.raises(FreeroutingAuthError) as excinfo:
143+
client.get_system_status()
144+
145+
assert "Authentication failed: 401" in str(excinfo.value)
146+
assert "Invalid credentials provided" in str(excinfo.value)
147+
148+
149+
def test_get_session_no_id_error(client):
150+
"""Test that get_session raises ValueError if no session ID is available."""
151+
client.session_id = None
152+
with pytest.raises(ValueError, match="No session ID provided or stored internally"):
153+
client.get_session()
154+
155+
def test_enqueue_job_no_id_error(client):
156+
"""Test that enqueue_job raises ValueError if no session ID is available."""
157+
client.session_id = None
158+
with pytest.raises(ValueError, match="No session ID provided or stored internally"):
159+
client.enqueue_job(name="test_job")
160+
161+
# --- Value Error tests for missing IDs ---
162+
def test_get_job_no_id(client):
163+
"""Test ValueError if job_id is empty string for get_job."""
164+
with pytest.raises(ValueError, match="Job ID must be provided"):
165+
client.get_job(job_id="")
166+
167+
def test_start_job_no_id(client):
168+
"""Test ValueError if job_id is empty string for start_job."""
169+
with pytest.raises(ValueError, match="Job ID must be provided"):
170+
client.start_job(job_id="")
171+
172+
def test_upload_input_no_params(client):
173+
"""Test ValueErrors for missing parameters in upload_input."""
174+
with pytest.raises(ValueError, match="Job ID must be provided"):
175+
client.upload_input(job_id="", filename="f", file_path="p")
176+
with pytest.raises(ValueError, match="Filename must be provided"):
177+
client.upload_input(job_id="j", filename="", file_path="p")
178+
with pytest.raises(ValueError, match="File path must be provided"):
179+
client.upload_input(job_id="j", filename="f", file_path="")
180+
181+
182+
# --- TODO: Add more tests! ---
183+
# - Test methods like upload_input with file mocking (e.g., using mock_open)
184+
# - Test download_output (mocking GET and checking file write if path provided)
185+
# - Test run_routing_job workflow (requires more complex mocking of multiple steps)
186+
# - Test network errors (patch requests.get/post/put to raise requests.exceptions.RequestException)

0 commit comments

Comments
 (0)