Skip to content

Commit 8ec60d8

Browse files
committed
Increase client test coverage
1 parent 5d36cb7 commit 8ec60d8

File tree

8 files changed

+1644
-0
lines changed

8 files changed

+1644
-0
lines changed
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
"""Tests for errors.py module."""
2+
import json
3+
from unittest.mock import Mock
4+
from terminusdb_client.errors import (
5+
Error,
6+
InterfaceError,
7+
DatabaseError,
8+
OperationalError,
9+
AccessDeniedError,
10+
APIError,
11+
InvalidURIError
12+
)
13+
14+
15+
def test_base_error_class():
16+
"""Test base Error class can be instantiated."""
17+
error = Error()
18+
assert isinstance(error, Exception)
19+
20+
21+
def test_interface_error():
22+
"""Test InterfaceError with message."""
23+
message = "Database interface error occurred"
24+
error = InterfaceError(message)
25+
26+
assert isinstance(error, Error)
27+
assert error.message == message
28+
29+
30+
def test_interface_error_string_representation():
31+
"""Test InterfaceError has message attribute."""
32+
message = "Connection failed"
33+
error = InterfaceError(message)
34+
35+
assert hasattr(error, 'message')
36+
assert error.message == message
37+
38+
39+
def test_database_error_with_empty_response():
40+
"""Test DatabaseError with empty response text."""
41+
mock_response = Mock()
42+
mock_response.text = ""
43+
mock_response.status_code = 500
44+
45+
error = DatabaseError(mock_response)
46+
47+
assert "Unknown Error" in error.message
48+
assert error.status_code == 500
49+
50+
51+
def test_database_error_with_json_api_message():
52+
"""Test DatabaseError with JSON response containing api:message."""
53+
mock_response = Mock()
54+
mock_response.text = "Error response"
55+
mock_response.headers = {"content-type": "application/json"}
56+
mock_response.json.return_value = {
57+
"api:message": "Database operation failed",
58+
"details": "Additional context"
59+
}
60+
mock_response.status_code = 400
61+
62+
error = DatabaseError(mock_response)
63+
64+
assert "Database operation failed" in error.message
65+
assert error.status_code == 400
66+
assert error.error_obj == mock_response.json()
67+
68+
69+
def test_database_error_with_vio_message():
70+
"""Test DatabaseError with JSON response containing vio:message."""
71+
mock_response = Mock()
72+
mock_response.text = "Error response"
73+
mock_response.headers = {"content-type": "application/json"}
74+
mock_response.json.return_value = {
75+
"api:error": {
76+
"vio:message": "Validation failed"
77+
}
78+
}
79+
mock_response.status_code = 422
80+
81+
error = DatabaseError(mock_response)
82+
83+
assert "Validation failed" in error.message
84+
assert error.status_code == 422
85+
86+
87+
def test_database_error_with_unknown_json():
88+
"""Test DatabaseError with JSON response without known message fields."""
89+
mock_response = Mock()
90+
mock_response.text = "Error response"
91+
mock_response.headers = {"content-type": "application/json"}
92+
mock_response.json.return_value = {
93+
"unknown_field": "some value"
94+
}
95+
mock_response.status_code = 500
96+
97+
error = DatabaseError(mock_response)
98+
99+
assert "Unknown Error" in error.message
100+
assert error.status_code == 500
101+
102+
103+
def test_database_error_with_non_json_response():
104+
"""Test DatabaseError with non-JSON response."""
105+
mock_response = Mock()
106+
mock_response.text = "Plain text error message"
107+
mock_response.headers = {"content-type": "text/plain"}
108+
mock_response.status_code = 503
109+
110+
error = DatabaseError(mock_response)
111+
112+
assert error.message == "Plain text error message"
113+
assert error.error_obj is None
114+
assert error.status_code == 503
115+
116+
117+
def test_database_error_string_representation():
118+
"""Test DatabaseError __str__ method."""
119+
mock_response = Mock()
120+
mock_response.text = "Error message"
121+
mock_response.headers = {"content-type": "text/plain"}
122+
mock_response.status_code = 500
123+
124+
error = DatabaseError(mock_response)
125+
126+
assert str(error) == error.message
127+
128+
129+
def test_operational_error():
130+
"""Test OperationalError inherits from DatabaseError."""
131+
mock_response = Mock()
132+
mock_response.text = "Operational error"
133+
mock_response.headers = {"content-type": "text/plain"}
134+
mock_response.status_code = 500
135+
136+
error = OperationalError(mock_response)
137+
138+
assert isinstance(error, DatabaseError)
139+
assert isinstance(error, Error)
140+
assert error.message == "Operational error"
141+
142+
143+
def test_access_denied_error():
144+
"""Test AccessDeniedError inherits from DatabaseError."""
145+
mock_response = Mock()
146+
mock_response.text = "Access denied"
147+
mock_response.headers = {"content-type": "text/plain"}
148+
mock_response.status_code = 403
149+
150+
error = AccessDeniedError(mock_response)
151+
152+
assert isinstance(error, DatabaseError)
153+
assert isinstance(error, Error)
154+
assert error.message == "Access denied"
155+
assert error.status_code == 403
156+
157+
158+
def test_api_error_class_exists():
159+
"""Test that APIError class exists and inherits from DatabaseError."""
160+
# APIError has a design issue where it calls super().__init__() without response
161+
# Just verify the class exists and has correct inheritance
162+
assert issubclass(APIError, DatabaseError)
163+
assert issubclass(APIError, Error)
164+
165+
166+
def test_invalid_uri_error():
167+
"""Test InvalidURIError can be instantiated."""
168+
error = InvalidURIError()
169+
170+
assert isinstance(error, Error)
171+
assert isinstance(error, Exception)
172+
173+
174+
def test_invalid_uri_error_is_simple_error():
175+
"""Test InvalidURIError is a simple pass-through error."""
176+
# InvalidURIError is a simple pass class, no custom __init__
177+
error = InvalidURIError()
178+
179+
assert isinstance(error, Error)
180+
assert isinstance(error, Exception)
181+
182+
183+
def test_database_error_json_formatting():
184+
"""Test that DatabaseError includes formatted JSON details."""
185+
mock_response = Mock()
186+
mock_response.text = "Error"
187+
mock_response.headers = {"content-type": "application/json"}
188+
error_data = {
189+
"api:message": "Error message",
190+
"code": "ERR_001"
191+
}
192+
mock_response.json.return_value = error_data
193+
mock_response.status_code = 400
194+
195+
error = DatabaseError(mock_response)
196+
197+
# Check that formatted JSON is in the message
198+
formatted = json.dumps(error_data, indent=4, sort_keys=True)
199+
assert formatted in error.message
200+
201+
202+
def test_all_error_classes_are_exceptions():
203+
"""Test that all error classes inherit from Exception."""
204+
errors = [
205+
Error(),
206+
InterfaceError("test"),
207+
InvalidURIError()
208+
]
209+
210+
for error in errors:
211+
assert isinstance(error, Exception)
212+
213+
214+
def test_error_inheritance_chain():
215+
"""Test proper inheritance chain for database errors."""
216+
mock_response = Mock()
217+
mock_response.text = "test"
218+
mock_response.headers = {"content-type": "text/plain"}
219+
mock_response.status_code = 500
220+
221+
operational = OperationalError(mock_response)
222+
access_denied = AccessDeniedError(mock_response)
223+
224+
# All should be DatabaseError
225+
assert isinstance(operational, DatabaseError)
226+
assert isinstance(access_denied, DatabaseError)
227+
228+
# All should be Error
229+
assert isinstance(operational, Error)
230+
assert isinstance(access_denied, Error)
231+
232+
# All should be Exception
233+
assert isinstance(operational, Exception)
234+
assert isinstance(access_denied, Exception)
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"""Tests for query_syntax/query_syntax.py module."""
2+
from terminusdb_client.query_syntax import query_syntax
3+
from terminusdb_client.woqlquery import WOQLQuery
4+
5+
6+
def test_query_syntax_exports_var():
7+
"""Test that Var is exported."""
8+
assert 'Var' in query_syntax.__all__
9+
assert hasattr(query_syntax, 'Var')
10+
11+
12+
def test_query_syntax_exports_vars():
13+
"""Test that Vars is exported."""
14+
assert 'Vars' in query_syntax.__all__
15+
assert hasattr(query_syntax, 'Vars')
16+
17+
18+
def test_query_syntax_exports_doc():
19+
"""Test that Doc is exported."""
20+
assert 'Doc' in query_syntax.__all__
21+
assert hasattr(query_syntax, 'Doc')
22+
23+
24+
def test_barred_items_not_exported():
25+
"""Test that barred items (re, vars) are not exported."""
26+
assert 're' not in query_syntax.__all__
27+
assert 'vars' not in query_syntax.__all__
28+
29+
30+
def test_allowed_dunder_methods_exported():
31+
"""Test that allowed dunder methods are exported."""
32+
# __and__, __or__, __add__ should be exported
33+
assert '__and__' in query_syntax.__all__
34+
assert '__or__' in query_syntax.__all__
35+
assert '__add__' in query_syntax.__all__
36+
37+
38+
def test_dynamic_function_creation():
39+
"""Test that functions are dynamically created from WOQLQuery."""
40+
# Check that some common WOQLQuery methods are available
41+
woql_methods = ['triple', 'select', 'limit']
42+
43+
for method in woql_methods:
44+
assert hasattr(query_syntax, method), f"{method} should be available"
45+
assert method in query_syntax.__all__, f"{method} should be in __all__"
46+
47+
48+
def test_created_functions_are_callable():
49+
"""Test that dynamically created functions are callable."""
50+
# Test that we can call a dynamically created function
51+
if hasattr(query_syntax, 'limit'):
52+
func = getattr(query_syntax, 'limit')
53+
assert callable(func)
54+
55+
56+
def test_created_function_returns_woqlquery():
57+
"""Test that created functions return WOQLQuery instances."""
58+
# Test a simple function that exists on WOQLQuery
59+
if hasattr(query_syntax, 'limit'):
60+
func = getattr(query_syntax, 'limit')
61+
result = func(10)
62+
# Should return a WOQLQuery or similar object
63+
assert result is not None
64+
65+
66+
def test_private_attributes_not_exported():
67+
"""Test that private attributes (starting with _) are not exported."""
68+
for attr in query_syntax.__all__:
69+
# All exported attributes should either be in __ALLOWED or not start with _
70+
if attr not in ['__and__', '__or__', '__add__']:
71+
assert not attr.startswith('_'), f"{attr} should not start with underscore"
72+
73+
74+
def test_module_attributes():
75+
"""Test module has expected attributes."""
76+
# These are defined in the module
77+
assert hasattr(query_syntax, '__BARRED')
78+
assert hasattr(query_syntax, '__ALLOWED')
79+
assert hasattr(query_syntax, '__module')
80+
assert hasattr(query_syntax, '__exported')

0 commit comments

Comments
 (0)