Skip to content

Commit d5d2950

Browse files
committed
test: add async response headers integration tests
1 parent 1db32d7 commit d5d2950

1 file changed

Lines changed: 169 additions & 0 deletions

File tree

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
from unittest.mock import AsyncMock
2+
3+
import pytest
4+
5+
import resend
6+
7+
8+
pytestmark = pytest.mark.asyncio
9+
10+
11+
class TestResponseHeadersIntegrationAsync:
12+
def setup_method(self) -> None:
13+
resend.api_key = "re_test_key"
14+
15+
async def test_email_send_async_response_includes_http_headers(self) -> None:
16+
"""Test that async email send response includes http_headers."""
17+
mock_client = AsyncMock()
18+
mock_client.request.return_value = (
19+
b'{"id": "email_123"}',
20+
200,
21+
{
22+
"content-type": "application/json",
23+
"x-request-id": "req_abc123",
24+
"x-ratelimit-limit": "100",
25+
"x-ratelimit-remaining": "95",
26+
"x-ratelimit-reset": "1699564800",
27+
},
28+
)
29+
30+
original_client = resend.default_async_http_client
31+
resend.default_async_http_client = mock_client
32+
33+
try:
34+
response = await resend.Emails.send_async(
35+
{
36+
"from": "test@example.com",
37+
"to": "user@example.com",
38+
"subject": "Test",
39+
"html": "<p>Test</p>",
40+
}
41+
)
42+
43+
assert isinstance(response, dict)
44+
assert response["id"] == "email_123"
45+
assert "http_headers" in response
46+
assert response["http_headers"]["x-request-id"] == "req_abc123"
47+
assert response["http_headers"]["x-ratelimit-limit"] == "100"
48+
assert response["http_headers"]["x-ratelimit-remaining"] == "95"
49+
assert response["http_headers"]["x-ratelimit-reset"] == "1699564800"
50+
51+
finally:
52+
resend.default_async_http_client = original_client
53+
54+
async def test_list_async_response_includes_http_headers(self) -> None:
55+
"""Test that async list responses include http_headers."""
56+
mock_client = AsyncMock()
57+
mock_client.request.return_value = (
58+
b'{"data": [{"id": "key_1"}, {"id": "key_2"}]}',
59+
200,
60+
{
61+
"content-type": "application/json",
62+
"x-request-id": "req_xyz",
63+
},
64+
)
65+
66+
original_client = resend.default_async_http_client
67+
resend.default_async_http_client = mock_client
68+
69+
try:
70+
response = await resend.ApiKeys.list_async()
71+
72+
assert isinstance(response, dict)
73+
assert "data" in response
74+
assert "http_headers" in response
75+
assert response["http_headers"]["x-request-id"] == "req_xyz"
76+
77+
finally:
78+
resend.default_async_http_client = original_client
79+
80+
async def test_received_email_async_headers_not_overwritten_by_http_headers(
81+
self,
82+
) -> None:
83+
"""
84+
Regression test: ReceivedEmail.headers (MIME email headers) must not be
85+
overwritten by the injected http_headers (API response headers).
86+
"""
87+
mock_client = AsyncMock()
88+
mock_client.request.return_value = (
89+
b'{"id": "email_456", "object": "received_email", '
90+
b'"from": "sender@example.com", "to": ["recipient@example.com"], '
91+
b'"subject": "Hello", "created_at": "2024-01-01T00:00:00Z", '
92+
b'"message_id": "msg_123", "attachments": [], '
93+
b'"headers": {"List-Unsubscribe": "<mailto:unsub@example.com>", '
94+
b'"X-Custom": "value"}}',
95+
200,
96+
{
97+
"content-type": "application/json",
98+
"x-request-id": "req_recv_123",
99+
"x-ratelimit-limit": "100",
100+
},
101+
)
102+
103+
original_client = resend.default_async_http_client
104+
resend.default_async_http_client = mock_client
105+
106+
try:
107+
response = await resend.Emails.Receiving.get_async(
108+
email_id="email_456"
109+
)
110+
111+
assert isinstance(response, dict)
112+
assert response["id"] == "email_456"
113+
114+
# MIME email headers must be intact
115+
assert "headers" in response
116+
assert response["headers"]["List-Unsubscribe"] == "<mailto:unsub@example.com>"
117+
assert response["headers"]["X-Custom"] == "value"
118+
119+
# HTTP response headers must be injected separately under http_headers
120+
assert "http_headers" in response
121+
assert response["http_headers"]["x-request-id"] == "req_recv_123"
122+
assert response["http_headers"]["x-ratelimit-limit"] == "100"
123+
124+
# The two must not collide
125+
assert response["headers"] is not response["http_headers"]
126+
127+
finally:
128+
resend.default_async_http_client = original_client
129+
130+
async def test_send_async_with_custom_email_headers_does_not_collide(
131+
self,
132+
) -> None:
133+
"""
134+
Test that SendParams.headers (custom email headers sent to the recipient)
135+
do not collide with http_headers in the response.
136+
"""
137+
mock_client = AsyncMock()
138+
mock_client.request.return_value = (
139+
b'{"id": "email_789"}',
140+
200,
141+
{
142+
"content-type": "application/json",
143+
"x-request-id": "req_789",
144+
},
145+
)
146+
147+
original_client = resend.default_async_http_client
148+
resend.default_async_http_client = mock_client
149+
150+
try:
151+
response = await resend.Emails.send_async(
152+
{
153+
"from": "test@example.com",
154+
"to": "user@example.com",
155+
"subject": "Test",
156+
"html": "<p>Test</p>",
157+
"headers": {"List-Unsubscribe": "<mailto:unsub@example.com>"},
158+
}
159+
)
160+
161+
assert isinstance(response, dict)
162+
assert response["id"] == "email_789"
163+
# SendResponse does not echo back SendParams.headers — only id is returned
164+
assert "headers" not in response
165+
assert "http_headers" in response
166+
assert response["http_headers"]["x-request-id"] == "req_789"
167+
168+
finally:
169+
resend.default_async_http_client = original_client

0 commit comments

Comments
 (0)