Skip to content

Commit cfbc89e

Browse files
committed
Add unit tests for JsonRpcBase header support and auth handling
1 parent cdb42e9 commit cfbc89e

File tree

4 files changed

+84
-0
lines changed

4 files changed

+84
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,4 @@ dmypy.json
135135

136136
# MacOS artifacts
137137
.DS_Store
138+
.jrb/ ..

tests/unit/asyn/clients/__init__.py

Whitespace-only changes.
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
from unittest.mock import AsyncMock, patch
2+
3+
import pytest
4+
from httpx import Response
5+
6+
from xrpl.asyncio.clients.exceptions import XRPLAuthenticationException
7+
from xrpl.asyncio.clients.json_rpc_base import JsonRpcBase
8+
from xrpl.models.requests import ServerInfo
9+
10+
11+
@pytest.mark.asyncio
12+
async def test_global_headers_are_sent():
13+
client = JsonRpcBase(
14+
"https://xrpl.fake", headers={"Authorization": "Bearer testtoken"}
15+
)
16+
17+
with patch("httpx.AsyncClient.post", new_callable=AsyncMock) as mock_post:
18+
mock_post.return_value = Response(
19+
status_code=200,
20+
json={"result": {"status": "success"}, "id": 1},
21+
)
22+
23+
await client._request_impl(ServerInfo())
24+
25+
headers_sent = mock_post.call_args.kwargs["headers"]
26+
assert headers_sent["Authorization"] == "Bearer testtoken"
27+
assert headers_sent["Content-Type"] == "application/json"
28+
29+
30+
@pytest.mark.asyncio
31+
async def test_per_request_headers_override_global():
32+
client = JsonRpcBase(
33+
"https://xrpl.fake", headers={"Authorization": "Bearer default"}
34+
)
35+
36+
with patch("httpx.AsyncClient.post", new_callable=AsyncMock) as mock_post:
37+
mock_post.return_value = Response(
38+
status_code=200,
39+
json={"result": {"status": "success"}, "id": 1},
40+
)
41+
42+
await client._request_impl(
43+
ServerInfo(), headers={"Authorization": "Bearer override"}
44+
)
45+
46+
headers_sent = mock_post.call_args.kwargs["headers"]
47+
assert headers_sent["Authorization"] == "Bearer override"
48+
49+
50+
@pytest.mark.asyncio
51+
async def test_no_headers_does_not_crash():
52+
client = JsonRpcBase("https://xrpl.fake")
53+
54+
with patch("httpx.AsyncClient.post", new_callable=AsyncMock) as mock_post:
55+
mock_post.return_value = Response(
56+
status_code=200,
57+
json={"result": {"status": "success"}, "id": 1},
58+
)
59+
60+
await client._request_impl(ServerInfo())
61+
62+
headers_sent = mock_post.call_args.kwargs["headers"]
63+
assert headers_sent["Content-Type"] == "application/json"
64+
65+
66+
@pytest.mark.asyncio
67+
async def test_raises_on_401_403():
68+
client = JsonRpcBase("https://xrpl.fake")
69+
70+
for code in [401, 403]:
71+
with patch("httpx.AsyncClient.post", new_callable=AsyncMock) as mock_post:
72+
mock_post.return_value = Response(status_code=code, text="Unauthorized")
73+
74+
with pytest.raises(
75+
XRPLAuthenticationException, match="Authentication failed"
76+
):
77+
await client._request_impl(ServerInfo())

xrpl/asyncio/clients/exceptions.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,9 @@ class XRPLWebsocketException(XRPLException):
3636
"""
3737

3838
pass
39+
40+
41+
class XRPLAuthenticationException(XRPLRequestFailureException):
42+
"""Raised when authentication with the XRPL node fails (401 or 403)."""
43+
44+
pass

0 commit comments

Comments
 (0)