Skip to content

Commit 8709665

Browse files
committed
test: expand test coverage with comprehensive exception and client tests
- Added tests for all exception types including APIError context - Added comprehensive client tests including env var handling - Added tests for context manager and direct API methods - All tests pass locally, ready for CI validation
1 parent 9e85f01 commit 8709665

2 files changed

Lines changed: 125 additions & 4 deletions

File tree

tests/unit/test_client.py

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,72 @@
11
"""Unit tests for NutrientClient."""
22

3+
import os
4+
from unittest.mock import patch
5+
36
from nutrient_dws.client import NutrientClient
47

58

6-
def test_client_init():
7-
"""Test client initialization."""
9+
def test_client_init_with_api_key():
10+
"""Test client initialization with API key."""
811
client = NutrientClient(api_key="test-key")
912
assert client is not None
13+
assert client._http_client._api_key == "test-key"
14+
15+
16+
def test_client_init_with_env_var():
17+
"""Test client initialization with environment variable."""
18+
with patch.dict(os.environ, {"NUTRIENT_API_KEY": "env-key"}):
19+
client = NutrientClient()
20+
assert client._http_client._api_key == "env-key"
21+
22+
23+
def test_client_init_precedence():
24+
"""Test that explicit API key takes precedence over env var."""
25+
with patch.dict(os.environ, {"NUTRIENT_API_KEY": "env-key"}):
26+
client = NutrientClient(api_key="explicit-key")
27+
assert client._http_client._api_key == "explicit-key"
28+
29+
30+
def test_client_build_method():
31+
"""Test that build() returns a BuildAPIWrapper."""
32+
client = NutrientClient(api_key="test-key")
33+
builder = client.build("test.pdf")
34+
35+
# Check class name to avoid import issues
36+
assert builder.__class__.__name__ == "BuildAPIWrapper"
37+
38+
39+
def test_client_has_direct_api_methods():
40+
"""Test that client has direct API methods."""
41+
client = NutrientClient(api_key="test-key")
42+
43+
# Check that direct API methods exist (from DirectAPIMixin)
44+
assert hasattr(client, "convert_to_pdf")
45+
assert hasattr(client, "flatten_annotations")
46+
assert hasattr(client, "rotate_pages")
47+
assert hasattr(client, "watermark_pdf")
48+
assert hasattr(client, "ocr_pdf")
49+
assert hasattr(client, "apply_redactions")
50+
assert hasattr(client, "merge_pdfs")
51+
52+
53+
def test_client_context_manager():
54+
"""Test client can be used as context manager."""
55+
with NutrientClient(api_key="test-key") as client:
56+
assert client is not None
57+
# Check that HTTP client is not closed
58+
assert hasattr(client._http_client, "_session")
59+
60+
# After exiting context, HTTP client session should be closed
61+
# We can't directly check if closed, but the method should have been called
62+
63+
64+
def test_client_close():
65+
"""Test client close method."""
66+
client = NutrientClient(api_key="test-key")
67+
68+
# Verify HTTP client exists
69+
assert hasattr(client, "_http_client")
70+
71+
# Close should not raise an error
72+
client.close()

tests/unit/test_exceptions.py

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
from nutrient_dws.exceptions import (
44
APIError,
55
AuthenticationError,
6+
FileProcessingError,
67
NutrientError,
8+
NutrientTimeoutError,
9+
ValidationError,
710
)
811

912

@@ -21,8 +24,63 @@ def test_authentication_error():
2124
assert isinstance(exc, NutrientError)
2225

2326

24-
def test_api_error():
25-
"""Test API error."""
27+
def test_api_error_basic():
28+
"""Test API error without additional context."""
2629
exc = APIError("API request failed")
2730
assert str(exc) == "API request failed"
2831
assert isinstance(exc, NutrientError)
32+
assert exc.status_code is None
33+
assert exc.response_body is None
34+
assert exc.request_id is None
35+
36+
37+
def test_api_error_with_status():
38+
"""Test API error with status code."""
39+
exc = APIError("Not found", status_code=404)
40+
assert exc.status_code == 404
41+
assert "Status: 404" in str(exc)
42+
43+
44+
def test_api_error_full_context():
45+
"""Test API error with all context."""
46+
exc = APIError(
47+
"Server error",
48+
status_code=500,
49+
response_body='{"error": "Internal server error"}',
50+
request_id="req-123",
51+
)
52+
assert exc.status_code == 500
53+
assert exc.response_body == '{"error": "Internal server error"}'
54+
assert exc.request_id == "req-123"
55+
assert "Status: 500" in str(exc)
56+
assert "Request ID: req-123" in str(exc)
57+
assert "Response:" in str(exc)
58+
59+
60+
def test_validation_error():
61+
"""Test validation error."""
62+
exc = ValidationError("Invalid input")
63+
assert str(exc) == "Invalid input"
64+
assert isinstance(exc, NutrientError)
65+
assert exc.errors == {}
66+
67+
68+
def test_validation_error_with_details():
69+
"""Test validation error with error details."""
70+
errors = {"field": "Invalid value"}
71+
exc = ValidationError("Validation failed", errors=errors)
72+
assert exc.errors == errors
73+
74+
75+
def test_timeout_error():
76+
"""Test timeout error."""
77+
exc = NutrientTimeoutError("Request timed out")
78+
assert str(exc) == "Request timed out"
79+
assert isinstance(exc, NutrientError)
80+
81+
82+
def test_file_processing_error():
83+
"""Test file processing error."""
84+
exc = FileProcessingError("Failed to process file")
85+
assert str(exc) == "Failed to process file"
86+
assert isinstance(exc, NutrientError)

0 commit comments

Comments
 (0)