From 1367cfdf5edcf6314d80312ca57d66d7699ae4b3 Mon Sep 17 00:00:00 2001 From: Pedro Impulcetto Date: Thu, 30 May 2024 19:25:27 -0300 Subject: [PATCH] Feat/generics request (#107) --- resend/api_keys/_api_keys.py | 18 +++++------ resend/audiences/_audiences.py | 24 +++++++++------ resend/contacts/_contacts.py | 30 +++++++++++-------- resend/domains/_domains.py | 36 +++++++++++++--------- resend/emails/_batch.py | 10 +++---- resend/emails/_emails.py | 20 ++++++++----- resend/exceptions.py | 9 ++++++ resend/request.py | 31 ++++++++++++++----- tests/api_keys_test.py | 14 +++++++++ tests/audiences_test.py | 24 +++++++++++++++ tests/batch_emails_test.py | 20 +++++++++++++ tests/conftest.py | 4 +-- tests/contacts_test.py | 55 ++++++++++++++++++++++++++++++++++ tests/domains_test.py | 45 ++++++++++++++++++++++++++++ tests/emails_test.py | 20 ++++++++++++- 15 files changed, 292 insertions(+), 68 deletions(-) diff --git a/resend/api_keys/_api_keys.py b/resend/api_keys/_api_keys.py index a686139..60f0361 100644 --- a/resend/api_keys/_api_keys.py +++ b/resend/api_keys/_api_keys.py @@ -53,12 +53,10 @@ def create(cls, params: CreateParams) -> ApiKey: ApiKey: The new API key object """ path = "/api-keys" - return cast( - ApiKey, - request.Request( - path=path, params=cast(Dict[Any, Any], params), verb="post" - ).perform(), - ) + resp = request.Request[ApiKey]( + path=path, params=cast(Dict[Any, Any], params), verb="post" + ).perform_with_content() + return resp @classmethod def list(cls) -> ListResponse: @@ -70,8 +68,10 @@ def list(cls) -> ListResponse: ListResponse: A list of API key objects """ path = "/api-keys" - resp = request.Request(path=path, params={}, verb="get").perform() - return cast(_ListResponse, resp) + resp = request.Request[_ListResponse]( + path=path, params={}, verb="get" + ).perform_with_content() + return resp @classmethod def remove(cls, api_key_id: str) -> None: @@ -88,5 +88,5 @@ def remove(cls, api_key_id: str) -> None: path = f"/api-keys/{api_key_id}" # This would raise if failed - request.Request(path=path, params={}, verb="delete").perform() + request.Request[None](path=path, params={}, verb="delete").perform() return None diff --git a/resend/audiences/_audiences.py b/resend/audiences/_audiences.py index 457d47b..cfff751 100644 --- a/resend/audiences/_audiences.py +++ b/resend/audiences/_audiences.py @@ -43,10 +43,10 @@ def create(cls, params: CreateParams) -> Audience: Audience: The new audience object """ path = "/audiences" - resp = request.Request( + resp = request.Request[Audience]( path=path, params=cast(Dict[Any, Any], params), verb="post" - ).perform() - return cast(Audience, resp) + ).perform_with_content() + return resp @classmethod def list(cls) -> ListResponse: @@ -58,8 +58,10 @@ def list(cls) -> ListResponse: ListResponse: A list of audience objects """ path = "/audiences/" - resp = request.Request(path=path, params={}, verb="get").perform() - return cast(_ListResponse, resp) + resp = request.Request[_ListResponse]( + path=path, params={}, verb="get" + ).perform_with_content() + return resp @classmethod def get(cls, id: str) -> Audience: @@ -74,8 +76,10 @@ def get(cls, id: str) -> Audience: Audience: The audience object """ path = f"/audiences/{id}" - resp = request.Request(path=path, params={}, verb="get").perform() - return cast(Audience, resp) + resp = request.Request[Audience]( + path=path, params={}, verb="get" + ).perform_with_content() + return resp @classmethod def remove(cls, id: str) -> Audience: @@ -90,5 +94,7 @@ def remove(cls, id: str) -> Audience: Audience: The audience object """ path = f"/audiences/{id}" - resp = request.Request(path=path, params={}, verb="delete").perform() - return cast(Audience, resp) + resp = request.Request[Audience]( + path=path, params={}, verb="delete" + ).perform_with_content() + return resp diff --git a/resend/contacts/_contacts.py b/resend/contacts/_contacts.py index a210803..7f89216 100644 --- a/resend/contacts/_contacts.py +++ b/resend/contacts/_contacts.py @@ -85,10 +85,10 @@ def create(cls, params: CreateParams) -> Contact: Contact: The new contact object """ path = f"/audiences/{params['audience_id']}/contacts" - resp = request.Request( + resp = request.Request[Contact]( path=path, params=cast(Dict[Any, Any], params), verb="post" - ).perform() - return cast(Contact, resp) + ).perform_with_content() + return resp @classmethod def update(cls, params: UpdateParams) -> Contact: @@ -103,10 +103,10 @@ def update(cls, params: UpdateParams) -> Contact: Contact: The updated contact object """ path = f"/audiences/{params['audience_id']}/contacts/{params['id']}" - resp = request.Request( + resp = request.Request[Contact]( path=path, params=cast(Dict[Any, Any], params), verb="patch" - ).perform() - return cast(Contact, resp) + ).perform_with_content() + return resp @classmethod def list(cls, audience_id: str) -> ListResponse: @@ -121,8 +121,10 @@ def list(cls, audience_id: str) -> ListResponse: ListResponse: A list of contact objects """ path = f"/audiences/{audience_id}/contacts" - resp = request.Request(path=path, params={}, verb="get").perform() - return cast(_ListResponse, resp) + resp = request.Request[_ListResponse]( + path=path, params={}, verb="get" + ).perform_with_content() + return resp @classmethod def get(cls, id: str, audience_id: str) -> Contact: @@ -138,8 +140,10 @@ def get(cls, id: str, audience_id: str) -> Contact: Contact: The contact object """ path = f"/audiences/{audience_id}/contacts/{id}" - resp = request.Request(path=path, params={}, verb="get").perform() - return cast(Contact, resp) + resp = request.Request[Contact]( + path=path, params={}, verb="get" + ).perform_with_content() + return resp @classmethod def remove(cls, audience_id: str, id: str = "", email: str = "") -> Contact: @@ -160,5 +164,7 @@ def remove(cls, audience_id: str, id: str = "", email: str = "") -> Contact: raise ValueError("id or email must be provided") path = f"/audiences/{audience_id}/contacts/{contact}" - resp = request.Request(path=path, params={}, verb="delete").perform() - return cast(Contact, resp) + resp = request.Request[Contact]( + path=path, params={}, verb="delete" + ).perform_with_content() + return resp diff --git a/resend/domains/_domains.py b/resend/domains/_domains.py index d1e013f..689899a 100644 --- a/resend/domains/_domains.py +++ b/resend/domains/_domains.py @@ -61,10 +61,10 @@ def create(cls, params: CreateParams) -> Domain: Domain: The new domain object """ path = "/domains" - resp = request.Request( + resp = request.Request[Domain]( path=path, params=cast(Dict[Any, Any], params), verb="post" - ).perform() - return cast(Domain, resp) + ).perform_with_content() + return resp @classmethod def update(cls, params: UpdateParams) -> Domain: @@ -79,10 +79,10 @@ def update(cls, params: UpdateParams) -> Domain: Domain: The updated domain object """ path = f"/domains/{params['id']}" - resp = request.Request( + resp = request.Request[Domain]( path=path, params=cast(Dict[Any, Any], params), verb="patch" - ).perform() - return cast(Domain, resp) + ).perform_with_content() + return resp @classmethod def get(cls, domain_id: str) -> Domain: @@ -97,8 +97,10 @@ def get(cls, domain_id: str) -> Domain: Domain: The domain object """ path = f"/domains/{domain_id}" - resp = request.Request(path=path, params={}, verb="get").perform() - return cast(Domain, resp) + resp = request.Request[Domain]( + path=path, params={}, verb="get" + ).perform_with_content() + return resp @classmethod def list(cls) -> ListResponse: @@ -110,8 +112,10 @@ def list(cls) -> ListResponse: ListResponse: A list of domain objects """ path = "/domains" - resp = request.Request(path=path, params={}, verb="get").perform() - return cast(_ListResponse, resp) + resp = request.Request[_ListResponse]( + path=path, params={}, verb="get" + ).perform_with_content() + return resp @classmethod def remove(cls, domain_id: str) -> Domain: @@ -126,8 +130,10 @@ def remove(cls, domain_id: str) -> Domain: Domain: The removed domain object """ path = f"/domains/{domain_id}" - resp = request.Request(path=path, params={}, verb="delete").perform() - return cast(Domain, resp) + resp = request.Request[Domain]( + path=path, params={}, verb="delete" + ).perform_with_content() + return resp @classmethod def verify(cls, domain_id: str) -> Domain: @@ -142,5 +148,7 @@ def verify(cls, domain_id: str) -> Domain: Domain: The verified domain object """ path = f"/domains/{domain_id}/verify" - resp = request.Request(path=path, params={}, verb="post").perform() - return cast(Domain, resp) + resp = request.Request[Domain]( + path=path, params={}, verb="post" + ).perform_with_content() + return resp diff --git a/resend/emails/_batch.py b/resend/emails/_batch.py index b238fe1..0c8c194 100644 --- a/resend/emails/_batch.py +++ b/resend/emails/_batch.py @@ -39,9 +39,7 @@ def send(cls, params: List[Emails.SendParams]) -> SendResponse: """ path = "/emails/batch" - return cast( - _SendResponse, - request.Request( - path=path, params=cast(List[Dict[Any, Any]], params), verb="post" - ).perform(), - ) + resp = request.Request[_SendResponse]( + path=path, params=cast(List[Dict[Any, Any]], params), verb="post" + ).perform_with_content() + return resp diff --git a/resend/emails/_emails.py b/resend/emails/_emails.py index 5baf2b0..9e49fb1 100644 --- a/resend/emails/_emails.py +++ b/resend/emails/_emails.py @@ -92,13 +92,12 @@ def send(cls, params: SendParams) -> Email: Email: The email object that was sent """ path = "/emails" - - return cast( - Email, - request.Request( - path=path, params=cast(Dict[Any, Any], params), verb="post" - ).perform(), - ) + resp = request.Request[Email]( + path=path, + params=cast(Dict[Any, Any], params), + verb="post", + ).perform_with_content() + return resp @classmethod def get(cls, email_id: str) -> Email: @@ -113,4 +112,9 @@ def get(cls, email_id: str) -> Email: Email: The email object that was retrieved """ path = f"/emails/{email_id}" - return cast(Email, request.Request(path=path, params={}, verb="get").perform()) + resp = request.Request[Email]( + path=path, + params={}, + verb="get", + ).perform_with_content() + return resp diff --git a/resend/exceptions.py b/resend/exceptions.py index 00a9284..92dcf7c 100644 --- a/resend/exceptions.py +++ b/resend/exceptions.py @@ -219,3 +219,12 @@ def raise_for_code_and_type( raise ResendError( code=code, message=message, error_type=error_type, suggested_action="" ) + + +class NoContentError(Exception): + """Raised when the response body is empty.""" + + def __init__(self) -> None: + self.message = """No content was returned from the API. + Please contact Resend support.""" + Exception.__init__(self, self.message) diff --git a/resend/request.py b/resend/request.py index 290392f..5ae029a 100644 --- a/resend/request.py +++ b/resend/request.py @@ -1,17 +1,19 @@ -from typing import Any, Dict, List, Union +from typing import Any, Dict, Generic, List, Union, cast import requests -from typing_extensions import Literal +from typing_extensions import Literal, TypeVar import resend -from resend.exceptions import raise_for_code_and_type +from resend.exceptions import NoContentError, raise_for_code_and_type from resend.version import get_version RequestVerb = Literal["get", "post", "put", "patch", "delete"] +T = TypeVar("T") + # This class wraps the HTTP request creation logic -class Request: +class Request(Generic[T]): def __init__( self, path: str, @@ -22,13 +24,13 @@ def __init__( self.params = params self.verb = verb - def perform(self) -> Any: + def perform(self) -> Union[T, None]: """Is the main function that makes the HTTP request to the Resend API. It uses the path, params, and verb attributes to make the request. Returns: - Dict: The JSON response from the API + Union[T, None]: A generic type of the Request class or None Raises: requests.HTTPError: If the request fails @@ -47,7 +49,22 @@ def perform(self) -> Any: message=error.get("message"), error_type=error.get("name"), ) - return resp.json() + return cast(T, resp.json()) + + def perform_with_content(self) -> T: + """ + Perform an HTTP request and return the response content. + + Returns: + T: The content of the response + + Raises: + NoContentError: If the response content is `None`. + """ + resp = self.perform() + if resp is None: + raise NoContentError() + return resp def __get_headers(self) -> Dict[Any, Any]: """get_headers returns the HTTP headers that will be diff --git a/tests/api_keys_test.py b/tests/api_keys_test.py index 92a35bb..f730d54 100644 --- a/tests/api_keys_test.py +++ b/tests/api_keys_test.py @@ -1,4 +1,5 @@ import resend +from resend.exceptions import NoContentError from tests.conftest import ResendBaseTest # flake8: noqa @@ -19,6 +20,14 @@ def test_api_keys_create(self) -> None: key: resend.ApiKey = resend.ApiKeys.create(params) assert key["id"] == "dacf4072-4119-4d88-932f-6202748ac7c8" + def test_should_create_api_key_raise_exception_when_no_content(self) -> None: + self.set_mock_json(None) + params: resend.ApiKeys.CreateParams = { + "name": "prod", + } + with self.assertRaises(NoContentError): + _ = resend.ApiKeys.create(params) + def test_api_keys_list(self) -> None: self.set_mock_json( { @@ -38,6 +47,11 @@ def test_api_keys_list(self) -> None: assert key["name"] == "Production" assert key["created_at"] == "2023-04-08T00:11:13.110779+00:00" + def test_should_list_api_key_raise_exception_when_no_content(self) -> None: + self.set_mock_json(None) + with self.assertRaises(NoContentError): + _ = resend.ApiKeys.list() + def test_api_keys_remove(self) -> None: self.set_mock_text("") diff --git a/tests/audiences_test.py b/tests/audiences_test.py index da3c52a..b85ef8f 100644 --- a/tests/audiences_test.py +++ b/tests/audiences_test.py @@ -1,4 +1,5 @@ import resend +from resend.exceptions import NoContentError from tests.conftest import ResendBaseTest # flake8: noqa @@ -21,6 +22,14 @@ def test_audiences_create(self) -> None: assert audience["id"] == "78261eea-8f8b-4381-83c6-79fa7120f1cf" assert audience["name"] == "Registered Users" + def test_should_create_audiences_raise_exception_when_no_content(self) -> None: + self.set_mock_json(None) + params: resend.Audiences.CreateParams = { + "name": "Python SDK Audience", + } + with self.assertRaises(NoContentError): + _ = resend.Audiences.create(params) + def test_audiences_get(self) -> None: self.set_mock_json( { @@ -36,6 +45,11 @@ def test_audiences_get(self) -> None: assert audience["name"] == "Registered Users" assert audience["created_at"] == "2023-10-06T22:59:55.977Z" + def test_should_get_audiences_raise_exception_when_no_content(self) -> None: + self.set_mock_json(None) + with self.assertRaises(NoContentError): + _ = resend.Audiences.get(id="78261eea-8f8b-4381-83c6-79fa7120f1cf") + def test_audiences_remove(self) -> None: self.set_mock_json( { @@ -49,6 +63,11 @@ def test_audiences_remove(self) -> None: assert rmed["id"] == "78261eea-8f8b-4381-83c6-79fa7120f1cf" assert rmed["deleted"] is True + def test_should_remove_audiences_raise_exception_when_no_content(self) -> None: + self.set_mock_json(None) + with self.assertRaises(NoContentError): + _ = resend.Audiences.remove(id="78261eea-8f8b-4381-83c6-79fa7120f1cf") + def test_audiences_list(self) -> None: self.set_mock_json( { @@ -66,3 +85,8 @@ def test_audiences_list(self) -> None: audiences: resend.Audiences.ListResponse = resend.Audiences.list() assert audiences["data"][0]["id"] == "78261eea-8f8b-4381-83c6-79fa7120f1cf" assert audiences["data"][0]["name"] == "Registered Users" + + def test_should_list_audiences_raise_exception_when_no_content(self) -> None: + self.set_mock_json(None) + with self.assertRaises(NoContentError): + _ = resend.Audiences.list() diff --git a/tests/batch_emails_test.py b/tests/batch_emails_test.py index 3ed4ca7..006a2a9 100644 --- a/tests/batch_emails_test.py +++ b/tests/batch_emails_test.py @@ -1,6 +1,7 @@ from typing import List import resend +from resend.exceptions import NoContentError from tests.conftest import ResendBaseTest # flake8: noqa @@ -36,3 +37,22 @@ def test_batch_email_send(self) -> None: assert len(emails["data"]) == 2 assert emails["data"][0]["id"] == "ae2014de-c168-4c61-8267-70d2662a1ce1" assert emails["data"][1]["id"] == "faccb7a5-8a28-4e9a-ac64-8da1cc3bc1cb" + + def test_should_send_batch_email_raise_exception_when_no_content(self) -> None: + self.set_mock_json(None) + params: List[resend.Emails.SendParams] = [ + { + "from": "from@resend.dev", + "to": ["to@resend.dev"], + "subject": "hey", + "html": "hello, world!", + }, + { + "from": "from@resend.dev", + "to": ["to@resend.dev"], + "subject": "hello", + "html": "hello, world!", + }, + ] + with self.assertRaises(NoContentError): + _ = resend.Batch.send(params) diff --git a/tests/conftest.py b/tests/conftest.py index 0918cbd..53c81a1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any from unittest import TestCase from unittest.mock import MagicMock, patch @@ -21,7 +21,7 @@ def setUp(self) -> None: def tearDown(self) -> None: self.patcher.stop() - def set_mock_json(self, mock_json: Dict[Any, Any]) -> None: + def set_mock_json(self, mock_json: Any) -> None: """Auxiliary function to set the mock json return value""" self.m.json = lambda: mock_json diff --git a/tests/contacts_test.py b/tests/contacts_test.py index f1e73bc..2100023 100644 --- a/tests/contacts_test.py +++ b/tests/contacts_test.py @@ -1,4 +1,5 @@ import resend +from resend.exceptions import NoContentError from tests.conftest import ResendBaseTest # flake8: noqa @@ -20,6 +21,18 @@ def test_contacts_create(self) -> None: contact: resend.Contact = resend.Contacts.create(params) assert contact["id"] == "479e3145-dd38-476b-932c-529ceb705947" + def test_should_create_contacts_raise_exception_when_no_content(self) -> None: + self.set_mock_json(None) + params: resend.Contacts.CreateParams = { + "audience_id": "48c269ed-9873-4d60-bdd9-cd7e6fc0b9b8", + "email": "steve.wozniak@gmail.com", + "first_name": "Steve", + "last_name": "Wozniak", + "unsubscribed": True, + } + with self.assertRaises(NoContentError): + _ = resend.Contacts.create(params) + def test_contacts_update(self) -> None: self.set_mock_json( { @@ -37,6 +50,17 @@ def test_contacts_update(self) -> None: contact = resend.Contacts.update(params) assert contact["id"] == "479e3145-dd38-476b-932c-529ceb705947" + def test_should_update_contacts_raise_exception_when_no_content(self) -> None: + self.set_mock_json(None) + params: resend.Contacts.UpdateParams = { + "audience_id": "48c269ed-9873-4d60-bdd9-cd7e6fc0b9b8", + "id": "479e3145-dd38-476b-932c-529ceb705947", + "first_name": "Updated", + "unsubscribed": True, + } + with self.assertRaises(NoContentError): + _ = resend.Contacts.update(params) + def test_contacts_get(self) -> None: self.set_mock_json( { @@ -61,6 +85,14 @@ def test_contacts_get(self) -> None: assert contact["created_at"] == "2023-10-06T23:47:56.678Z" assert contact["unsubscribed"] is False + def test_should_get_contacts_raise_exception_when_no_content(self) -> None: + self.set_mock_json(None) + with self.assertRaises(NoContentError): + _ = resend.Contacts.get( + id="e169aa45-1ecf-4183-9955-b1499d5701d3", + audience_id="48c269ed-9873-4d60-bdd9-cd7e6fc0b9b8", + ) + def test_contacts_remove_by_id(self) -> None: self.set_mock_json( { @@ -77,6 +109,14 @@ def test_contacts_remove_by_id(self) -> None: assert rmed["id"] == "520784e2-887d-4c25-b53c-4ad46ad38100" assert rmed["deleted"] is True + def test_should_remove_contacts_by_id_raise_exception_when_no_content(self) -> None: + self.set_mock_json(None) + with self.assertRaises(NoContentError): + _ = resend.Contacts.remove( + audience_id="48c269ed-9873-4d60-bdd9-cd7e6fc0b9b8", + id="78261eea-8f8b-4381-83c6-79fa7120f1cf", + ) + def test_contacts_remove_by_email(self) -> None: self.set_mock_json( { @@ -93,6 +133,16 @@ def test_contacts_remove_by_email(self) -> None: assert rmed["id"] == "520784e2-887d-4c25-b53c-4ad46ad38100" assert rmed["deleted"] is True + def test_should_remove_contacts_by_email_raise_exception_when_no_content( + self, + ) -> None: + self.set_mock_json(None) + with self.assertRaises(NoContentError): + _ = resend.Contacts.remove( + audience_id="48c269ed-9873-4d60-bdd9-cd7e6fc0b9b8", + email="someemail@email.com", + ) + def test_contacts_remove_raises(self) -> None: resend.api_key = "re_123" @@ -129,3 +179,8 @@ def test_contacts_list(self) -> None: assert contacts["data"][0]["last_name"] == "Wozniak" assert contacts["data"][0]["created_at"] == "2023-10-06T23:47:56.678Z" assert contacts["data"][0]["unsubscribed"] is False + + def test_should_list_contacts_raise_exception_when_no_content(self) -> None: + self.set_mock_json(None) + with self.assertRaises(NoContentError): + _ = resend.Contacts.list(audience_id="48c269ed-9873-4d60-bdd9-cd7e6fc0b9b8") diff --git a/tests/domains_test.py b/tests/domains_test.py index 36803a6..814b887 100644 --- a/tests/domains_test.py +++ b/tests/domains_test.py @@ -1,4 +1,5 @@ import resend +from resend.exceptions import NoContentError from tests.conftest import ResendBaseTest # flake8: noqa @@ -69,6 +70,14 @@ def test_domains_create(self) -> None: assert domain["created_at"] == "2023-03-28T17:12:02.059593+00:00" assert domain["region"] == "us-east-1" + def test_should_create_domains_raise_exception_when_no_content(self) -> None: + self.set_mock_json(None) + create_params: resend.Domains.CreateParams = { + "name": "example.com", + } + with self.assertRaises(NoContentError): + _ = resend.Domains.create(params=create_params) + def test_domains_get(self) -> None: self.set_mock_json( { @@ -90,6 +99,13 @@ def test_domains_get(self) -> None: assert domain["created_at"] == "2023-04-26T20:21:26.347412+00:00" assert domain["region"] == "us-east-1" + def test_should_get_domains_raise_exception_when_no_content(self) -> None: + self.set_mock_json(None) + with self.assertRaises(NoContentError): + _ = resend.Domains.get( + domain_id="d91cd9bd-1176-453e-8fc1-35364d380206", + ) + def test_domains_list(self) -> None: self.set_mock_json( { @@ -112,6 +128,11 @@ def test_domains_list(self) -> None: assert domains["data"][0]["created_at"] == "2023-04-26T20:21:26.347412+00:00" assert domains["data"][0]["region"] == "us-east-1" + def test_should_list_domains_raise_exception_when_no_content(self) -> None: + self.set_mock_json(None) + with self.assertRaises(NoContentError): + _ = resend.Domains.list() + def test_domains_remove(self) -> None: self.set_mock_json( { @@ -127,6 +148,13 @@ def test_domains_remove(self) -> None: assert domain["deleted"] is True assert domain["id"] == "4ef9a417-02e9-4d39-ad75-9611e0fcc33c" + def test_should_remove_domains_raise_exception_when_no_content(self) -> None: + self.set_mock_json(None) + with self.assertRaises(NoContentError): + _ = resend.Domains.remove( + domain_id="4ef9a417-02e9-4d39-ad75-9611e0fcc33c", + ) + def test_domains_verify(self) -> None: self.set_mock_json( {"object": "domain", "id": "d91cd9bd-1176-453e-8fc1-35364d380206"} @@ -137,6 +165,13 @@ def test_domains_verify(self) -> None: ) assert domain["id"] == "d91cd9bd-1176-453e-8fc1-35364d380206" + def test_should_verify_domains_raise_exception_when_no_content(self) -> None: + self.set_mock_json(None) + with self.assertRaises(NoContentError): + _ = resend.Domains.verify( + domain_id="d91cd9bd-1176-453e-8fc1-35364d380206", + ) + def test_domains_update(self) -> None: self.set_mock_json( { @@ -152,3 +187,13 @@ def test_domains_update(self) -> None: } domain = resend.Domains.update(params) assert domain["id"] == "479e3145-dd38-476b-932c-529ceb705947" + + def test_should_update_domains_raise_exception_when_no_content(self) -> None: + self.set_mock_json(None) + params: resend.Domains.UpdateParams = { + "id": "479e3145-dd38-476b-932c-529ceb705947", + "open_tracking": True, + "click_tracking": True, + } + with self.assertRaises(NoContentError): + _ = resend.Domains.update(params) diff --git a/tests/emails_test.py b/tests/emails_test.py index e8dedff..6e64dd6 100644 --- a/tests/emails_test.py +++ b/tests/emails_test.py @@ -1,4 +1,5 @@ import resend +from resend.exceptions import NoContentError from tests.conftest import ResendBaseTest # flake8: noqa @@ -12,7 +13,6 @@ def test_email_send_with_from(self) -> None: "id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794", } ) - params: resend.Emails.SendParams = { "to": "to@email.com", "from": "from@email.com", @@ -22,6 +22,17 @@ def test_email_send_with_from(self) -> None: email: resend.Email = resend.Emails.send(params) assert email["id"] == "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794" + def test_should_send_email_raise_exception_when_no_content(self) -> None: + self.set_mock_json(None) + params: resend.Emails.SendParams = { + "to": "to@email.com", + "from": "from@email.com", + "subject": "subject", + "html": "html", + } + with self.assertRaises(NoContentError): + _ = resend.Emails.send(params) + def test_email_get(self) -> None: self.set_mock_json( { @@ -44,3 +55,10 @@ def test_email_get(self) -> None: email_id="4ef9a417-02e9-4d39-ad75-9611e0fcc33c", ) assert email["id"] == "4ef9a417-02e9-4d39-ad75-9611e0fcc33c" + + def test_should_get_email_raise_exception_when_no_content(self) -> None: + self.set_mock_json(None) + with self.assertRaises(NoContentError): + _ = resend.Emails.get( + email_id="4ef9a417-02e9-4d39-ad75-9611e0fcc33c", + )