diff --git a/src/main/java/com/resend/services/apikeys/ApiKeys.java b/src/main/java/com/resend/services/apikeys/ApiKeys.java index e705d58..8ff53b3 100644 --- a/src/main/java/com/resend/services/apikeys/ApiKeys.java +++ b/src/main/java/com/resend/services/apikeys/ApiKeys.java @@ -4,6 +4,7 @@ import com.resend.core.helper.URLHelper; import com.resend.core.net.AbstractHttpResponse; import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.core.service.BaseService; import com.resend.services.apikeys.model.CreateApiKeyResponse; @@ -25,6 +26,10 @@ public ApiKeys(final String apiKey) { super(apiKey); } + ApiKeys(final String apiKey, final IHttpClient httpClient) { + super(apiKey, httpClient); + } + /** * Creates an API key. * diff --git a/src/main/java/com/resend/services/audiences/Audiences.java b/src/main/java/com/resend/services/audiences/Audiences.java index 958de23..65922bc 100644 --- a/src/main/java/com/resend/services/audiences/Audiences.java +++ b/src/main/java/com/resend/services/audiences/Audiences.java @@ -4,6 +4,7 @@ import com.resend.core.helper.URLHelper; import com.resend.core.net.AbstractHttpResponse; import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.core.service.BaseService; import com.resend.services.audiences.model.*; @@ -23,6 +24,10 @@ public Audiences(final String apiKey) { super(apiKey); } + Audiences(final String apiKey, final IHttpClient httpClient) { + super(apiKey, httpClient); + } + /** * Creates an Audience. * diff --git a/src/main/java/com/resend/services/automations/Automations.java b/src/main/java/com/resend/services/automations/Automations.java index 60e1731..bcbca12 100644 --- a/src/main/java/com/resend/services/automations/Automations.java +++ b/src/main/java/com/resend/services/automations/Automations.java @@ -3,6 +3,7 @@ import com.resend.core.exception.ResendException; import com.resend.core.net.AbstractHttpResponse; import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.service.BaseService; import com.resend.services.automations.model.*; import okhttp3.MediaType; @@ -22,6 +23,10 @@ public Automations(final String apiKey) { super(apiKey); } + Automations(final String apiKey, final IHttpClient httpClient) { + super(apiKey, httpClient); + } + /** * Creates a new automation. * diff --git a/src/main/java/com/resend/services/batch/Batch.java b/src/main/java/com/resend/services/batch/Batch.java index 5ffe37c..cdcf2df 100644 --- a/src/main/java/com/resend/services/batch/Batch.java +++ b/src/main/java/com/resend/services/batch/Batch.java @@ -3,6 +3,7 @@ import com.resend.core.exception.ResendException; import com.resend.core.net.AbstractHttpResponse; import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.RequestOptions; import com.resend.core.service.BaseService; @@ -25,6 +26,10 @@ public Batch(final String apiKey) { super(apiKey); } + Batch(final String apiKey, final IHttpClient httpClient) { + super(apiKey, httpClient); + } + /** * Sends up to 100 batch emails. * diff --git a/src/main/java/com/resend/services/broadcasts/Broadcasts.java b/src/main/java/com/resend/services/broadcasts/Broadcasts.java index 6b28ab0..7723067 100644 --- a/src/main/java/com/resend/services/broadcasts/Broadcasts.java +++ b/src/main/java/com/resend/services/broadcasts/Broadcasts.java @@ -4,6 +4,7 @@ import com.resend.core.helper.URLHelper; import com.resend.core.net.AbstractHttpResponse; import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.core.service.BaseService; import com.resend.services.broadcasts.model.*; @@ -21,7 +22,10 @@ public class Broadcasts extends BaseService { */ public Broadcasts(final String apiKey) { super(apiKey); + } + Broadcasts(final String apiKey, final IHttpClient httpClient) { + super(apiKey, httpClient); } /** diff --git a/src/main/java/com/resend/services/contactproperties/ContactProperties.java b/src/main/java/com/resend/services/contactproperties/ContactProperties.java index 1a6d43c..5fc2598 100644 --- a/src/main/java/com/resend/services/contactproperties/ContactProperties.java +++ b/src/main/java/com/resend/services/contactproperties/ContactProperties.java @@ -3,6 +3,7 @@ import com.resend.core.exception.ResendException; import com.resend.core.net.AbstractHttpResponse; import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.service.BaseService; import com.resend.services.contactproperties.model.*; import okhttp3.MediaType; @@ -21,6 +22,10 @@ public ContactProperties(final String apiKey) { super(apiKey); } + ContactProperties(final String apiKey, final IHttpClient httpClient) { + super(apiKey, httpClient); + } + /** * Creates a ContactProperty. * diff --git a/src/main/java/com/resend/services/contacts/ContactImports.java b/src/main/java/com/resend/services/contacts/ContactImports.java index ccfc483..2f88b72 100644 --- a/src/main/java/com/resend/services/contacts/ContactImports.java +++ b/src/main/java/com/resend/services/contacts/ContactImports.java @@ -4,6 +4,7 @@ import com.resend.core.helper.URLHelper; import com.resend.core.net.AbstractHttpResponse; import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.core.net.RequestOptions; import com.resend.core.service.BaseService; @@ -40,6 +41,10 @@ public ContactImports(final String apiKey) { super(apiKey); } + ContactImports(final String apiKey, final IHttpClient httpClient) { + super(apiKey, httpClient); + } + /** * Creates a new contact import from a CSV file. * diff --git a/src/main/java/com/resend/services/contacts/ContactSegments.java b/src/main/java/com/resend/services/contacts/ContactSegments.java index ff73d9c..1a8fb99 100644 --- a/src/main/java/com/resend/services/contacts/ContactSegments.java +++ b/src/main/java/com/resend/services/contacts/ContactSegments.java @@ -4,6 +4,7 @@ import com.resend.core.helper.URLHelper; import com.resend.core.net.AbstractHttpResponse; import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.core.service.BaseService; import com.resend.services.contacts.model.*; @@ -24,6 +25,10 @@ public ContactSegments(final String apiKey) { super(apiKey); } + ContactSegments(final String apiKey, final IHttpClient httpClient) { + super(apiKey, httpClient); + } + /** * Adds an existing contact to a segment. * diff --git a/src/main/java/com/resend/services/contacts/ContactTopics.java b/src/main/java/com/resend/services/contacts/ContactTopics.java index 6e54c19..4fbdc54 100644 --- a/src/main/java/com/resend/services/contacts/ContactTopics.java +++ b/src/main/java/com/resend/services/contacts/ContactTopics.java @@ -4,6 +4,7 @@ import com.resend.core.helper.URLHelper; import com.resend.core.net.AbstractHttpResponse; import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.core.service.BaseService; import com.resend.services.contacts.model.ListContactTopicsResponse; @@ -26,6 +27,10 @@ public ContactTopics(final String apiKey) { super(apiKey); } + ContactTopics(final String apiKey, final IHttpClient httpClient) { + super(apiKey, httpClient); + } + /** * Retrieves a list of topic subscriptions for a contact. * diff --git a/src/main/java/com/resend/services/contacts/Contacts.java b/src/main/java/com/resend/services/contacts/Contacts.java index 77ab64c..69d38c5 100644 --- a/src/main/java/com/resend/services/contacts/Contacts.java +++ b/src/main/java/com/resend/services/contacts/Contacts.java @@ -4,6 +4,7 @@ import com.resend.core.helper.URLHelper; import com.resend.core.net.AbstractHttpResponse; import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.core.service.BaseService; import com.resend.services.contacts.model.*; @@ -49,6 +50,10 @@ public Contacts(final String apiKey) { super(apiKey); } + Contacts(final String apiKey, final IHttpClient httpClient) { + super(apiKey, httpClient); + } + /** * Returns the ContactSegments sub-service for managing contact segment memberships. * @@ -56,7 +61,7 @@ public Contacts(final String apiKey) { */ public ContactSegments segments() { if (this.contactSegments == null) { - this.contactSegments = new ContactSegments(this.apiKey); + this.contactSegments = new ContactSegments(this.apiKey, this.httpClient); } return this.contactSegments; } @@ -68,7 +73,7 @@ public ContactSegments segments() { */ public ContactTopics topics() { if (this.contactTopics == null) { - this.contactTopics = new ContactTopics(this.apiKey); + this.contactTopics = new ContactTopics(this.apiKey, this.httpClient); } return this.contactTopics; } @@ -83,7 +88,7 @@ public ContactTopics topics() { */ public ContactImports imports() { if (this.contactImports == null) { - this.contactImports = new ContactImports(this.apiKey); + this.contactImports = new ContactImports(this.apiKey, this.httpClient); } return this.contactImports; } diff --git a/src/main/java/com/resend/services/domains/Domains.java b/src/main/java/com/resend/services/domains/Domains.java index cb43638..9b795cc 100644 --- a/src/main/java/com/resend/services/domains/Domains.java +++ b/src/main/java/com/resend/services/domains/Domains.java @@ -4,6 +4,7 @@ import com.resend.core.helper.URLHelper; import com.resend.core.net.AbstractHttpResponse; import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.core.service.BaseService; import com.resend.services.domains.model.*; @@ -23,6 +24,10 @@ public Domains(final String apiKey) { super(apiKey); } + Domains(final String apiKey, final IHttpClient httpClient) { + super(apiKey, httpClient); + } + /** * Creates a domain based on the provided CreateDomainOptions and returns a CreateDomainResponse. * @@ -142,7 +147,7 @@ public UpdateDomainResponseSuccess update(UpdateDomainOptions updateDomainOption * @return A DomainClaims object. */ public DomainClaims claims() { - return new DomainClaims(apiKey); + return new DomainClaims(apiKey, this.httpClient); } /** diff --git a/src/main/java/com/resend/services/emails/Emails.java b/src/main/java/com/resend/services/emails/Emails.java index 00139f8..05b861d 100644 --- a/src/main/java/com/resend/services/emails/Emails.java +++ b/src/main/java/com/resend/services/emails/Emails.java @@ -4,6 +4,7 @@ import com.resend.core.helper.URLHelper; import com.resend.core.net.AbstractHttpResponse; import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.core.net.RequestOptions; import com.resend.core.service.BaseService; @@ -27,6 +28,10 @@ public Emails(final String apiKey) { super(apiKey); } + Emails(final String apiKey, final IHttpClient httpClient) { + super(apiKey, httpClient); + } + /** * Sends an email based on the provided email request. * diff --git a/src/main/java/com/resend/services/events/Events.java b/src/main/java/com/resend/services/events/Events.java index 014f95e..6ffcfd0 100644 --- a/src/main/java/com/resend/services/events/Events.java +++ b/src/main/java/com/resend/services/events/Events.java @@ -4,6 +4,7 @@ import com.resend.core.helper.URLHelper; import com.resend.core.net.AbstractHttpResponse; import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.core.service.BaseService; import com.resend.services.events.model.*; @@ -23,6 +24,10 @@ public Events(final String apiKey) { super(apiKey); } + Events(final String apiKey, final IHttpClient httpClient) { + super(apiKey, httpClient); + } + /** * Creates a new event. * diff --git a/src/main/java/com/resend/services/logs/Logs.java b/src/main/java/com/resend/services/logs/Logs.java index a87def9..648abb2 100644 --- a/src/main/java/com/resend/services/logs/Logs.java +++ b/src/main/java/com/resend/services/logs/Logs.java @@ -4,6 +4,7 @@ import com.resend.core.helper.URLHelper; import com.resend.core.net.AbstractHttpResponse; import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.core.service.BaseService; import com.resend.services.logs.model.GetLogResponseSuccess; @@ -24,6 +25,10 @@ public Logs(final String apiKey) { super(apiKey); } + Logs(final String apiKey, final IHttpClient httpClient) { + super(apiKey, httpClient); + } + /** * Retrieves a single log entry by its unique identifier. * diff --git a/src/main/java/com/resend/services/receiving/Receiving.java b/src/main/java/com/resend/services/receiving/Receiving.java index bb55fee..a9c63cb 100644 --- a/src/main/java/com/resend/services/receiving/Receiving.java +++ b/src/main/java/com/resend/services/receiving/Receiving.java @@ -4,6 +4,7 @@ import com.resend.core.helper.URLHelper; import com.resend.core.net.AbstractHttpResponse; import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.core.service.BaseService; import com.resend.services.receiving.model.*; @@ -23,6 +24,10 @@ public Receiving(final String apiKey) { super(apiKey); } + Receiving(final String apiKey, final IHttpClient httpClient) { + super(apiKey, httpClient); + } + /** * Retrieves a single received email by its ID. * diff --git a/src/main/java/com/resend/services/segments/Segments.java b/src/main/java/com/resend/services/segments/Segments.java index 5b0dd96..a83dedf 100644 --- a/src/main/java/com/resend/services/segments/Segments.java +++ b/src/main/java/com/resend/services/segments/Segments.java @@ -4,6 +4,7 @@ import com.resend.core.helper.URLHelper; import com.resend.core.net.AbstractHttpResponse; import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.core.service.BaseService; import com.resend.services.segments.model.*; @@ -23,6 +24,10 @@ public Segments(final String apiKey) { super(apiKey); } + Segments(final String apiKey, final IHttpClient httpClient) { + super(apiKey, httpClient); + } + /** * Creates a Segment. * diff --git a/src/main/java/com/resend/services/templates/Templates.java b/src/main/java/com/resend/services/templates/Templates.java index 14a3c79..af19fdd 100644 --- a/src/main/java/com/resend/services/templates/Templates.java +++ b/src/main/java/com/resend/services/templates/Templates.java @@ -4,6 +4,7 @@ import com.resend.core.helper.URLHelper; import com.resend.core.net.AbstractHttpResponse; import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.core.service.BaseService; import com.resend.services.templates.model.*; @@ -23,6 +24,10 @@ public Templates(final String apiKey) { super(apiKey); } + Templates(final String apiKey, final IHttpClient httpClient) { + super(apiKey, httpClient); + } + /** * Creates a new template. * diff --git a/src/main/java/com/resend/services/topics/Topics.java b/src/main/java/com/resend/services/topics/Topics.java index 97d5a7b..1bea4d9 100644 --- a/src/main/java/com/resend/services/topics/Topics.java +++ b/src/main/java/com/resend/services/topics/Topics.java @@ -4,6 +4,7 @@ import com.resend.core.helper.URLHelper; import com.resend.core.net.AbstractHttpResponse; import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.core.service.BaseService; import com.resend.services.topics.model.*; @@ -23,6 +24,10 @@ public Topics(final String apiKey) { super(apiKey); } + Topics(final String apiKey, final IHttpClient httpClient) { + super(apiKey, httpClient); + } + /** * Creates a new topic. * diff --git a/src/main/java/com/resend/services/webhooks/Webhooks.java b/src/main/java/com/resend/services/webhooks/Webhooks.java index 08b5612..6cfc071 100644 --- a/src/main/java/com/resend/services/webhooks/Webhooks.java +++ b/src/main/java/com/resend/services/webhooks/Webhooks.java @@ -4,6 +4,7 @@ import com.resend.core.helper.URLHelper; import com.resend.core.net.AbstractHttpResponse; import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.core.service.BaseService; import com.resend.services.webhooks.model.CreateWebhookOptions; @@ -34,6 +35,10 @@ public Webhooks(final String apiKey) { super(apiKey); } + Webhooks(final String apiKey, final IHttpClient httpClient) { + super(apiKey, httpClient); + } + /** * Creates a webhook based on the provided CreateWebhookOptions and returns a CreateWebhookResponseSuccess. * diff --git a/src/test/java/com/resend/services/apikey/ApiKeysTest.java b/src/test/java/com/resend/services/apikey/ApiKeysTest.java deleted file mode 100644 index b9636e2..0000000 --- a/src/test/java/com/resend/services/apikey/ApiKeysTest.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.resend.services.apikey; - -import com.resend.core.exception.ResendException; -import com.resend.core.net.ListParams; -import com.resend.services.apikeys.ApiKeys; -import com.resend.services.apikeys.model.CreateApiKeyOptions; -import com.resend.services.apikeys.model.CreateApiKeyResponse; -import com.resend.services.apikeys.model.ListApiKeysResponse; -import com.resend.services.util.ApiKeysUtil; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.Mockito.*; -import static org.mockito.Mockito.times; - -public class ApiKeysTest { - - @Mock - private ApiKeys apiKeys; - - @BeforeEach - public void setUp() { - MockitoAnnotations.openMocks(this); - apiKeys = mock(ApiKeys.class); - } - - @Test - public void testCreateApiKey_Success() throws ResendException { - CreateApiKeyResponse expectedApiKey = ApiKeysUtil.createApiKeyResponse(); - CreateApiKeyOptions param = ApiKeysUtil.createApiKeyRequest(); - - when(apiKeys.create(param)) - .thenReturn(expectedApiKey); - - CreateApiKeyResponse retrievedApiKey = apiKeys.create(param); - - - assertEquals(retrievedApiKey, expectedApiKey); - verify(apiKeys, times(1)).create(param); - } - - @Test - public void testDeleteApiKey_Success() throws ResendException { - String apiKeyId = "123"; - when(apiKeys.remove(apiKeyId)) - .thenReturn(true); - - boolean response = apiKeys.remove(apiKeyId); - - assertEquals(true, response); - } - - @Test - public void testListApiKeys_Success() throws ResendException { - ListApiKeysResponse expectedResponse = ApiKeysUtil.createListApiKeyResponse(); - - when(apiKeys.list()) - .thenReturn(expectedResponse); - - ListApiKeysResponse response = apiKeys.list(); - - assertNotNull(response); - assertEquals(expectedResponse.getData().size(), response.getData().size()); - } - - @Test - public void testListApiKeysWithPagination_Success() throws ResendException { - ListParams params = ListParams.builder() - .limit(2).build(); - ListApiKeysResponse expectedResponse = ApiKeysUtil.createListApiKeyResponse(); - - when(apiKeys.list(params)) - .thenReturn(expectedResponse); - - ListApiKeysResponse response = apiKeys.list(params); - - assertNotNull(response); - assertEquals(params.getLimit(), response.getData().size()); - } -} diff --git a/src/test/java/com/resend/services/apikeys/ApiKeysTest.java b/src/test/java/com/resend/services/apikeys/ApiKeysTest.java new file mode 100644 index 0000000..4b9f58e --- /dev/null +++ b/src/test/java/com/resend/services/apikeys/ApiKeysTest.java @@ -0,0 +1,134 @@ +package com.resend.services.apikeys; + +import com.resend.core.exception.ResendException; +import com.resend.core.net.AbstractHttpResponse; +import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; +import com.resend.services.apikeys.model.CreateApiKeyOptions; +import com.resend.services.apikeys.model.CreateApiKeyResponse; +import com.resend.services.apikeys.model.ListApiKeysResponse; +import com.resend.services.util.ApiKeysUtil; +import okhttp3.MediaType; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.when; + +@SuppressWarnings("unchecked") +public class ApiKeysTest { + + private static final String CREATE_RESPONSE_JSON = "{\"id\":\"123\",\"token\":\"re_123\"}"; + + private static final String LIST_RESPONSE_JSON = "{\"data\":[" + + "{\"id\":\"abcdefg-4321-5678-ijklmnop\",\"name\":\"Production\"," + + "\"created_at\":\"2023-04-08T00:11:13.110779+00:00\",\"last_used_at\":\"2023-04-26T20:21:26.844Z\"}," + + "{\"id\":\"abcdefg-1234-5678-ijklmnop\",\"name\":\"Staging\"," + + "\"created_at\":\"2023-04-08T00:11:13.110779+00:00\"}" + + "],\"has_more\":true,\"object\":\"list\"}"; + + @Mock + private IHttpClient httpClient; + + private ApiKeys apiKeys; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + apiKeys = new ApiKeys("test-api-key", httpClient); + } + + @Test + public void testCreateApiKey_Success() throws ResendException { + CreateApiKeyOptions request = ApiKeysUtil.createApiKeyRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, CREATE_RESPONSE_JSON, true); + + when(httpClient.perform(eq("/api-keys"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); + + CreateApiKeyResponse response = apiKeys.create(request); + + assertNotNull(response); + assertEquals("123", response.getId()); + assertEquals("re_123", response.getToken()); + } + + @Test + public void testCreateApiKey_ApiError_ThrowsResendException() throws ResendException { + CreateApiKeyOptions request = ApiKeysUtil.createApiKeyRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(422, + "{\"name\":\"validation_error\",\"message\":\"Invalid request\"}", false); + + when(httpClient.perform(eq("/api-keys"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); + + ResendException ex = assertThrows(ResendException.class, () -> apiKeys.create(request)); + assertEquals(422, (int) ex.getStatusCode()); + } + + @Test + public void testDeleteApiKey_Success() throws ResendException { + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, "", true); + + when(httpClient.perform(eq("/api-keys/123"), anyString(), eq(HttpMethod.DELETE), eq(""), isNull())) + .thenReturn(httpResponse); + + boolean result = apiKeys.remove("123"); + + assertTrue(result); + } + + @Test + public void testDeleteApiKey_ApiError_ThrowsResendException() throws ResendException { + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(404, + "{\"name\":\"not_found\",\"message\":\"API key not found\"}", false); + + when(httpClient.perform(eq("/api-keys/123"), anyString(), eq(HttpMethod.DELETE), eq(""), isNull())) + .thenReturn(httpResponse); + + assertThrows(ResendException.class, () -> apiKeys.remove("123")); + } + + @Test + public void testListApiKeys_Success() throws ResendException { + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_RESPONSE_JSON, true); + + when(httpClient.perform(eq("/api-keys"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); + + ListApiKeysResponse response = apiKeys.list(); + + assertNotNull(response); + assertEquals(2, response.getData().size()); + assertEquals("abcdefg-4321-5678-ijklmnop", response.getData().get(0).getId()); + assertEquals("list", response.getObject()); + } + + @Test + public void testListApiKeys_ApiError_ThrowsResendException() throws ResendException { + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(401, + "{\"name\":\"unauthorized\",\"message\":\"Unauthorized\"}", false); + + when(httpClient.perform(eq("/api-keys"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); + + assertThrows(ResendException.class, () -> apiKeys.list()); + } + + @Test + public void testListApiKeysWithPagination_Success() throws ResendException { + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_RESPONSE_JSON, true); + + when(httpClient.perform(eq("/api-keys?limit=2"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); + + com.resend.core.net.ListParams params = com.resend.core.net.ListParams.builder().limit(2).build(); + ListApiKeysResponse response = apiKeys.list(params); + + assertNotNull(response); + assertEquals(2, response.getData().size()); + } +} diff --git a/src/test/java/com/resend/services/audiences/AudiencesTest.java b/src/test/java/com/resend/services/audiences/AudiencesTest.java index f9ed0db..df4eb9f 100644 --- a/src/test/java/com/resend/services/audiences/AudiencesTest.java +++ b/src/test/java/com/resend/services/audiences/AudiencesTest.java @@ -1,88 +1,170 @@ package com.resend.services.audiences; import com.resend.core.exception.ResendException; -import com.resend.core.net.ListParams; +import com.resend.core.net.AbstractHttpResponse; +import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.services.audiences.model.CreateAudienceOptions; import com.resend.services.audiences.model.CreateAudienceResponseSuccess; +import com.resend.services.audiences.model.GetAudienceResponseSuccess; import com.resend.services.audiences.model.ListAudiencesResponseSuccess; import com.resend.services.audiences.model.RemoveAudienceResponseSuccess; - import com.resend.services.util.AudiencesUtil; +import okhttp3.MediaType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.Mockito.*; -import static org.mockito.Mockito.times; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.when; +@SuppressWarnings("unchecked") public class AudiencesTest { + private static final String CREATE_RESPONSE_JSON = + "{\"id\":\"123\",\"name\":\"aud\",\"object\":\"audience\"}"; + + private static final String LIST_RESPONSE_JSON = "{\"data\":[" + + "{\"id\":\"1\",\"name\":\"test1\",\"created_at\":\"2023-04-08T00:11:13.110779+00:00\"}," + + "{\"id\":\"2\",\"name\":\"test2\",\"created_at\":\"2023-04-08T00:11:13.110779+00:00\"}," + + "{\"id\":\"3\",\"name\":\"test3\",\"created_at\":\"2023-04-08T00:11:13.110779+00:00\"}" + + "],\"object\":\"list\",\"has_more\":true}"; + + private static final String REMOVE_RESPONSE_JSON = + "{\"id\":\"123\",\"object\":\"audience\",\"deleted\":true}"; + + private static final String GET_RESPONSE_JSON = + "{\"id\":\"123\",\"name\":\"aud\",\"created_at\":\"2023-04-08T00:11:13.110779+00:00\",\"object\":\"audience\"}"; + @Mock + private IHttpClient httpClient; + private Audiences audiences; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); - audiences = mock(Audiences.class); + audiences = new Audiences("test-api-key", httpClient); } @Test public void testCreateAudience_Success() throws ResendException { - CreateAudienceResponseSuccess expectedAudience = AudiencesUtil.createAudienceResponse(); - CreateAudienceOptions param = AudiencesUtil.createAudienceRequest(); + CreateAudienceOptions request = AudiencesUtil.createAudienceRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, CREATE_RESPONSE_JSON, true); + + when(httpClient.perform(eq("/audiences"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); + + CreateAudienceResponseSuccess response = audiences.create(request); - when(audiences.create(param)) - .thenReturn(expectedAudience); + assertNotNull(response); + assertEquals("123", response.getId()); + assertEquals("aud", response.getName()); + assertEquals("audience", response.getObject()); + } - CreateAudienceResponseSuccess createdAud = audiences.create(param); + @Test + public void testCreateAudience_ApiError_ThrowsResendException() throws ResendException { + CreateAudienceOptions request = AudiencesUtil.createAudienceRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(422, + "{\"name\":\"validation_error\",\"message\":\"Invalid request\"}", false); + when(httpClient.perform(eq("/audiences"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); - assertEquals(createdAud, expectedAudience); - verify(audiences, times(1)).create(param); + ResendException ex = assertThrows(ResendException.class, () -> audiences.create(request)); + assertEquals(422, (int) ex.getStatusCode()); } @Test public void testDeleteAudience_Success() throws ResendException { - String audienceId = "123"; - RemoveAudienceResponseSuccess removed = AudiencesUtil.removeAudiencesResponseSuccess(); - when(audiences.remove(audienceId)) - .thenReturn(removed); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, REMOVE_RESPONSE_JSON, true); + + when(httpClient.perform(eq("/audiences/123"), anyString(), eq(HttpMethod.DELETE), eq(""), isNull())) + .thenReturn(httpResponse); + + RemoveAudienceResponseSuccess response = audiences.remove("123"); + + assertNotNull(response); + assertEquals("123", response.getId()); + assertTrue(response.getDeleted()); + } + + @Test + public void testDeleteAudience_ApiError_ThrowsResendException() throws ResendException { + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(404, + "{\"name\":\"not_found\",\"message\":\"Audience not found\"}", false); - RemoveAudienceResponseSuccess res = audiences.remove(audienceId); + when(httpClient.perform(eq("/audiences/123"), anyString(), eq(HttpMethod.DELETE), eq(""), isNull())) + .thenReturn(httpResponse); - assertEquals(removed, res); + assertThrows(ResendException.class, () -> audiences.remove("123")); + } + + @Test + public void testGetAudience_Success() throws ResendException { + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, GET_RESPONSE_JSON, true); + + when(httpClient.perform(eq("/audiences/123"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); + + GetAudienceResponseSuccess response = audiences.get("123"); + + assertNotNull(response); + assertEquals("123", response.getId()); + assertEquals("aud", response.getName()); + } + + @Test + public void testGetAudience_ApiError_ThrowsResendException() throws ResendException { + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(404, + "{\"name\":\"not_found\",\"message\":\"Audience not found\"}", false); + + when(httpClient.perform(eq("/audiences/123"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); + + assertThrows(ResendException.class, () -> audiences.get("123")); } @Test public void testListAudiences_Success() throws ResendException { - ListAudiencesResponseSuccess expectedResponse = AudiencesUtil.createAudiencesListResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_RESPONSE_JSON, true); + + when(httpClient.perform(eq("/audiences"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - when(audiences.list()) - .thenReturn(expectedResponse); + ListAudiencesResponseSuccess response = audiences.list(); + + assertNotNull(response); + assertEquals(3, response.getData().size()); + assertEquals("list", response.getObject()); + } + + @Test + public void testListAudiences_ApiError_ThrowsResendException() throws ResendException { + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(401, + "{\"name\":\"unauthorized\",\"message\":\"Unauthorized\"}", false); - ListAudiencesResponseSuccess res = audiences.list(); + when(httpClient.perform(eq("/audiences"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - assertNotNull(res); - assertEquals(expectedResponse.getData().size(), res.getData().size()); - assertEquals(expectedResponse.getObject(), res.getObject()); + assertThrows(ResendException.class, () -> audiences.list()); } @Test - public void testListAudienceWithPagination_Success() throws ResendException { - ListParams params = ListParams.builder() - .limit(3).build(); - ListAudiencesResponseSuccess expectedResponse = AudiencesUtil.createAudiencesListResponse(); + public void testListAudiencesWithPagination_Success() throws ResendException { + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_RESPONSE_JSON, true); - when(audiences.list(params)) - .thenReturn(expectedResponse); + when(httpClient.perform(eq("/audiences?limit=3"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - ListAudiencesResponseSuccess res = audiences.list(params); + com.resend.core.net.ListParams params = com.resend.core.net.ListParams.builder().limit(3).build(); + ListAudiencesResponseSuccess response = audiences.list(params); - assertNotNull(res); - assertEquals(params.getLimit(), res.getData().size()); - assertEquals(expectedResponse.getObject(), res.getObject()); + assertNotNull(response); + assertEquals(3, response.getData().size()); + assertEquals("list", response.getObject()); } } diff --git a/src/test/java/com/resend/services/automations/AutomationsTest.java b/src/test/java/com/resend/services/automations/AutomationsTest.java index 62eaa57..2e278a5 100644 --- a/src/test/java/com/resend/services/automations/AutomationsTest.java +++ b/src/test/java/com/resend/services/automations/AutomationsTest.java @@ -1,67 +1,129 @@ package com.resend.services.automations; import com.resend.core.exception.ResendException; +import com.resend.core.net.AbstractHttpResponse; +import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.services.automations.model.*; import com.resend.services.util.AutomationsUtil; +import okhttp3.MediaType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.when; +@SuppressWarnings("unchecked") public class AutomationsTest { + private static final String AUTOMATION_ID = "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"; + + private static final String CREATE_AUTOMATION_JSON = + "{\"object\":\"automation\",\"id\":\"" + AUTOMATION_ID + "\"}"; + + private static final String GET_AUTOMATION_JSON = + "{\"object\":\"automation\",\"id\":\"" + AUTOMATION_ID + "\"," + + "\"name\":\"Welcome Automation\",\"status\":\"enabled\"," + + "\"created_at\":\"2024-12-01T10:00:00.000Z\",\"updated_at\":\"2024-12-02T10:00:00.000Z\"," + + "\"steps\":[],\"connections\":[]}"; + + private static final String LIST_AUTOMATIONS_JSON = + "{\"object\":\"list\",\"has_more\":false,\"data\":[" + + "{\"id\":\"1\",\"name\":\"Automation 1\",\"status\":\"enabled\",\"created_at\":\"2024-12-01T10:00:00.000Z\",\"updated_at\":\"2024-12-01T10:00:00.000Z\"}," + + "{\"id\":\"2\",\"name\":\"Automation 2\",\"status\":\"disabled\",\"created_at\":\"2024-12-02T10:00:00.000Z\",\"updated_at\":\"2024-12-02T10:00:00.000Z\"}," + + "{\"id\":\"3\",\"name\":\"Automation 3\",\"status\":\"enabled\",\"created_at\":\"2024-12-03T10:00:00.000Z\",\"updated_at\":\"2024-12-03T10:00:00.000Z\"}" + + "]}"; + + private static final String UPDATE_AUTOMATION_JSON = + "{\"object\":\"automation\",\"id\":\"" + AUTOMATION_ID + "\"}"; + + private static final String DELETE_AUTOMATION_JSON = + "{\"object\":\"automation\",\"id\":\"" + AUTOMATION_ID + "\",\"deleted\":true}"; + + private static final String STOP_AUTOMATION_JSON = + "{\"object\":\"automation\",\"id\":\"" + AUTOMATION_ID + "\",\"status\":\"disabled\"}"; + + private static final String LIST_RUNS_JSON = + "{\"object\":\"list\",\"has_more\":false,\"data\":[" + + "{\"id\":\"run_1\",\"status\":\"completed\",\"started_at\":\"2024-12-01T10:00:00.000Z\",\"finished_at\":\"2024-12-01T10:05:00.000Z\",\"created_at\":\"2024-12-01T10:00:00.000Z\"}," + + "{\"id\":\"run_2\",\"status\":\"running\",\"started_at\":\"2024-12-02T10:00:00.000Z\",\"created_at\":\"2024-12-02T10:00:00.000Z\"}," + + "{\"id\":\"run_3\",\"status\":\"failed\",\"started_at\":\"2024-12-03T10:00:00.000Z\",\"finished_at\":\"2024-12-03T10:02:00.000Z\",\"created_at\":\"2024-12-03T10:00:00.000Z\"}" + + "]}"; + + private static final String GET_RUN_JSON = + "{\"object\":\"automation_run\",\"id\":\"run_1\",\"status\":\"completed\"," + + "\"started_at\":\"2024-12-01T10:00:00.000Z\",\"finished_at\":\"2024-12-01T10:00:05.000Z\"," + + "\"created_at\":\"2024-12-01T10:00:00.000Z\",\"steps\":[]}"; + @Mock + private IHttpClient httpClient; + private Automations automations; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); - automations = mock(Automations.class); + automations = new Automations("test-api-key", httpClient); } @Test public void testCreateAutomation_Success() throws ResendException { - CreateAutomationResponseSuccess expectedResponse = AutomationsUtil.createAutomationResponse(); CreateAutomationOptions createOptions = AutomationsUtil.createAutomationRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, CREATE_AUTOMATION_JSON, true); - when(automations.create(createOptions)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/automations"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); CreateAutomationResponseSuccess response = automations.create(createOptions); - assertEquals(expectedResponse, response); - verify(automations, times(1)).create(createOptions); + assertNotNull(response); + assertEquals(AUTOMATION_ID, response.getId()); + assertEquals("automation", response.getObject()); + } + + @Test + public void testCreateAutomation_ApiError_ThrowsResendException() throws ResendException { + CreateAutomationOptions createOptions = AutomationsUtil.createAutomationRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(422, + "{\"name\":\"validation_error\",\"message\":\"Invalid options\"}", false); + + when(httpClient.perform(eq("/automations"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); + + ResendException ex = assertThrows(ResendException.class, () -> automations.create(createOptions)); + assertEquals(422, (int) ex.getStatusCode()); + assertEquals("Invalid options", ex.getMessage()); } @Test public void testGetAutomation_Success() throws ResendException { - String automationId = "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"; - Automation expectedResponse = AutomationsUtil.getAutomationResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, GET_AUTOMATION_JSON, true); - when(automations.get(automationId)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/automations/" + AUTOMATION_ID), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - Automation response = automations.get(automationId); + Automation response = automations.get(AUTOMATION_ID); assertNotNull(response); - assertEquals(expectedResponse.getId(), response.getId()); - assertEquals(expectedResponse.getName(), response.getName()); - verify(automations, times(1)).get(automationId); + assertEquals(AUTOMATION_ID, response.getId()); + assertEquals("Welcome Automation", response.getName()); } @Test public void testListAutomations_Success() throws ResendException { - ListAutomationsResponseSuccess expectedResponse = AutomationsUtil.listAutomationsResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_AUTOMATIONS_JSON, true); - when(automations.list()).thenReturn(expectedResponse); + when(httpClient.perform(eq("/automations"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListAutomationsResponseSuccess response = automations.list(); assertNotNull(response); - assertEquals(expectedResponse.getData().size(), response.getData().size()); - assertEquals(expectedResponse.getObject(), response.getObject()); - verify(automations, times(1)).list(); + assertEquals(3, response.getData().size()); + assertEquals("list", response.getObject()); } @Test @@ -70,127 +132,124 @@ public void testListAutomationsWithParams_Success() throws ResendException { .status(AutomationStatus.ENABLED) .limit(10) .build(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_AUTOMATIONS_JSON, true); - ListAutomationsResponseSuccess expectedResponse = AutomationsUtil.listAutomationsResponse(); - - when(automations.list(params)).thenReturn(expectedResponse); + when(httpClient.perform(startsWith("/automations?"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListAutomationsResponseSuccess response = automations.list(params); assertNotNull(response); - assertEquals(expectedResponse.getObject(), response.getObject()); - verify(automations, times(1)).list(params); + assertEquals("list", response.getObject()); } @Test public void testUpdateAutomation_Success() throws ResendException { - UpdateAutomationResponseSuccess expectedResponse = AutomationsUtil.updateAutomationResponse(); UpdateAutomationOptions updateOptions = AutomationsUtil.updateAutomationRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, UPDATE_AUTOMATION_JSON, true); - when(automations.update(updateOptions)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/automations/" + AUTOMATION_ID), anyString(), eq(HttpMethod.PATCH), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); UpdateAutomationResponseSuccess response = automations.update(updateOptions); - assertEquals(expectedResponse, response); - verify(automations, times(1)).update(updateOptions); + assertNotNull(response); + assertEquals(AUTOMATION_ID, response.getId()); + assertEquals("automation", response.getObject()); } @Test public void testDeleteAutomation_Success() throws ResendException { - String automationId = "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"; - DeleteAutomationResponseSuccess expectedResponse = AutomationsUtil.deleteAutomationResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, DELETE_AUTOMATION_JSON, true); - when(automations.remove(automationId)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/automations/" + AUTOMATION_ID), anyString(), eq(HttpMethod.DELETE), eq(""), isNull())) + .thenReturn(httpResponse); - DeleteAutomationResponseSuccess response = automations.remove(automationId); + DeleteAutomationResponseSuccess response = automations.remove(AUTOMATION_ID); - assertEquals(expectedResponse, response); + assertNotNull(response); assertTrue(response.getDeleted()); - verify(automations, times(1)).remove(automationId); + assertEquals(AUTOMATION_ID, response.getId()); } @Test public void testStopAutomation_Success() throws ResendException { - String automationId = "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"; - StopAutomationResponseSuccess expectedResponse = AutomationsUtil.stopAutomationResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, STOP_AUTOMATION_JSON, true); - when(automations.stop(automationId)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/automations/" + AUTOMATION_ID + "/stop"), anyString(), eq(HttpMethod.POST), eq(""), any(MediaType.class))) + .thenReturn(httpResponse); - StopAutomationResponseSuccess response = automations.stop(automationId); + StopAutomationResponseSuccess response = automations.stop(AUTOMATION_ID); - assertEquals(expectedResponse, response); + assertNotNull(response); assertEquals(AutomationStatus.DISABLED, response.getStatus()); - verify(automations, times(1)).stop(automationId); } @Test public void testListAutomationRuns_Success() throws ResendException { - String automationId = "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"; - ListAutomationRunsResponseSuccess expectedResponse = AutomationsUtil.listAutomationRunsResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_RUNS_JSON, true); - when(automations.listRuns(automationId)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/automations/" + AUTOMATION_ID + "/runs"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - ListAutomationRunsResponseSuccess response = automations.listRuns(automationId); + ListAutomationRunsResponseSuccess response = automations.listRuns(AUTOMATION_ID); assertNotNull(response); - assertEquals(expectedResponse.getData().size(), response.getData().size()); - verify(automations, times(1)).listRuns(automationId); + assertEquals(3, response.getData().size()); } @Test public void testListAutomationRunsWithParams_Success() throws ResendException { - String automationId = "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"; ListAutomationRunsParams params = ListAutomationRunsParams.builder() .status(RunStatus.COMPLETED) .limit(10) .build(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_RUNS_JSON, true); - ListAutomationRunsResponseSuccess expectedResponse = AutomationsUtil.listAutomationRunsResponse(); + when(httpClient.perform(startsWith("/automations/" + AUTOMATION_ID + "/runs?"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - when(automations.listRuns(automationId, params)).thenReturn(expectedResponse); - - ListAutomationRunsResponseSuccess response = automations.listRuns(automationId, params); + ListAutomationRunsResponseSuccess response = automations.listRuns(AUTOMATION_ID, params); assertNotNull(response); - verify(automations, times(1)).listRuns(automationId, params); + assertEquals(3, response.getData().size()); } @Test public void testListAutomationRunsWithMultipleStatuses_Success() throws ResendException { - String automationId = "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"; ListAutomationRunsParams params = ListAutomationRunsParams.builder() .status(RunStatus.RUNNING, RunStatus.COMPLETED) .limit(10) .build(); - ListAutomationRunsResponseSuccess expectedResponse = AutomationsUtil.listAutomationRunsResponse(); + assertEquals(2, params.getStatus().size()); + assertEquals("?status=running,completed&limit=10", params.toQueryString()); + + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_RUNS_JSON, true); - when(automations.listRuns(automationId, params)).thenReturn(expectedResponse); + when(httpClient.perform(startsWith("/automations/" + AUTOMATION_ID + "/runs?"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - ListAutomationRunsResponseSuccess response = automations.listRuns(automationId, params); + ListAutomationRunsResponseSuccess response = automations.listRuns(AUTOMATION_ID, params); assertNotNull(response); - assertEquals(2, params.getStatus().size()); - assertEquals("?status=running,completed&limit=10", params.toQueryString()); - verify(automations, times(1)).listRuns(automationId, params); } @Test public void testGetAutomationRun_Success() throws ResendException { GetAutomationRunOptions options = GetAutomationRunOptions.builder() - .automationId("49a3999c-0ce1-4ea6-ab68-afcd6dc2e794") + .automationId(AUTOMATION_ID) .runId("run_1") .build(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, GET_RUN_JSON, true); - AutomationRun expectedResponse = AutomationsUtil.getAutomationRunResponse(); - - when(automations.getRun(options)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/automations/" + AUTOMATION_ID + "/runs/run_1"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); AutomationRun response = automations.getRun(options); assertNotNull(response); - assertEquals(expectedResponse.getId(), response.getId()); - assertEquals(expectedResponse.getStatus(), response.getStatus()); - verify(automations, times(1)).getRun(options); + assertEquals("run_1", response.getId()); + assertEquals(RunStatus.COMPLETED, response.getStatus()); } } diff --git a/src/test/java/com/resend/services/batch/BatchTest.java b/src/test/java/com/resend/services/batch/BatchTest.java new file mode 100644 index 0000000..0ae1c22 --- /dev/null +++ b/src/test/java/com/resend/services/batch/BatchTest.java @@ -0,0 +1,113 @@ +package com.resend.services.batch; + +import com.resend.core.exception.ResendException; +import com.resend.core.net.AbstractHttpResponse; +import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; +import com.resend.core.net.RequestOptions; +import com.resend.services.batch.model.CreateBatchEmailsResponse; +import com.resend.services.emails.model.CreateEmailOptions; +import com.resend.services.util.EmailsUtil; +import okhttp3.MediaType; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.when; + +@SuppressWarnings("unchecked") +public class BatchTest { + + private static final String BATCH_RESPONSE_JSON = + "{\"data\":[{\"id\":\"123\"},{\"id\":\"321\"}],\"errors\":[]}"; + + private static final String PERMISSIVE_BATCH_RESPONSE_JSON = + "{\"data\":[{\"id\":\"123\"},{\"id\":\"321\"}]," + + "\"errors\":[{\"index\":456,\"message\":\"error for email at index 456\"}," + + "{\"index\":789,\"message\":\"error for email at index 789\"}]}"; + + @Mock + private IHttpClient httpClient; + + private Batch batch; + + private AutoCloseable mocks; + + @BeforeEach + public void setUp() { + mocks = MockitoAnnotations.openMocks(this); + batch = new Batch("test-api-key", httpClient); + } + + @AfterEach + public void tearDown() throws Exception { + mocks.close(); + } + + @Test + public void testCreateBatchEmails_Success() throws ResendException { + List batchEmailsRequest = EmailsUtil.createBatchEmailOptions(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, BATCH_RESPONSE_JSON, true); + + when(httpClient.perform(eq("/emails/batch"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); + + CreateBatchEmailsResponse response = batch.send(batchEmailsRequest); + + assertNotNull(response); + assertEquals(2, response.getData().size()); + assertEquals("123", response.getData().get(0).getId()); + } + + @Test + public void testCreateBatchEmails_ApiError_ThrowsResendException() throws ResendException { + List batchEmailsRequest = EmailsUtil.createBatchEmailOptions(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(422, + "{\"name\":\"validation_error\",\"message\":\"Batch failed\"}", false); + + when(httpClient.perform(eq("/emails/batch"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); + + ResendException ex = assertThrows(ResendException.class, () -> batch.send(batchEmailsRequest)); + assertEquals(422, (int) ex.getStatusCode()); + } + + @Test + public void testCreatePermissiveBatchEmails_Success() throws ResendException { + RequestOptions options = RequestOptions.builder() + .add("x-batch-validation", "permissive").build(); + List batchEmailsRequest = EmailsUtil.createBatchEmailOptions(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, PERMISSIVE_BATCH_RESPONSE_JSON, true); + + when(httpClient.perform(eq("/emails/batch"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class), any(RequestOptions.class))) + .thenReturn(httpResponse); + + CreateBatchEmailsResponse response = batch.send(batchEmailsRequest, options); + + assertNotNull(response); + assertEquals(2, response.getData().size()); + assertEquals(2, response.getErrors().size()); + assertTrue(response.hasErrors()); + } + + @Test + public void testCreateBatchEmailsWithIdempotencyKey_Success() throws ResendException { + RequestOptions requestOptions = EmailsUtil.createRequestOptions(); + List batchEmailsRequest = EmailsUtil.createBatchEmailOptions(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, BATCH_RESPONSE_JSON, true); + + when(httpClient.perform(eq("/emails/batch"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class), any(RequestOptions.class))) + .thenReturn(httpResponse); + + CreateBatchEmailsResponse response = batch.send(batchEmailsRequest, requestOptions); + + assertNotNull(response); + assertEquals(2, response.getData().size()); + } +} diff --git a/src/test/java/com/resend/services/broadcasts/BroadcastsTest.java b/src/test/java/com/resend/services/broadcasts/BroadcastsTest.java index 1b01863..f3e1f09 100644 --- a/src/test/java/com/resend/services/broadcasts/BroadcastsTest.java +++ b/src/test/java/com/resend/services/broadcasts/BroadcastsTest.java @@ -1,156 +1,211 @@ package com.resend.services.broadcasts; import com.resend.core.exception.ResendException; +import com.resend.core.net.AbstractHttpResponse; +import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.services.broadcasts.model.*; import com.resend.services.util.BroadcastsUtil; +import okhttp3.MediaType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.when; +@SuppressWarnings("unchecked") public class BroadcastsTest { + private static final String BROADCAST_ID = "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"; + private static final String AUDIENCE_ID = "78261eea-8f8b-4381-83c6-79fa7120f1cf"; + private static final String GET_BROADCAST_ID = "559ac32e-9ef5-46fb-82a1-b76b840c0f7b"; + + private static final String CREATE_RESPONSE_JSON = + "{\"id\":\"" + BROADCAST_ID + "\"}"; + + private static final String GET_RESPONSE_JSON = + "{\"id\":\"" + GET_BROADCAST_ID + "\"," + + "\"audience_id\":\"" + AUDIENCE_ID + "\"," + + "\"status\":\"draft\"," + + "\"created_at\":\"2024-12-01T19:32:22.980Z\"," + + "\"object\":\"broadcast\"," + + "\"name\":\"Announcements\"," + + "\"from\":\"Acme \"," + + "\"html\":\"

Hello World

\"," + + "\"subject\":\"Check out our latest announcements\"," + + "\"text\":\"The plain text of the broadcast\"}"; + + private static final String REMOVE_RESPONSE_JSON = + "{\"id\":\"" + GET_BROADCAST_ID + "\",\"object\":\"object\",\"deleted\":true}"; + + private static final String LIST_RESPONSE_JSON = + "{\"object\":\"list\",\"has_more\":true,\"data\":[" + + "{\"id\":\"1\",\"audience_id\":\"" + AUDIENCE_ID + "\",\"status\":\"draft\",\"created_at\":\"2024-12-01T19:32:22.980Z\"}," + + "{\"id\":\"2\",\"audience_id\":\"" + AUDIENCE_ID + "\",\"status\":\"sent\",\"created_at\":\"2024-12-02T10:15:30.000Z\"}," + + "{\"id\":\"3\",\"audience_id\":\"" + AUDIENCE_ID + "\",\"status\":\"queued\",\"created_at\":\"2024-12-03T08:45:00.000Z\"}" + + "]}"; + + private static final String SEND_RESPONSE_JSON = + "{\"id\":\"" + BROADCAST_ID + "\"}"; + + private static final String UPDATE_RESPONSE_JSON = + "{\"id\":\"" + BROADCAST_ID + "\"}"; + @Mock + private IHttpClient httpClient; + private Broadcasts broadcasts; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); - broadcasts = mock(Broadcasts.class); + broadcasts = new Broadcasts("test-api-key", httpClient); } @Test public void testCreateBroadcast_Success() throws ResendException { - CreateBroadcastResponseSuccess expectedResponse = BroadcastsUtil.createBroadcastResponse(); CreateBroadcastOptions createOptions = BroadcastsUtil.createBroadcastRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, CREATE_RESPONSE_JSON, true); - when(broadcasts.create(createOptions)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/broadcasts"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); CreateBroadcastResponseSuccess response = broadcasts.create(createOptions); - assertEquals(expectedResponse, response); - verify(broadcasts, times(1)).create(createOptions); + assertNotNull(response); + assertEquals(BROADCAST_ID, response.getId()); } @Test - public void testUpdateBroadcast_Success() throws ResendException { - UpdateBroadcastResponseSuccess expectedResponse = BroadcastsUtil.updateBroadcastResponse(); - UpdateBroadcastOptions updateOptions = BroadcastsUtil.updateBroadcastRequest(); - - when(broadcasts.update(updateOptions)).thenReturn(expectedResponse); + public void testCreateBroadcast_ApiError_ThrowsResendException() throws ResendException { + CreateBroadcastOptions createOptions = BroadcastsUtil.createBroadcastRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(422, + "{\"name\":\"validation_error\",\"message\":\"Invalid audience\"}", false); - UpdateBroadcastResponseSuccess response = broadcasts.update(updateOptions); + when(httpClient.perform(eq("/broadcasts"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); - assertEquals(expectedResponse, response); - verify(broadcasts, times(1)).update(updateOptions); + ResendException ex = assertThrows(ResendException.class, () -> broadcasts.create(createOptions)); + assertEquals(422, (int) ex.getStatusCode()); } @Test public void testGetBroadcast_Success() throws ResendException { - String broadcastId = "12345"; - GetBroadcastResponseSuccess expectedResponse = BroadcastsUtil.getBroadcastResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, GET_RESPONSE_JSON, true); - when(broadcasts.get(broadcastId)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/broadcasts/" + GET_BROADCAST_ID), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - GetBroadcastResponseSuccess response = broadcasts.get(broadcastId); + GetBroadcastResponseSuccess response = broadcasts.get(GET_BROADCAST_ID); assertNotNull(response); - assertEquals(expectedResponse.getId(), response.getId()); - verify(broadcasts, times(1)).get(broadcastId); + assertEquals(GET_BROADCAST_ID, response.getId()); + assertEquals("draft", response.getStatus()); + assertEquals("Announcements", response.getName()); } @Test public void testSendBroadcast_Success() throws ResendException { - String broadcastId = "12345"; SendBroadcastOptions sendOptions = BroadcastsUtil.sendBroadcastRequest(); - SendBroadcastResponseSuccess expectedResponse = BroadcastsUtil.sendBroadcastResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, SEND_RESPONSE_JSON, true); - when(broadcasts.send(sendOptions, broadcastId)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/broadcasts/" + BROADCAST_ID + "/send"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); - SendBroadcastResponseSuccess response = broadcasts.send(sendOptions, broadcastId); + SendBroadcastResponseSuccess response = broadcasts.send(sendOptions, BROADCAST_ID); assertNotNull(response); - assertEquals(expectedResponse.getId(), response.getId()); - verify(broadcasts, times(1)).send(sendOptions, broadcastId); + assertEquals(BROADCAST_ID, response.getId()); } @Test public void testDeleteBroadcast_Success() throws ResendException { - String broadcastId = "12345"; - RemoveBroadcastResponseSuccess expectedResponse = BroadcastsUtil.removeBroadcastResponseSuccess(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, REMOVE_RESPONSE_JSON, true); - when(broadcasts.remove(broadcastId)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/broadcasts/" + GET_BROADCAST_ID), anyString(), eq(HttpMethod.DELETE), eq(""), isNull())) + .thenReturn(httpResponse); - RemoveBroadcastResponseSuccess response = broadcasts.remove(broadcastId); + RemoveBroadcastResponseSuccess response = broadcasts.remove(GET_BROADCAST_ID); - assertEquals(expectedResponse, response); - verify(broadcasts, times(1)).remove(broadcastId); + assertNotNull(response); + assertEquals(GET_BROADCAST_ID, response.getId()); + assertTrue(response.isDeleted()); } @Test public void testListBroadcasts_Success() throws ResendException { - ListBroadcastsResponseSuccess expectedResponse = BroadcastsUtil.createBroadcastsListResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_RESPONSE_JSON, true); - when(broadcasts.list()).thenReturn(expectedResponse); + when(httpClient.perform(eq("/broadcasts"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListBroadcastsResponseSuccess response = broadcasts.list(); assertNotNull(response); - assertEquals(expectedResponse.getData().size(), response.getData().size()); - assertEquals(expectedResponse.getObject(), response.getObject()); - verify(broadcasts, times(1)).list(); + assertEquals(3, response.getData().size()); + assertEquals("list", response.getObject()); } @Test public void testListBroadcastsWithPagination_Success() throws ResendException { - ListParams params = ListParams.builder() - .limit(3).build(); + ListParams params = ListParams.builder().limit(3).build(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_RESPONSE_JSON, true); - ListBroadcastsResponseSuccess expectedResponse = BroadcastsUtil.createBroadcastsListResponse(); - - when(broadcasts.list(params)).thenReturn(expectedResponse); + when(httpClient.perform(startsWith("/broadcasts?"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListBroadcastsResponseSuccess response = broadcasts.list(params); assertNotNull(response); - assertEquals(params.getLimit(), response.getData().size()); - assertEquals(expectedResponse.getObject(), response.getObject()); + assertEquals("list", response.getObject()); + } - verify(broadcasts, times(1)).list(params); + @Test + public void testUpdateBroadcast_Success() throws ResendException { + UpdateBroadcastOptions updateOptions = BroadcastsUtil.updateBroadcastRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, UPDATE_RESPONSE_JSON, true); + + when(httpClient.perform(eq("/broadcasts/" + updateOptions.getId()), anyString(), eq(HttpMethod.PATCH), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); + + UpdateBroadcastResponseSuccess response = broadcasts.update(updateOptions); + + assertNotNull(response); + assertEquals(BROADCAST_ID, response.getId()); } @Test public void testCreateAndSendBroadcast_Success() throws ResendException { - CreateBroadcastResponseSuccess expectedResponse = BroadcastsUtil.createBroadcastResponse(); CreateBroadcastOptions createOptions = BroadcastsUtil.createAndSendBroadcastRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, CREATE_RESPONSE_JSON, true); - when(broadcasts.create(createOptions)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/broadcasts"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); CreateBroadcastResponseSuccess response = broadcasts.create(createOptions); - assertEquals(expectedResponse, response); + assertNotNull(response); + assertEquals(BROADCAST_ID, response.getId()); assertTrue(createOptions.getSend()); - verify(broadcasts, times(1)).create(createOptions); } @Test public void testCreateAndScheduleBroadcast_Success() throws ResendException { - CreateBroadcastResponseSuccess expectedResponse = BroadcastsUtil.createBroadcastResponse(); CreateBroadcastOptions createOptions = BroadcastsUtil.createAndScheduleBroadcastRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, CREATE_RESPONSE_JSON, true); - when(broadcasts.create(createOptions)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/broadcasts"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); CreateBroadcastResponseSuccess response = broadcasts.create(createOptions); - assertEquals(expectedResponse, response); + assertNotNull(response); + assertEquals(BROADCAST_ID, response.getId()); assertTrue(createOptions.getSend()); assertNotNull(createOptions.getScheduledAt()); - verify(broadcasts, times(1)).create(createOptions); } - } - diff --git a/src/test/java/com/resend/services/contactproperties/ContactPropertiesTest.java b/src/test/java/com/resend/services/contactproperties/ContactPropertiesTest.java index aa4183a..e728108 100644 --- a/src/test/java/com/resend/services/contactproperties/ContactPropertiesTest.java +++ b/src/test/java/com/resend/services/contactproperties/ContactPropertiesTest.java @@ -1,96 +1,131 @@ package com.resend.services.contactproperties; import com.resend.core.exception.ResendException; +import com.resend.core.net.AbstractHttpResponse; +import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.services.contactproperties.model.*; import com.resend.services.util.ContactPropertiesUtil; +import okhttp3.MediaType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.when; +@SuppressWarnings("unchecked") public class ContactPropertiesTest { + private static final String CREATE_RESPONSE_JSON = "{\"id\":\"123\",\"object\":\"contact_property\"}"; + private static final String GET_RESPONSE_JSON = + "{\"id\":\"123\",\"key\":\"age\",\"created_at\":\"2023-04-08T00:11:13.110779+00:00\",\"type\":\"number\",\"fallback_value\":\"25\"}"; + private static final String UPDATE_RESPONSE_JSON = "{\"id\":\"123\",\"object\":\"contact_property\"}"; + private static final String REMOVE_RESPONSE_JSON = "{\"id\":\"123\",\"object\":\"contact_property\",\"deleted\":true}"; + private static final String LIST_RESPONSE_JSON = + "{\"data\":[" + + "{\"id\":\"1\",\"key\":\"age\",\"created_at\":\"2023-04-08T00:11:13.110779+00:00\",\"type\":\"string\",\"fallback_value\":\"25\"}," + + "{\"id\":\"2\",\"key\":\"city\",\"created_at\":\"2023-04-08T00:11:13.110779+00:00\",\"type\":\"string\",\"fallback_value\":\"New York\"}," + + "{\"id\":\"3\",\"key\":\"subscribed\",\"created_at\":\"2023-04-08T00:11:13.110779+00:00\",\"type\":\"string\",\"fallback_value\":\"fallback\"}" + + "],\"object\":\"list\",\"has_more\":false}"; + @Mock + private IHttpClient httpClient; + private ContactProperties contactProperties; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); - contactProperties = mock(ContactProperties.class); + contactProperties = new ContactProperties("test-api-key", httpClient); } @Test public void testCreateContactProperty_Success() throws ResendException { - CreateContactPropertyResponseSuccess expected = ContactPropertiesUtil.createContactPropertyResponseSuccess(); CreateContactPropertyOptions param = ContactPropertiesUtil.createContactPropertyRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, CREATE_RESPONSE_JSON, true); - when(contactProperties.create(param)) - .thenReturn(expected); + when(httpClient.perform(eq("/contact-properties"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); CreateContactPropertyResponseSuccess created = contactProperties.create(param); - assertEquals(created, expected); - verify(contactProperties, times(1)).create(param); + assertNotNull(created); + assertEquals("123", created.getId()); + assertEquals("contact_property", created.getObject()); + } + + @Test + public void testCreateContactProperty_ApiError_ThrowsResendException() throws ResendException { + CreateContactPropertyOptions param = ContactPropertiesUtil.createContactPropertyRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(422, "{\"name\":\"validation_error\",\"message\":\"Invalid\"}", false); + + when(httpClient.perform(eq("/contact-properties"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); + + ResendException ex = assertThrows(ResendException.class, () -> contactProperties.create(param)); + assertEquals(422, (int) ex.getStatusCode()); } @Test public void testListContactProperties_Success() throws ResendException { - ListContactPropertiesResponseSuccess expectedResponse = ContactPropertiesUtil.createContactPropertiesListResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_RESPONSE_JSON, true); - when(contactProperties.list()) - .thenReturn(expectedResponse); + when(httpClient.perform(eq("/contact-properties"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListContactPropertiesResponseSuccess res = contactProperties.list(); assertNotNull(res); - assertEquals(expectedResponse.getData().size(), res.getData().size()); - assertEquals(expectedResponse.getObject(), res.getObject()); + assertEquals(3, res.getData().size()); + assertEquals("list", res.getObject()); } @Test public void testGetContactProperty_Success() throws ResendException { String id = "123"; - ContactProperty expected = ContactPropertiesUtil.getContactProperty(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, GET_RESPONSE_JSON, true); - when(contactProperties.get(id)).thenReturn(expected); + when(httpClient.perform(eq("/contact-properties/" + id), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ContactProperty res = contactProperties.get(id); assertNotNull(res); - assertEquals(expected, res); - verify(contactProperties, times(1)).get(id); + assertEquals("123", res.getId()); + assertEquals("age", res.getKey()); + assertEquals("number", res.getType()); } @Test public void testUpdateContactProperty_Success() throws ResendException { UpdateContactPropertyOptions params = ContactPropertiesUtil.createUpdateOptions(); - UpdateContactPropertyResponseSuccess expectedResponse = ContactPropertiesUtil.updateContactPropertyResponseSuccess(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, UPDATE_RESPONSE_JSON, true); - when(contactProperties.update(params)) - .thenReturn(expectedResponse); + when(httpClient.perform(eq("/contact-properties/123"), anyString(), eq(HttpMethod.PATCH), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); UpdateContactPropertyResponseSuccess res = contactProperties.update(params); assertNotNull(res); - assertEquals(expectedResponse.getId(), res.getId()); - assertEquals(expectedResponse.getObject(), res.getObject()); + assertEquals("123", res.getId()); + assertEquals("contact_property", res.getObject()); } @Test public void testRemoveContactProperty_Success() throws ResendException { String id = "123"; - RemoveContactPropertyResponseSuccess removed = ContactPropertiesUtil.removeContactPropertyResponseSuccess(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, REMOVE_RESPONSE_JSON, true); - when(contactProperties.remove(id)) - .thenReturn(removed); + when(httpClient.perform(eq("/contact-properties/" + id), anyString(), eq(HttpMethod.DELETE), eq(""), isNull())) + .thenReturn(httpResponse); RemoveContactPropertyResponseSuccess res = contactProperties.remove(id); - assertEquals(removed, res); - verify(contactProperties, times(1)).remove(id); + assertNotNull(res); + assertEquals("123", res.getId()); + assertTrue(res.isDeleted()); } } diff --git a/src/test/java/com/resend/services/contacts/ContactImportsTest.java b/src/test/java/com/resend/services/contacts/ContactImportsTest.java index 117d9a2..bd358a5 100644 --- a/src/test/java/com/resend/services/contacts/ContactImportsTest.java +++ b/src/test/java/com/resend/services/contacts/ContactImportsTest.java @@ -1,12 +1,16 @@ package com.resend.services.contacts; import com.resend.core.exception.ResendException; +import com.resend.core.net.AbstractHttpResponse; +import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.services.contacts.model.CreateContactImportOptions; import com.resend.services.contacts.model.CreateContactImportResponseSuccess; import com.resend.services.contacts.model.GetContactImportResponseSuccess; import com.resend.services.contacts.model.ListContactImportsParams; import com.resend.services.contacts.model.ListContactImportsResponseSuccess; import com.resend.services.util.ContactImportsUtil; +import okhttp3.MediaType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; @@ -14,68 +18,104 @@ import java.io.File; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.when; +@SuppressWarnings("unchecked") public class ContactImportsTest { + private static final String IMPORT_ID = ContactImportsUtil.CONTACT_IMPORT_ID; + + private static final String CREATE_IMPORT_JSON = + "{\"object\":\"contact_import\",\"id\":\"" + IMPORT_ID + "\"}"; + + private static final String GET_IMPORT_JSON = + "{\"object\":\"contact_import\",\"id\":\"" + IMPORT_ID + "\"," + + "\"status\":\"completed\"," + + "\"created_at\":\"2026-05-15 18:32:37.823+00\"," + + "\"completed_at\":\"2026-05-15 18:33:42.916+00\"," + + "\"counts\":{\"total\":1200,\"created\":800,\"updated\":300,\"skipped\":75,\"failed\":25}}"; + + private static final String LIST_IMPORTS_JSON = + "{\"object\":\"list\",\"has_more\":false,\"data\":[" + + "{\"object\":\"contact_import\",\"id\":\"" + IMPORT_ID + "\"," + + "\"status\":\"completed\"," + + "\"created_at\":\"2026-05-15 18:32:37.823+00\"," + + "\"completed_at\":\"2026-05-15 18:33:42.916+00\"," + + "\"counts\":{\"total\":1200,\"created\":800,\"updated\":300,\"skipped\":75,\"failed\":25}}" + + "]}"; + @Mock + private IHttpClient httpClient; + private ContactImports contactImports; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); - contactImports = mock(ContactImports.class); + contactImports = new ContactImports("test-api-key", httpClient); } @Test public void testCreateContactImport_Success() throws ResendException { File file = new File("contacts.csv"); CreateContactImportOptions options = ContactImportsUtil.createContactImportOptions(file); - CreateContactImportResponseSuccess expected = ContactImportsUtil.createContactImportResponseSuccess(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, CREATE_IMPORT_JSON, true); - when(contactImports.create(options)).thenReturn(expected); + when(httpClient.performMultipart(eq("/contacts/imports"), anyString(), eq(HttpMethod.POST), + any(File.class), any(MediaType.class), anyMap(), isNull())) + .thenReturn(httpResponse); CreateContactImportResponseSuccess res = contactImports.create(options); assertNotNull(res); - assertEquals(expected.getId(), res.getId()); - assertEquals(expected.getObject(), res.getObject()); - verify(contactImports, times(1)).create(options); + assertEquals(IMPORT_ID, res.getId()); + assertEquals("contact_import", res.getObject()); + } + + @Test + public void testCreateContactImport_ApiError_ThrowsResendException() throws ResendException { + File file = new File("contacts.csv"); + CreateContactImportOptions options = ContactImportsUtil.createContactImportOptions(file); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(422, + "{\"name\":\"validation_error\",\"message\":\"Invalid file\"}", false); + + when(httpClient.performMultipart(eq("/contacts/imports"), anyString(), eq(HttpMethod.POST), + any(File.class), any(MediaType.class), anyMap(), isNull())) + .thenReturn(httpResponse); + + ResendException ex = assertThrows(ResendException.class, () -> contactImports.create(options)); + assertEquals(422, (int) ex.getStatusCode()); } @Test public void testGetContactImport_Success() throws ResendException { - String id = ContactImportsUtil.CONTACT_IMPORT_ID; - GetContactImportResponseSuccess expected = ContactImportsUtil.getContactImportResponseSuccess(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, GET_IMPORT_JSON, true); - when(contactImports.get(id)).thenReturn(expected); + when(httpClient.perform(eq("/contacts/imports/" + IMPORT_ID), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - GetContactImportResponseSuccess res = contactImports.get(id); + GetContactImportResponseSuccess res = contactImports.get(IMPORT_ID); assertNotNull(res); - assertEquals(expected.getId(), res.getId()); - assertEquals(expected.getStatus(), res.getStatus()); - assertEquals(expected.getCounts().getTotal(), res.getCounts().getTotal()); - verify(contactImports, times(1)).get(id); + assertEquals(IMPORT_ID, res.getId()); + assertEquals("completed", res.getStatus()); + assertEquals(Integer.valueOf(1200), res.getCounts().getTotal()); } @Test public void testListContactImports_Success() throws ResendException { - ListContactImportsResponseSuccess expected = ContactImportsUtil.listContactImportsResponseSuccess(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_IMPORTS_JSON, true); - when(contactImports.list()).thenReturn(expected); + when(httpClient.perform(eq("/contacts/imports"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListContactImportsResponseSuccess res = contactImports.list(); assertNotNull(res); - assertEquals(expected.getData().size(), res.getData().size()); - assertEquals(expected.getObject(), res.getObject()); - verify(contactImports, times(1)).list(); + assertEquals(1, res.getData().size()); + assertEquals("list", res.getObject()); } @Test @@ -84,16 +124,15 @@ public void testListContactImportsWithParams_Success() throws ResendException { .limit(10) .status("completed") .build(); - ListContactImportsResponseSuccess expected = ContactImportsUtil.listContactImportsResponseSuccess(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_IMPORTS_JSON, true); - when(contactImports.list(params)).thenReturn(expected); + when(httpClient.perform(anyString(), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListContactImportsResponseSuccess res = contactImports.list(params); assertNotNull(res); - assertEquals(expected.getData().size(), res.getData().size()); - assertEquals("completed", params.getStatus()); - assertEquals(Integer.valueOf(10), params.getLimit()); - verify(contactImports, times(1)).list(params); + assertEquals(1, res.getData().size()); + assertEquals("completed", res.getData().get(0).getStatus()); } } diff --git a/src/test/java/com/resend/services/contacts/ContactSegmentsTest.java b/src/test/java/com/resend/services/contacts/ContactSegmentsTest.java index 69a56ee..1321e32 100644 --- a/src/test/java/com/resend/services/contacts/ContactSegmentsTest.java +++ b/src/test/java/com/resend/services/contacts/ContactSegmentsTest.java @@ -1,145 +1,177 @@ package com.resend.services.contacts; import com.resend.core.exception.ResendException; +import com.resend.core.net.AbstractHttpResponse; +import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.services.contacts.model.*; import com.resend.services.util.ContactsUtil; +import okhttp3.MediaType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.when; +@SuppressWarnings("unchecked") public class ContactSegmentsTest { + private static final String SEGMENT_ID = "78261eea-8f8b-4381-83c6-79fa7120f1cf"; + private static final String CONTACT_ID = "e169aa45-1ecf-4183-9955-b1499d5701d3"; + + private static final String ADD_SEGMENT_JSON = "{\"id\":\"" + SEGMENT_ID + "\"}"; + private static final String REMOVE_SEGMENT_JSON = "{\"id\":\"" + SEGMENT_ID + "\",\"deleted\":true}"; + private static final String LIST_SEGMENTS_JSON = + "{\"object\":\"list\",\"data\":[" + + "{\"id\":\"" + SEGMENT_ID + "\",\"name\":\"Registered Users\",\"created_at\":\"2023-10-06T22:59:55.977Z\"}," + + "{\"id\":\"b9d24c8e-bf1c-5d4d-cf1d-470cbd97482f\",\"name\":\"Premium Users\",\"created_at\":\"2023-11-12T14:23:10.123Z\"}" + + "],\"has_more\":false}"; + @Mock + private IHttpClient httpClient; + private ContactSegments contactSegments; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); - contactSegments = mock(ContactSegments.class); + contactSegments = new ContactSegments("test-api-key", httpClient); } @Test public void testAddContactToSegmentById_Success() throws ResendException { AddContactToSegmentOptions options = ContactsUtil.createAddContactToSegmentOptions(); - AddContactToSegmentResponseSuccess expectedResponse = ContactsUtil.addContactToSegmentResponseSuccess(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, ADD_SEGMENT_JSON, true); - when(contactSegments.add(options)) - .thenReturn(expectedResponse); + when(httpClient.perform( + eq("/contacts/" + CONTACT_ID + "/segments/" + SEGMENT_ID), + anyString(), eq(HttpMethod.POST), eq(""), any(MediaType.class))) + .thenReturn(httpResponse); AddContactToSegmentResponseSuccess res = contactSegments.add(options); assertNotNull(res); - assertEquals(expectedResponse.getId(), res.getId()); - verify(contactSegments, times(1)).add(options); + assertEquals(SEGMENT_ID, res.getId()); } @Test public void testAddContactToSegmentByEmail_Success() throws ResendException { AddContactToSegmentOptions options = AddContactToSegmentOptions.builder() .email("steve.wozniak@gmail.com") - .segmentId("78261eea-8f8b-4381-83c6-79fa7120f1cf") + .segmentId(SEGMENT_ID) .build(); - AddContactToSegmentResponseSuccess expectedResponse = ContactsUtil.addContactToSegmentResponseSuccess(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, ADD_SEGMENT_JSON, true); - when(contactSegments.add(options)) - .thenReturn(expectedResponse); + when(httpClient.perform( + eq("/contacts/steve.wozniak@gmail.com/segments/" + SEGMENT_ID), + anyString(), eq(HttpMethod.POST), eq(""), any(MediaType.class))) + .thenReturn(httpResponse); AddContactToSegmentResponseSuccess res = contactSegments.add(options); assertNotNull(res); - assertEquals(expectedResponse.getId(), res.getId()); - verify(contactSegments, times(1)).add(options); + assertEquals(SEGMENT_ID, res.getId()); + } + + @Test + public void testAddContactToSegment_ApiError_ThrowsResendException() throws ResendException { + AddContactToSegmentOptions options = ContactsUtil.createAddContactToSegmentOptions(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(404, + "{\"name\":\"not_found\",\"message\":\"Segment not found\"}", false); + + when(httpClient.perform(anyString(), anyString(), eq(HttpMethod.POST), eq(""), any(MediaType.class))) + .thenReturn(httpResponse); + + ResendException ex = assertThrows(ResendException.class, () -> contactSegments.add(options)); + assertEquals(404, (int) ex.getStatusCode()); } @Test public void testRemoveContactFromSegmentById_Success() throws ResendException { RemoveContactFromSegmentOptions options = ContactsUtil.createRemoveContactFromSegmentOptions(); - RemoveContactFromSegmentResponseSuccess expectedResponse = ContactsUtil.removeContactFromSegmentResponseSuccess(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, REMOVE_SEGMENT_JSON, true); - when(contactSegments.remove(options)) - .thenReturn(expectedResponse); + when(httpClient.perform( + eq("/contacts/" + CONTACT_ID + "/segments/" + SEGMENT_ID), + anyString(), eq(HttpMethod.DELETE), eq(""), isNull())) + .thenReturn(httpResponse); RemoveContactFromSegmentResponseSuccess res = contactSegments.remove(options); assertNotNull(res); - assertEquals(expectedResponse.getId(), res.getId()); - assertEquals(expectedResponse.getDeleted(), res.getDeleted()); - verify(contactSegments, times(1)).remove(options); + assertEquals(SEGMENT_ID, res.getId()); + assertEquals(Boolean.TRUE, res.getDeleted()); } @Test public void testRemoveContactFromSegmentByEmail_Success() throws ResendException { RemoveContactFromSegmentOptions options = RemoveContactFromSegmentOptions.builder() .email("steve.wozniak@gmail.com") - .segmentId("78261eea-8f8b-4381-83c6-79fa7120f1cf") + .segmentId(SEGMENT_ID) .build(); - RemoveContactFromSegmentResponseSuccess expectedResponse = ContactsUtil.removeContactFromSegmentResponseSuccess(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, REMOVE_SEGMENT_JSON, true); - when(contactSegments.remove(options)) - .thenReturn(expectedResponse); + when(httpClient.perform( + eq("/contacts/steve.wozniak@gmail.com/segments/" + SEGMENT_ID), + anyString(), eq(HttpMethod.DELETE), eq(""), isNull())) + .thenReturn(httpResponse); RemoveContactFromSegmentResponseSuccess res = contactSegments.remove(options); assertNotNull(res); - assertEquals(expectedResponse.getId(), res.getId()); - assertEquals(expectedResponse.getDeleted(), res.getDeleted()); - verify(contactSegments, times(1)).remove(options); + assertEquals(SEGMENT_ID, res.getId()); + assertEquals(Boolean.TRUE, res.getDeleted()); } @Test public void testListContactSegmentsById_Success() throws ResendException { - String contactId = "e169aa45-1ecf-4183-9955-b1499d5701d3"; - ListContactSegmentsResponseSuccess expectedResponse = ContactsUtil.listContactSegmentsResponseSuccess(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_SEGMENTS_JSON, true); - when(contactSegments.list(contactId)) - .thenReturn(expectedResponse); + when(httpClient.perform( + eq("/contacts/" + CONTACT_ID + "/segments"), + anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - ListContactSegmentsResponseSuccess res = contactSegments.list(contactId); + ListContactSegmentsResponseSuccess res = contactSegments.list(CONTACT_ID); assertNotNull(res); - assertEquals(expectedResponse.getData().size(), res.getData().size()); - assertEquals(expectedResponse.getObject(), res.getObject()); - verify(contactSegments, times(1)).list(contactId); + assertEquals(2, res.getData().size()); + assertEquals("list", res.getObject()); } @Test public void testListContactSegmentsByEmail_Success() throws ResendException { String contactEmail = "steve.wozniak@gmail.com"; - ListContactSegmentsResponseSuccess expectedResponse = ContactsUtil.listContactSegmentsResponseSuccess(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_SEGMENTS_JSON, true); - when(contactSegments.list(contactEmail)) - .thenReturn(expectedResponse); + when(httpClient.perform( + eq("/contacts/" + contactEmail + "/segments"), + anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListContactSegmentsResponseSuccess res = contactSegments.list(contactEmail); assertNotNull(res); - assertEquals(expectedResponse.getData().size(), res.getData().size()); - assertEquals(expectedResponse.getObject(), res.getObject()); - verify(contactSegments, times(1)).list(contactEmail); + assertEquals(2, res.getData().size()); + assertEquals("list", res.getObject()); } @Test public void testListContactSegmentsWithPagination_Success() throws ResendException { - String contactId = "e169aa45-1ecf-4183-9955-b1499d5701d3"; - ListParams params = ListParams.builder() - .limit(10) - .build(); - ListContactSegmentsResponseSuccess expectedResponse = ContactsUtil.listContactSegmentsResponseSuccess(); + ListParams params = ListParams.builder().limit(10).build(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_SEGMENTS_JSON, true); - when(contactSegments.list(contactId, params)) - .thenReturn(expectedResponse); + when(httpClient.perform(anyString(), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - ListContactSegmentsResponseSuccess res = contactSegments.list(contactId, params); + ListContactSegmentsResponseSuccess res = contactSegments.list(CONTACT_ID, params); assertNotNull(res); - assertEquals(expectedResponse.getData().size(), res.getData().size()); - assertEquals(expectedResponse.getObject(), res.getObject()); - verify(contactSegments, times(1)).list(contactId, params); + assertEquals(2, res.getData().size()); + assertEquals("list", res.getObject()); } } diff --git a/src/test/java/com/resend/services/contacts/ContactTopicsTest.java b/src/test/java/com/resend/services/contacts/ContactTopicsTest.java index 8dda8e1..4be169e 100644 --- a/src/test/java/com/resend/services/contacts/ContactTopicsTest.java +++ b/src/test/java/com/resend/services/contacts/ContactTopicsTest.java @@ -1,94 +1,118 @@ package com.resend.services.contacts; import com.resend.core.exception.ResendException; +import com.resend.core.net.AbstractHttpResponse; +import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.services.contacts.model.ListContactTopicsResponse; import com.resend.services.contacts.model.UpdateContactTopicsOptions; import com.resend.services.contacts.model.UpdateContactTopicsResponse; import com.resend.services.util.ContactsUtil; +import okhttp3.MediaType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.when; +@SuppressWarnings("unchecked") public class ContactTopicsTest { + private static final String CONTACT_ID = "e169aa45-1ecf-4183-9955-b1499d5701d3"; + + private static final String LIST_TOPICS_JSON = + "{\"object\":\"list\",\"data\":[" + + "{\"id\":\"b6d24b8e-af0b-4c3c-be0c-359bbd97381e\",\"name\":\"Product Updates\"," + + "\"description\":\"New features, and latest announcements.\",\"subscription\":\"opt_in\"}," + + "{\"id\":\"07d84122-7224-4881-9c31-1c048e204602\",\"name\":\"Weekly Newsletter\"," + + "\"description\":\"Weekly digest of content.\",\"subscription\":\"opt_out\"}" + + "],\"has_more\":false}"; + + private static final String UPDATE_TOPICS_JSON = "{\"id\":\"" + CONTACT_ID + "\"}"; + @Mock + private IHttpClient httpClient; + private ContactTopics contactTopics; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); - contactTopics = mock(ContactTopics.class); + contactTopics = new ContactTopics("test-api-key", httpClient); } @Test public void testListTopicsById_Success() throws ResendException { - String contactId = "e169aa45-1ecf-4183-9955-b1499d5701d3"; - ListContactTopicsResponse expectedResponse = ContactsUtil.createContactTopicsListResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_TOPICS_JSON, true); - when(contactTopics.list(contactId)) - .thenReturn(expectedResponse); + when(httpClient.perform(eq("/contacts/" + CONTACT_ID + "/topics"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - ListContactTopicsResponse res = contactTopics.list(contactId); + ListContactTopicsResponse res = contactTopics.list(CONTACT_ID); assertNotNull(res); - assertEquals(expectedResponse.getData().size(), res.getData().size()); - assertEquals(expectedResponse.getObject(), res.getObject()); - verify(contactTopics, times(1)).list(contactId); + assertEquals(2, res.getData().size()); + assertEquals("list", res.getObject()); + assertEquals("b6d24b8e-af0b-4c3c-be0c-359bbd97381e", res.getData().get(0).getId()); } @Test public void testListTopicsByEmail_Success() throws ResendException { String contactEmail = "steve.wozniak@gmail.com"; - ListContactTopicsResponse expectedResponse = ContactsUtil.createContactTopicsListResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_TOPICS_JSON, true); - when(contactTopics.list(contactEmail)) - .thenReturn(expectedResponse); + when(httpClient.perform(eq("/contacts/" + contactEmail + "/topics"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListContactTopicsResponse res = contactTopics.list(contactEmail); assertNotNull(res); - assertEquals(expectedResponse.getData().size(), res.getData().size()); - assertEquals(expectedResponse.getObject(), res.getObject()); - verify(contactTopics, times(1)).list(contactEmail); + assertEquals(2, res.getData().size()); + assertEquals("list", res.getObject()); } @Test public void testListTopicsWithPagination_Success() throws ResendException { - String contactId = "e169aa45-1ecf-4183-9955-b1499d5701d3"; - ListParams params = ListParams.builder() - .limit(10) - .build(); - ListContactTopicsResponse expectedResponse = ContactsUtil.createContactTopicsListResponse(); + ListParams params = ListParams.builder().limit(10).build(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_TOPICS_JSON, true); - when(contactTopics.list(contactId, params)) - .thenReturn(expectedResponse); + when(httpClient.perform(anyString(), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - ListContactTopicsResponse res = contactTopics.list(contactId, params); + ListContactTopicsResponse res = contactTopics.list(CONTACT_ID, params); assertNotNull(res); - assertEquals(expectedResponse.getData().size(), res.getData().size()); - assertEquals(expectedResponse.getObject(), res.getObject()); - verify(contactTopics, times(1)).list(contactId, params); + assertEquals(2, res.getData().size()); + assertEquals("list", res.getObject()); } @Test public void testUpdateTopics_Success() throws ResendException { UpdateContactTopicsOptions options = ContactsUtil.createUpdateTopicsOptions(); - UpdateContactTopicsResponse expectedResponse = ContactsUtil.updateContactTopicsResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, UPDATE_TOPICS_JSON, true); - when(contactTopics.update(options)) - .thenReturn(expectedResponse); + when(httpClient.perform(eq("/contacts/" + CONTACT_ID + "/topics"), anyString(), eq(HttpMethod.PATCH), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); UpdateContactTopicsResponse res = contactTopics.update(options); assertNotNull(res); - assertEquals(expectedResponse.getId(), res.getId()); - verify(contactTopics, times(1)).update(options); + assertEquals(CONTACT_ID, res.getId()); + } + + @Test + public void testListTopics_ApiError_ThrowsResendException() throws ResendException { + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(404, + "{\"name\":\"not_found\",\"message\":\"Contact not found\"}", false); + + when(httpClient.perform(anyString(), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); + + ResendException ex = assertThrows(ResendException.class, () -> contactTopics.list(CONTACT_ID)); + assertEquals(404, (int) ex.getStatusCode()); } } diff --git a/src/test/java/com/resend/services/contacts/ContactsTest.java b/src/test/java/com/resend/services/contacts/ContactsTest.java index 5931f98..12d1d24 100644 --- a/src/test/java/com/resend/services/contacts/ContactsTest.java +++ b/src/test/java/com/resend/services/contacts/ContactsTest.java @@ -1,59 +1,97 @@ package com.resend.services.contacts; import com.resend.core.exception.ResendException; +import com.resend.core.net.AbstractHttpResponse; +import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.services.contacts.model.*; import com.resend.services.util.ContactsUtil; +import okhttp3.MediaType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.when; +@SuppressWarnings("unchecked") public class ContactsTest { + private static final String CREATE_CONTACT_JSON = "{\"id\":\"123\",\"object\":\"contact\"}"; + private static final String REMOVE_CONTACT_JSON = "{\"id\":\"123\",\"object\":\"contact\",\"deleted\":true}"; + private static final String UPDATE_CONTACT_JSON = "{\"id\":\"123\",\"object\":\"contact\"}"; + private static final String GET_CONTACT_JSON = + "{\"object\":\"contacts\",\"id\":\"123\",\"email\":\"user@example.com\"," + + "\"first_name\":\"test\",\"last_name\":\"test\"," + + "\"created_at\":\"2025-04-30T12:00:00Z\",\"unsubscribed\":false}"; + private static final String LIST_CONTACTS_JSON = + "{\"data\":[" + + "{\"id\":\"1\",\"email\":\"frodo.baggins@shire.com\",\"first_name\":\"Frodo\"," + + "\"last_name\":\"Baggins\",\"created_at\":\"2023-04-08T00:11:13.110779+00:00\",\"unsubscribed\":false}," + + "{\"id\":\"2\",\"email\":\"aragorn.strider@gondor.com\",\"first_name\":\"Aragorn\"," + + "\"last_name\":\"Strider\",\"created_at\":\"2023-04-08T00:11:13.110779+00:00\",\"unsubscribed\":false}," + + "{\"id\":\"3\",\"email\":\"legolas.greenleaf@woodland.com\",\"first_name\":\"Legolas\"," + + "\"last_name\":\"Greenleaf\",\"created_at\":\"2023-04-08T00:11:13.110779+00:00\",\"unsubscribed\":false}" + + "],\"object\":\"list\"}"; + private static final String UPDATE_TOPICS_JSON = + "{\"id\":\"e169aa45-1ecf-4183-9955-b1499d5701d3\"}"; + @Mock + private IHttpClient httpClient; + private Contacts contacts; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); - contacts = mock(Contacts.class); + contacts = new Contacts("test-api-key", httpClient); } @Test public void testCreateContact_Success() throws ResendException { - CreateContactResponseSuccess expectedContact = ContactsUtil.createContactResponseSuccess(); CreateContactOptions param = ContactsUtil.createContactRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, CREATE_CONTACT_JSON, true); - when(contacts.create(param)) - .thenReturn(expectedContact); + when(httpClient.perform(eq("/contacts"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); - CreateContactResponseSuccess createdContact = contacts.create(param); + CreateContactResponseSuccess response = contacts.create(param); + + assertNotNull(response); + assertEquals("123", response.getId()); + assertEquals("contact", response.getObject()); + } + + @Test + public void testCreateContact_ApiError_ThrowsResendException() throws ResendException { + CreateContactOptions param = ContactsUtil.createContactRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(422, + "{\"name\":\"validation_error\",\"message\":\"Invalid email\"}", false); + when(httpClient.perform(eq("/contacts"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); - assertEquals(createdContact, expectedContact); - verify(contacts, times(1)).create(param); + ResendException ex = assertThrows(ResendException.class, () -> contacts.create(param)); + assertEquals(422, (int) ex.getStatusCode()); } @Test public void testRemoveContact_Success() throws ResendException { RemoveContactOptions params = RemoveContactOptions.builder() .id("e169aa45-1ecf-4183-9955-b1499d5701d3") - .audienceId("78261eea-8f8b-4381-83c6-79fa7120f1cf") .build(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, REMOVE_CONTACT_JSON, true); - RemoveContactResponseSuccess removed = ContactsUtil.removeContactResponseSuccess(); - when(contacts.remove(params)) - .thenReturn(removed); + when(httpClient.perform(eq("/contacts/e169aa45-1ecf-4183-9955-b1499d5701d3"), anyString(), eq(HttpMethod.DELETE), eq(""), isNull())) + .thenReturn(httpResponse); RemoveContactResponseSuccess res = contacts.remove(params); - assertEquals(removed, res); + assertNotNull(res); + assertEquals("123", res.getId()); } @Test @@ -61,15 +99,16 @@ public void testListContactsBySegmentId_Success() throws ResendException { ListContactsOptions options = ListContactsOptions.builder() .segmentId("segment-123") .build(); - ListContactsResponseSuccess expectedResponse = ContactsUtil.createContactsListResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_CONTACTS_JSON, true); - when(contacts.list(options)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/segments/segment-123/contacts"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListContactsResponseSuccess res = contacts.list(options); assertNotNull(res); - assertEquals(expectedResponse.getData().size(), res.getData().size()); - assertEquals(expectedResponse.getObject(), res.getObject()); + assertEquals(3, res.getData().size()); + assertEquals("list", res.getObject()); } @Test @@ -77,15 +116,16 @@ public void testListContactsByAudienceId_Success() throws ResendException { ListContactsOptions options = ListContactsOptions.builder() .audienceId("audience-123") .build(); - ListContactsResponseSuccess expectedResponse = ContactsUtil.createContactsListResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_CONTACTS_JSON, true); - when(contacts.list(options)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/segments/audience-123/contacts"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListContactsResponseSuccess res = contacts.list(options); assertNotNull(res); - assertEquals(expectedResponse.getData().size(), res.getData().size()); - assertEquals(expectedResponse.getObject(), res.getObject()); + assertEquals(3, res.getData().size()); + assertEquals("list", res.getObject()); } @Test @@ -104,203 +144,200 @@ public void testListContactsWithPaginationBySegmentId_Success() throws ResendExc ListContactsOptions options = ListContactsOptions.builder() .segmentId("segment-123") .build(); - ListContactsResponseSuccess expectedResponse = ContactsUtil.createContactsListResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_CONTACTS_JSON, true); - when(contacts.list(options, params)).thenReturn(expectedResponse); + when(httpClient.perform(anyString(), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListContactsResponseSuccess res = contacts.list(options, params); assertNotNull(res); - assertEquals(params.getLimit(), res.getData().size()); - assertEquals(expectedResponse.getObject(), res.getObject()); + assertEquals(3, res.getData().size()); + assertEquals("list", res.getObject()); } @Test public void testListContactsGlobalWhenNoId_Success() throws ResendException { ListContactsOptions options = ListContactsOptions.builder().build(); - ListContactsResponseSuccess expectedResponse = ContactsUtil.createContactsListResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_CONTACTS_JSON, true); - when(contacts.list(options)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/contacts"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListContactsResponseSuccess res = contacts.list(options); assertNotNull(res); - assertEquals(expectedResponse.getData().size(), res.getData().size()); + assertEquals(3, res.getData().size()); } @Test public void testListContacts_Success() throws ResendException { - String audienceId = "123"; - ListContactsResponseSuccess expectedResponse = ContactsUtil.createContactsListResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_CONTACTS_JSON, true); - when(contacts.list(audienceId)) - .thenReturn(expectedResponse); + when(httpClient.perform(eq("/segments/123/contacts"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - ListContactsResponseSuccess res = contacts.list(audienceId); + ListContactsResponseSuccess res = contacts.list("123"); assertNotNull(res); - assertEquals(expectedResponse.getData().size(), res.getData().size()); - assertEquals(expectedResponse.getObject(), res.getObject()); + assertEquals(3, res.getData().size()); + assertEquals("list", res.getObject()); } @Test public void testListContactsWithPagination_Success() throws ResendException { - ListParams params = ListParams.builder() - .limit(3).build(); - - String audienceId = "123"; - ListContactsResponseSuccess expectedResponse = ContactsUtil.createContactsListResponse(); + ListParams params = ListParams.builder().limit(3).build(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_CONTACTS_JSON, true); - when(contacts.list(audienceId, params)) - .thenReturn(expectedResponse); + when(httpClient.perform(anyString(), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - ListContactsResponseSuccess res = contacts.list(audienceId, params); + ListContactsResponseSuccess res = contacts.list("123", params); assertNotNull(res); - assertEquals(params.getLimit(), res.getData().size()); - assertEquals(expectedResponse.getObject(), res.getObject()); + assertEquals(3, res.getData().size()); + assertEquals("list", res.getObject()); } @Test public void testUpdateContact_Success() throws ResendException { UpdateContactOptions params = ContactsUtil.createUpdateOptions(); - UpdateContactResponseSuccess expectedResponse = ContactsUtil.updateContactResponseSuccess(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, UPDATE_CONTACT_JSON, true); - when(contacts.update(params)) - .thenReturn(expectedResponse); + when(httpClient.perform(eq("/contacts/123"), anyString(), eq(HttpMethod.PATCH), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); UpdateContactResponseSuccess res = contacts.update(params); assertNotNull(res); - assertEquals(expectedResponse.getId(), res.getId()); - assertEquals(expectedResponse.getObject(), res.getObject()); + assertEquals("123", res.getId()); + assertEquals("contact", res.getObject()); } @Test public void testGetContactById_Success() throws ResendException { GetContactOptions params = GetContactOptions.builder() .id("e169aa45-1ecf-4183-9955-b1499d5701d3") - .audienceId("78261eea-8f8b-4381-83c6-79fa7120f1cf") .build(); - GetContactResponseSuccess expected = ContactsUtil.getContactResponseSuccess(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, GET_CONTACT_JSON, true); - when(contacts.get(params)).thenReturn(expected); + when(httpClient.perform(eq("/contacts/e169aa45-1ecf-4183-9955-b1499d5701d3"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); GetContactResponseSuccess res = contacts.get(params); assertNotNull(res); - assertEquals(expected, res); - verify(contacts, times(1)).get(params); + assertEquals("123", res.getId()); + assertEquals("user@example.com", res.getEmail()); } @Test public void testGetContactByEmail_Success() throws ResendException { GetContactOptions params = GetContactOptions.builder() .email("user@example.com") - .audienceId("78261eea-8f8b-4381-83c6-79fa7120f1cf") .build(); - GetContactResponseSuccess expected = ContactsUtil.getContactResponseSuccess(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, GET_CONTACT_JSON, true); - when(contacts.get(params)).thenReturn(expected); + when(httpClient.perform(eq("/contacts/user@example.com"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); GetContactResponseSuccess res = contacts.get(params); assertNotNull(res); - assertEquals(expected, res); - verify(contacts, times(1)).get(params); + assertEquals("123", res.getId()); + assertEquals("user@example.com", res.getEmail()); } - // Global contacts tests (without segment ID) - @Test public void testCreateGlobalContact_Success() throws ResendException { - CreateContactResponseSuccess expectedContact = ContactsUtil.createContactResponseSuccess(); CreateContactOptions param = CreateContactOptions.builder() .email("user@example.com") .firstName("John") .lastName("Doe") .build(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, CREATE_CONTACT_JSON, true); - when(contacts.create(param)) - .thenReturn(expectedContact); + when(httpClient.perform(eq("/contacts"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); - CreateContactResponseSuccess createdContact = contacts.create(param); + CreateContactResponseSuccess response = contacts.create(param); - assertEquals(createdContact, expectedContact); - verify(contacts, times(1)).create(param); + assertNotNull(response); + assertEquals("123", response.getId()); + assertEquals("contact", response.getObject()); } @Test public void testListGlobalContacts_Success() throws ResendException { - ListContactsResponseSuccess expectedResponse = ContactsUtil.createContactsListResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_CONTACTS_JSON, true); - when(contacts.list()) - .thenReturn(expectedResponse); + when(httpClient.perform(eq("/contacts"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListContactsResponseSuccess res = contacts.list(); assertNotNull(res); - assertEquals(expectedResponse.getData().size(), res.getData().size()); - assertEquals(expectedResponse.getObject(), res.getObject()); + assertEquals(3, res.getData().size()); + assertEquals("list", res.getObject()); } @Test public void testListGlobalContactsWithPagination_Success() throws ResendException { - ListParams params = ListParams.builder() - .limit(3).build(); - - ListContactsResponseSuccess expectedResponse = ContactsUtil.createContactsListResponse(); + ListParams params = ListParams.builder().limit(3).build(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_CONTACTS_JSON, true); - when(contacts.list(params)) - .thenReturn(expectedResponse); + when(httpClient.perform(anyString(), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListContactsResponseSuccess res = contacts.list(params); assertNotNull(res); - assertEquals(params.getLimit(), res.getData().size()); - assertEquals(expectedResponse.getObject(), res.getObject()); + assertEquals(3, res.getData().size()); + assertEquals("list", res.getObject()); } @Test public void testGetGlobalContactById_Success() throws ResendException { String contactId = "e169aa45-1ecf-4183-9955-b1499d5701d3"; - GetContactResponseSuccess expected = ContactsUtil.getContactResponseSuccess(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, GET_CONTACT_JSON, true); - when(contacts.get(contactId)).thenReturn(expected); + when(httpClient.perform(eq("/contacts/" + contactId), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); GetContactResponseSuccess res = contacts.get(contactId); assertNotNull(res); - assertEquals(expected, res); - verify(contacts, times(1)).get(contactId); + assertEquals("123", res.getId()); + assertEquals("user@example.com", res.getEmail()); } @Test public void testGetGlobalContactByEmail_Success() throws ResendException { String contactEmail = "user@example.com"; - GetContactResponseSuccess expected = ContactsUtil.getContactResponseSuccess(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, GET_CONTACT_JSON, true); - when(contacts.get(contactEmail)).thenReturn(expected); + when(httpClient.perform(eq("/contacts/" + contactEmail), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); GetContactResponseSuccess res = contacts.get(contactEmail); assertNotNull(res); - assertEquals(expected, res); - verify(contacts, times(1)).get(contactEmail); + assertEquals("123", res.getId()); + assertEquals("user@example.com", res.getEmail()); } @Test public void testRemoveGlobalContactById_Success() throws ResendException { String contactId = "e169aa45-1ecf-4183-9955-b1499d5701d3"; - RemoveContactResponseSuccess removed = ContactsUtil.removeContactResponseSuccess(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, REMOVE_CONTACT_JSON, true); - when(contacts.remove(contactId)) - .thenReturn(removed); + when(httpClient.perform(eq("/contacts/" + contactId), anyString(), eq(HttpMethod.DELETE), eq(""), isNull())) + .thenReturn(httpResponse); RemoveContactResponseSuccess res = contacts.remove(contactId); - assertEquals(removed, res); - verify(contacts, times(1)).remove(contactId); + assertNotNull(res); + assertEquals("123", res.getId()); } @Test @@ -310,16 +347,16 @@ public void testUpdateGlobalContact_Success() throws ResendException { .firstName("Jane") .lastName("Smith") .build(); - UpdateContactResponseSuccess expectedResponse = ContactsUtil.updateContactResponseSuccess(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, UPDATE_CONTACT_JSON, true); - when(contacts.update(params)) - .thenReturn(expectedResponse); + when(httpClient.perform(eq("/contacts/e169aa45-1ecf-4183-9955-b1499d5701d3"), anyString(), eq(HttpMethod.PATCH), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); UpdateContactResponseSuccess res = contacts.update(params); assertNotNull(res); - assertEquals(expectedResponse.getId(), res.getId()); - assertEquals(expectedResponse.getObject(), res.getObject()); + assertEquals("123", res.getId()); + assertEquals("contact", res.getObject()); } @Test @@ -327,14 +364,14 @@ public void testGetContactWithoutSegmentId_Success() throws ResendException { GetContactOptions params = GetContactOptions.builder() .id("e169aa45-1ecf-4183-9955-b1499d5701d3") .build(); - GetContactResponseSuccess expected = ContactsUtil.getContactResponseSuccess(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, GET_CONTACT_JSON, true); - when(contacts.get(params)).thenReturn(expected); + when(httpClient.perform(eq("/contacts/e169aa45-1ecf-4183-9955-b1499d5701d3"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); GetContactResponseSuccess res = contacts.get(params); assertNotNull(res); - assertEquals(expected, res); - verify(contacts, times(1)).get(params); + assertEquals("123", res.getId()); } } diff --git a/src/test/java/com/resend/services/domains/DomainsTest.java b/src/test/java/com/resend/services/domains/DomainsTest.java index 492ad1f..cf8cc5c 100644 --- a/src/test/java/com/resend/services/domains/DomainsTest.java +++ b/src/test/java/com/resend/services/domains/DomainsTest.java @@ -1,131 +1,232 @@ package com.resend.services.domains; import com.resend.core.exception.ResendException; -import com.resend.core.net.ListParams; -import com.resend.services.domains.dto.DomainDTO; +import com.resend.core.net.AbstractHttpResponse; +import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.services.domains.model.*; import com.resend.services.util.DomainsUtil; +import okhttp3.MediaType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.Mockito.mock; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.when; +@SuppressWarnings("unchecked") public class DomainsTest { + private static final String DOMAIN_ID = "2c64b27c-6237-4626-85d2-a0a8b5832070"; + + private static final String CREATE_RESPONSE_JSON = + "{\"id\":\"" + DOMAIN_ID + "\",\"name\":\"resend.dev\"," + + "\"created_at\":\"2023-04-08T00:11:13.110779+00:00\",\"status\":\"Active\"," + + "\"region\":\"us-east-1\",\"dnsProvider\":\"DNSProviderXYZ\",\"records\":[]}"; + + private static final String GET_RESPONSE_JSON = + "{\"id\":\"" + DOMAIN_ID + "\",\"name\":\"resend.dev\"," + + "\"created_at\":\"2023-04-08T00:11:13.110779+00:00\",\"status\":\"Active\"," + + "\"region\":\"us-east-1\",\"dnsProvider\":\"DNSProviderXYZ\",\"object\":\"domain\",\"records\":[]}"; + + private static final String VERIFY_RESPONSE_JSON = + "{\"object\":\"domain\",\"id\":\"" + DOMAIN_ID + "\"}"; + + private static final String REMOVE_RESPONSE_JSON = + "{\"object\":\"domain\",\"id\":\"" + DOMAIN_ID + "\",\"deleted\":true}"; + + private static final String UPDATE_RESPONSE_JSON = + "{\"id\":\"" + DOMAIN_ID + "\",\"object\":\"domain\"}"; + + private static final String LIST_RESPONSE_JSON = "{\"data\":[" + + "{\"id\":\"id-1\",\"name\":\"resend.dev\",\"created_at\":\"2023-04-08T00:11:13.110Z\"," + + "\"status\":\"Active\",\"region\":\"us-east-1\"}," + + "{\"id\":\"id-2\",\"name\":\"example.com\",\"created_at\":\"2023-01-01T12:00:00.000Z\"," + + "\"status\":\"Active\",\"region\":\"us-east-2\"}," + + "{\"id\":\"id-3\",\"name\":\"another.com\",\"created_at\":\"2023-02-15T08:30:00.000Z\"," + + "\"status\":\"Inactive\",\"region\":\"us-west-1\"}" + + "],\"has_more\":true,\"object\":\"list\"}"; + @Mock + private IHttpClient httpClient; + private Domains domains; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); - domains = mock(Domains.class); + domains = new Domains("test-api-key", httpClient); } @Test public void testCreateDomain_Success() throws ResendException { - CreateDomainResponse expectedDomain = DomainsUtil.createDomainResponse(); - CreateDomainOptions request = DomainsUtil.createDomainRequest(); - when(domains.create(request)) - .thenReturn(expectedDomain); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, CREATE_RESPONSE_JSON, true); + + when(httpClient.perform(eq("/domains"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); CreateDomainResponse response = domains.create(request); assertNotNull(response); - assertEquals(expectedDomain.getId(), response.getId()); + assertEquals(DOMAIN_ID, response.getId()); + assertEquals("resend.dev", response.getName()); + assertEquals("Active", response.getStatus()); + } + + @Test + public void testCreateDomain_ApiError_ThrowsResendException() throws ResendException { + CreateDomainOptions request = DomainsUtil.createDomainRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(422, + "{\"name\":\"validation_error\",\"message\":\"Invalid domain\"}", false); + + when(httpClient.perform(eq("/domains"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); + + ResendException ex = assertThrows(ResendException.class, () -> domains.create(request)); + assertEquals(422, (int) ex.getStatusCode()); } @Test public void testRetrieveDomain_Success() throws ResendException { - Domain expectedDomain = DomainsUtil.createDomain(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, GET_RESPONSE_JSON, true); - when(domains.get(expectedDomain.getId())) - .thenReturn(expectedDomain); + when(httpClient.perform(eq("/domains/" + DOMAIN_ID), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - Domain response = domains.get(expectedDomain.getId()); + Domain response = domains.get(DOMAIN_ID); assertNotNull(response); - assertEquals(expectedDomain, response); - assertEquals(expectedDomain.getId(), response.getId()); + assertEquals(DOMAIN_ID, response.getId()); + assertEquals("resend.dev", response.getName()); + } + + @Test + public void testRetrieveDomain_ApiError_ThrowsResendException() throws ResendException { + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(404, + "{\"name\":\"not_found\",\"message\":\"Domain not found\"}", false); + + when(httpClient.perform(eq("/domains/" + DOMAIN_ID), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); + + assertThrows(ResendException.class, () -> domains.get(DOMAIN_ID)); } @Test public void testVerifyDomain_Success() throws ResendException { - VerifyDomainResponse expectedResponse = DomainsUtil.verifyDomain(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, VERIFY_RESPONSE_JSON, true); - when(domains.verify(expectedResponse.getId())) - .thenReturn(expectedResponse); + when(httpClient.perform(eq("/domains/" + DOMAIN_ID + "/verify"), anyString(), eq(HttpMethod.POST), eq(""), isNull())) + .thenReturn(httpResponse); - VerifyDomainResponse response = domains.verify(expectedResponse.getId()); + VerifyDomainResponse response = domains.verify(DOMAIN_ID); assertNotNull(response); - assertEquals(expectedResponse, response); - assertEquals(expectedResponse.getId(), response.getId()); + assertEquals(DOMAIN_ID, response.getId()); + assertEquals("domain", response.getObject()); + } + + @Test + public void testVerifyDomain_ApiError_ThrowsResendException() throws ResendException { + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(422, + "{\"name\":\"validation_error\",\"message\":\"Domain already verified\"}", false); + + when(httpClient.perform(eq("/domains/" + DOMAIN_ID + "/verify"), anyString(), eq(HttpMethod.POST), eq(""), isNull())) + .thenReturn(httpResponse); + + assertThrows(ResendException.class, () -> domains.verify(DOMAIN_ID)); } @Test public void testDeleteDomain_Success() throws ResendException { - RemoveDomainResponse expectedResponse = DomainsUtil.deleteDomain(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, REMOVE_RESPONSE_JSON, true); - when(domains.remove(expectedResponse.getId())) - .thenReturn(expectedResponse); + when(httpClient.perform(eq("/domains/" + DOMAIN_ID), anyString(), eq(HttpMethod.DELETE), eq(""), isNull())) + .thenReturn(httpResponse); - RemoveDomainResponse response = domains.remove(expectedResponse.getId()); + RemoveDomainResponse response = domains.remove(DOMAIN_ID); assertNotNull(response); - assertEquals(expectedResponse, response); - assertEquals(expectedResponse.getId(), response.getId()); + assertEquals(DOMAIN_ID, response.getId()); + assertTrue(response.isDeleted()); + } + + @Test + public void testDeleteDomain_ApiError_ThrowsResendException() throws ResendException { + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(404, + "{\"name\":\"not_found\",\"message\":\"Domain not found\"}", false); + + when(httpClient.perform(eq("/domains/" + DOMAIN_ID), anyString(), eq(HttpMethod.DELETE), eq(""), isNull())) + .thenReturn(httpResponse); + + assertThrows(ResendException.class, () -> domains.remove(DOMAIN_ID)); } @Test public void testUpdateDomain_Success() throws ResendException { - UpdateDomainResponseSuccess expectedResponse = DomainsUtil.updateDomain(); - UpdateDomainOptions params = DomainsUtil.updateDomainRequest(); + UpdateDomainOptions request = DomainsUtil.updateDomainRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, UPDATE_RESPONSE_JSON, true); - when(domains.update(params)) - .thenReturn(expectedResponse); + when(httpClient.perform(eq("/domains/" + DOMAIN_ID), anyString(), eq(HttpMethod.PATCH), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); - UpdateDomainResponseSuccess response = domains.update(params); + UpdateDomainResponseSuccess response = domains.update(request); assertNotNull(response); - assertEquals(expectedResponse, response); - assertEquals(expectedResponse.getId(), response.getId()); + assertEquals(DOMAIN_ID, response.getId()); + assertEquals("domain", response.getObject()); } @Test - public void testListDomains_Success() throws ResendException { - ListDomainsResponse expectedResponse = DomainsUtil.createDomainListResponse(); + public void testUpdateDomain_ApiError_ThrowsResendException() throws ResendException { + UpdateDomainOptions request = DomainsUtil.updateDomainRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(422, + "{\"name\":\"validation_error\",\"message\":\"Invalid update\"}", false); - when(domains.list()).thenReturn(expectedResponse); + when(httpClient.perform(eq("/domains/" + DOMAIN_ID), anyString(), eq(HttpMethod.PATCH), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); + assertThrows(ResendException.class, () -> domains.update(request)); + } + + @Test + public void testListDomains_Success() throws ResendException { + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_RESPONSE_JSON, true); + + when(httpClient.perform(eq("/domains"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListDomainsResponse response = domains.list(); assertNotNull(response); - assertEquals(expectedResponse.getData().size(), response.getData().size()); + assertEquals(3, response.getData().size()); + assertEquals("id-1", response.getData().get(0).getId()); + } + + @Test + public void testListDomains_ApiError_ThrowsResendException() throws ResendException { + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(401, + "{\"name\":\"unauthorized\",\"message\":\"Unauthorized\"}", false); + + when(httpClient.perform(eq("/domains"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); + + assertThrows(ResendException.class, () -> domains.list()); } @Test public void testListDomainsWithPagination_Success() throws ResendException { - // Arrange - ListParams params = ListParams.builder().limit(2).build(); - ListDomainsResponse expectedResponse = DomainsUtil.createDomainListResponse(); - List paginatedData = expectedResponse.getData().subList(0, params.getLimit()); - ListDomainsResponse paginatedResponse = new ListDomainsResponse(paginatedData, true, "list"); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_RESPONSE_JSON, true); - when(domains.list(params)).thenReturn(paginatedResponse); + when(httpClient.perform(eq("/domains?limit=2"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); + com.resend.core.net.ListParams params = com.resend.core.net.ListParams.builder().limit(2).build(); ListDomainsResponse response = domains.list(params); assertNotNull(response); - assertEquals(params.getLimit(), response.getData().size()); - + assertEquals(3, response.getData().size()); } } diff --git a/src/test/java/com/resend/services/emails/EmailsTest.java b/src/test/java/com/resend/services/emails/EmailsTest.java index 81bb38d..c638186 100644 --- a/src/test/java/com/resend/services/emails/EmailsTest.java +++ b/src/test/java/com/resend/services/emails/EmailsTest.java @@ -1,222 +1,230 @@ package com.resend.services.emails; + import com.resend.core.exception.ResendException; +import com.resend.core.net.AbstractHttpResponse; +import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.core.net.RequestOptions; -import com.resend.services.batch.Batch; -import com.resend.services.batch.model.AbstractBatchEmailsResponse; -import com.resend.services.batch.model.CreateBatchEmailsResponse; import com.resend.services.emails.model.*; import com.resend.services.util.EmailsUtil; +import okhttp3.MediaType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.when; +@SuppressWarnings("unchecked") public class EmailsTest { - @Mock - private Emails emails; + + private static final String EMAIL_ID = "qwert"; + private static final String UPDATE_EMAIL_ID = "123"; + private static final String ATTACHMENT_ID = "2a0c9ce0-3112-4728-976e-47ddcd16a318"; + private static final String ATTACHMENT_EMAIL_ID = "4ef9a417-02e9-4d39-ad75-9611e0fcc33c"; + + private static final String SEND_RESPONSE_JSON = "{\"id\":\"mock_id\"}"; + + private static final String GET_EMAIL_JSON = + "{\"object\":\"email_object\",\"id\":\"" + EMAIL_ID + "\"," + + "\"from\":\"sender@example.com\",\"to\":[\"recipient@example.com\"]," + + "\"created_at\":\"2023-04-08T00:11:13.110779+00:00\"," + + "\"subject\":\"Test Email Subject\"," + + "\"html\":\"This is the HTML content\"," + + "\"text\":\"This is the plain text content\"," + + "\"bcc\":[\"bcc@example.com\"],\"cc\":[\"cc@example.com\"]," + + "\"reply_to\":[\"replyto@example.com\"],\"last_event\":\"last_event_status\"}"; + + private static final String UPDATE_RESPONSE_JSON = "{\"id\":\"" + UPDATE_EMAIL_ID + "\",\"object\":\"emails\"}"; + + private static final String CANCEL_RESPONSE_JSON = "{\"id\":\"" + UPDATE_EMAIL_ID + "\",\"object\":\"emails\"}"; + + private static final String LIST_RESPONSE_JSON = + "{\"object\":\"emails\",\"has_more\":true,\"data\":[" + + "{\"id\":\"email_1\",\"from\":\"sender1@example.com\"}," + + "{\"id\":\"email_2\",\"from\":\"sender2@example.com\"}," + + "{\"id\":\"email_3\",\"from\":\"sender3@example.com\"}" + + "]}"; + + private static final String ATTACHMENT_RESPONSE_JSON = + "{\"object\":\"attachment\",\"id\":\"" + ATTACHMENT_ID + "\"," + + "\"filename\":\"avatar.png\",\"size\":4096,\"content_type\":\"image/png\"," + + "\"download_url\":\"https://outbound-cdn.resend.com/attachments/" + ATTACHMENT_ID + "\"}"; + + private static final String LIST_ATTACHMENTS_JSON = + "{\"object\":\"list\",\"has_more\":false,\"data\":[" + + "{\"object\":\"attachment\",\"id\":\"" + ATTACHMENT_ID + "\",\"filename\":\"avatar.png\",\"size\":4096,\"content_type\":\"image/png\"}," + + "{\"object\":\"attachment\",\"id\":\"3b0d9ce0-4223-5839-087f-58eede27b429\",\"filename\":\"invoice.pdf\",\"size\":8192,\"content_type\":\"application/pdf\"}" + + "]}"; @Mock - private Batch batch; + private IHttpClient httpClient; + private Emails emails; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); - emails = mock(Emails.class); + emails = new Emails("test-api-key", httpClient); } @Test - public void testRetrieveEmail_Success() throws ResendException { - Email expectedEmail = EmailsUtil.createTestEmail(); - - when(emails.get(expectedEmail.getId())) - .thenReturn(expectedEmail); + public void testSendEmail_Success() throws ResendException { + CreateEmailOptions createEmailOptions = EmailsUtil.createEmailOptions(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, SEND_RESPONSE_JSON, true); - Email retrievedEmail = emails.get(expectedEmail.getId()); + when(httpClient.perform(eq("/emails"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); + CreateEmailResponse response = emails.send(createEmailOptions); - assertEquals(expectedEmail, retrievedEmail); - verify(emails, times(1)).get(expectedEmail.getId()); + assertNotNull(response); + assertEquals("mock_id", response.getId()); } @Test - public void testSendEmail_Success() throws ResendException { + public void testSendEmail_ApiError_ThrowsResendException() throws ResendException { CreateEmailOptions createEmailOptions = EmailsUtil.createEmailOptions(); - CreateEmailResponse expectedRes = EmailsUtil.createSendEmailResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(422, + "{\"name\":\"validation_error\",\"message\":\"Invalid recipient\"}", false); - when(emails.send(createEmailOptions)).thenReturn(expectedRes); + when(httpClient.perform(eq("/emails"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); - CreateEmailResponse createEmailResponse = emails.send(createEmailOptions); - - assertNotNull(createEmailResponse); + ResendException ex = assertThrows(ResendException.class, () -> emails.send(createEmailOptions)); + assertEquals(422, (int) ex.getStatusCode()); } @Test public void testSendEmail_WithIdempotencyKey_Success() throws ResendException { - CreateEmailResponse expectedResponse = EmailsUtil.createSendEmailResponse(); - CreateEmailOptions createOptions = EmailsUtil.createEmailOptions(); + CreateEmailOptions createEmailOptions = EmailsUtil.createEmailOptions(); RequestOptions requestOptions = EmailsUtil.createRequestOptions(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, SEND_RESPONSE_JSON, true); - when(emails.send(createOptions, requestOptions)) - .thenReturn(expectedResponse); + when(httpClient.perform(eq("/emails"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class), any(RequestOptions.class))) + .thenReturn(httpResponse); - CreateEmailResponse response = emails.send(createOptions, requestOptions); + CreateEmailResponse response = emails.send(createEmailOptions, requestOptions); - assertEquals(expectedResponse, response); - verify(emails, times(1)).send(createOptions, requestOptions); - } - - - @Test - public void testCreateBatchEmails_Success() throws ResendException { - List batchEmailsRequest = EmailsUtil.createBatchEmailOptions(); - CreateBatchEmailsResponse expectedRes = EmailsUtil.createBatchEmailsResponse(); - - when(batch.send(batchEmailsRequest)).thenReturn(expectedRes); - - CreateBatchEmailsResponse sendBatchEmailsResponse = batch.send(batchEmailsRequest); - - assertNotNull(sendBatchEmailsResponse); - assertEquals(expectedRes.getData().size(), sendBatchEmailsResponse.getData().size()); - } - - @Test - public void testCreatePermissiveBatchEmails_Success() throws ResendException { - RequestOptions options = RequestOptions.builder() - .add("x-batch-validation", "permissive").build(); - - List batchEmailsRequest = EmailsUtil.createBatchEmailOptions(); - CreateBatchEmailsResponse expectedRes = EmailsUtil.createPermissiveBatchEmailsResponse(); - - when(batch.send(batchEmailsRequest, options)).thenReturn(expectedRes); - - CreateBatchEmailsResponse sendBatchEmailsResponse = batch.send(batchEmailsRequest, options); - - assertNotNull(sendBatchEmailsResponse); - assertEquals(expectedRes.getData().size(), sendBatchEmailsResponse.getData().size()); + assertNotNull(response); + assertEquals("mock_id", response.getId()); } @Test - public void testCreateBatchEmailsWithIdempotencyKey_Success() throws ResendException { - List batchEmailsRequest = EmailsUtil.createBatchEmailOptions(); - CreateBatchEmailsResponse expectedRes = EmailsUtil.createBatchEmailsResponse(); - RequestOptions requestOptions = EmailsUtil.createRequestOptions(); + public void testRetrieveEmail_Success() throws ResendException { + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, GET_EMAIL_JSON, true); - when(batch.send(batchEmailsRequest, requestOptions)).thenReturn(expectedRes); + when(httpClient.perform(eq("/emails/" + EMAIL_ID), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - AbstractBatchEmailsResponse sendBatchEmailsResponse = batch.send(batchEmailsRequest, requestOptions); + Email response = emails.get(EMAIL_ID); - assertNotNull(sendBatchEmailsResponse); - assertEquals(expectedRes.getData().size(), sendBatchEmailsResponse.getData().size()); + assertNotNull(response); + assertEquals(EMAIL_ID, response.getId()); + assertEquals("sender@example.com", response.getFrom()); } @Test public void testUpdateEmail_Success() throws ResendException { UpdateEmailOptions updateEmailOptions = EmailsUtil.updateEmailOptions(); - UpdateEmailResponse expectedRes = EmailsUtil.updateEmailResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, UPDATE_RESPONSE_JSON, true); - when(emails.update("123", updateEmailOptions)).thenReturn(expectedRes); + when(httpClient.perform(eq("/emails/" + UPDATE_EMAIL_ID), anyString(), eq(HttpMethod.PATCH), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); - UpdateEmailResponse updateEmailResponse = emails.update("123", updateEmailOptions); + UpdateEmailResponse response = emails.update(UPDATE_EMAIL_ID, updateEmailOptions); - assertNotNull(updateEmailResponse); + assertNotNull(response); + assertEquals(UPDATE_EMAIL_ID, response.getId()); } @Test public void testCancelEmail_Success() throws ResendException { - CancelEmailResponse expectedRes = EmailsUtil.cancelEmailResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, CANCEL_RESPONSE_JSON, true); - when(emails.cancel("123")).thenReturn(expectedRes); + when(httpClient.perform(eq("/emails/" + UPDATE_EMAIL_ID + "/cancel"), anyString(), eq(HttpMethod.POST), eq(""), any(MediaType.class))) + .thenReturn(httpResponse); - CancelEmailResponse cancelEmailResponse = emails.cancel("123"); + CancelEmailResponse response = emails.cancel(UPDATE_EMAIL_ID); - assertNotNull(cancelEmailResponse); + assertNotNull(response); + assertEquals(UPDATE_EMAIL_ID, response.getId()); } @Test public void testListEmails_Success() throws ResendException { + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_RESPONSE_JSON, true); - ListEmailsResponseSuccess expectedResponse = new ListEmailsResponseSuccess(EmailsUtil.createEmailList(), "emails", true); - - when(emails.list()).thenReturn(expectedResponse); + when(httpClient.perform(eq("/emails"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListEmailsResponseSuccess response = emails.list(); assertNotNull(response); - assertEquals(expectedResponse.getData().size(), response.getData().size()); + assertEquals(3, response.getData().size()); } @Test public void testListEmailsWithPagination_Success() throws ResendException { - ListParams params = ListParams.builder() - .limit(3).build(); - ListEmailsResponseSuccess expectedResponse = new ListEmailsResponseSuccess(EmailsUtil.createEmailList(), "emails", true); + ListParams params = ListParams.builder().limit(3).build(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_RESPONSE_JSON, true); - when(emails.list(params)).thenReturn(expectedResponse); + when(httpClient.perform(startsWith("/emails?"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListEmailsResponseSuccess response = emails.list(params); assertNotNull(response); - assertEquals(params.getLimit(), response.getData().size()); + assertEquals(3, response.getData().size()); } @Test public void testGetAttachment_Success() throws ResendException { - String emailId = "4ef9a417-02e9-4d39-ad75-9611e0fcc33c"; - String attachmentId = "2a0c9ce0-3112-4728-976e-47ddcd16a318"; - AttachmentResponse expectedResponse = EmailsUtil.createAttachmentResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, ATTACHMENT_RESPONSE_JSON, true); - when(emails.getAttachment(emailId, attachmentId)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/emails/" + ATTACHMENT_EMAIL_ID + "/attachments/" + ATTACHMENT_ID), + anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - AttachmentResponse response = emails.getAttachment(emailId, attachmentId); + AttachmentResponse response = emails.getAttachment(ATTACHMENT_EMAIL_ID, ATTACHMENT_ID); assertNotNull(response); - assertEquals(expectedResponse.getId(), response.getId()); - assertEquals(expectedResponse.getFilename(), response.getFilename()); - assertEquals(expectedResponse.getSize(), response.getSize()); - assertEquals(expectedResponse.getContentType(), response.getContentType()); - assertEquals(expectedResponse.getDownloadUrl(), response.getDownloadUrl()); - verify(emails, times(1)).getAttachment(emailId, attachmentId); + assertEquals(ATTACHMENT_ID, response.getId()); + assertEquals("avatar.png", response.getFilename()); + assertEquals(4096, response.getSize()); } @Test public void testListAttachments_Success() throws ResendException { - String emailId = "4ef9a417-02e9-4d39-ad75-9611e0fcc33c"; - ListAttachmentsResponse expectedResponse = EmailsUtil.createListAttachmentsResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_ATTACHMENTS_JSON, true); - when(emails.listAttachments(emailId)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/emails/" + ATTACHMENT_EMAIL_ID + "/attachments"), + anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - ListAttachmentsResponse response = emails.listAttachments(emailId); + ListAttachmentsResponse response = emails.listAttachments(ATTACHMENT_EMAIL_ID); assertNotNull(response); - assertEquals(expectedResponse.getData().size(), response.getData().size()); - assertEquals(expectedResponse.getObject(), response.getObject()); - verify(emails, times(1)).listAttachments(emailId); + assertEquals(2, response.getData().size()); + assertEquals("list", response.getObject()); } @Test public void testListAttachmentsWithPagination_Success() throws ResendException { - String emailId = "4ef9a417-02e9-4d39-ad75-9611e0fcc33c"; - ListParams params = ListParams.builder() - .limit(10) - .build(); - ListAttachmentsResponse expectedResponse = EmailsUtil.createListAttachmentsResponse(); + ListParams params = ListParams.builder().limit(10).build(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_ATTACHMENTS_JSON, true); - when(emails.listAttachments(emailId, params)).thenReturn(expectedResponse); + when(httpClient.perform(startsWith("/emails/" + ATTACHMENT_EMAIL_ID + "/attachments?"), + anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - ListAttachmentsResponse response = emails.listAttachments(emailId, params); + ListAttachmentsResponse response = emails.listAttachments(ATTACHMENT_EMAIL_ID, params); assertNotNull(response); - assertEquals(expectedResponse.getData().size(), response.getData().size()); - assertEquals(expectedResponse.hasMore(), response.hasMore()); - verify(emails, times(1)).listAttachments(emailId, params); + assertEquals(2, response.getData().size()); } @Test @@ -224,8 +232,6 @@ public void testSendEmail_WithTemplate_Success() throws ResendException { Template template = Template.builder() .id("template_123") .addVariable("firstName", "John") - .addVariable("lastName", "Doe") - .addVariable("company", "Acme Corp") .build(); CreateEmailOptions emailWithTemplate = CreateEmailOptions.builder() @@ -235,54 +241,14 @@ public void testSendEmail_WithTemplate_Success() throws ResendException { .template(template) .build(); - CreateEmailResponse expectedResponse = EmailsUtil.createSendEmailResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, SEND_RESPONSE_JSON, true); - when(emails.send(emailWithTemplate)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/emails"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); CreateEmailResponse response = emails.send(emailWithTemplate); assertNotNull(response); - assertEquals(expectedResponse.getId(), response.getId()); - verify(emails, times(1)).send(emailWithTemplate); - } - - @Test - public void testSendBatchEmails_WithTemplate_Success() throws ResendException { - Template template1 = Template.builder() - .id("template_123") - .addVariable("firstName", "John") - .addVariable("company", "Tech Corp") - .build(); - - Template template2 = Template.builder() - .id("template_123") - .addVariable("firstName", "Jane") - .addVariable("company", "Design Studios") - .build(); - - CreateEmailOptions email1 = CreateEmailOptions.builder() - .from("Acme ") - .to("john@example.com") - .subject("Welcome John!") - .template(template1) - .build(); - - CreateEmailOptions email2 = CreateEmailOptions.builder() - .from("Acme ") - .to("jane@example.com") - .subject("Welcome Jane!") - .template(template2) - .build(); - - List batchEmails = java.util.Arrays.asList(email1, email2); - CreateBatchEmailsResponse expectedResponse = EmailsUtil.createBatchEmailsResponse(); - - when(batch.send(batchEmails)).thenReturn(expectedResponse); - - CreateBatchEmailsResponse response = batch.send(batchEmails); - - assertNotNull(response); - assertEquals(expectedResponse.getData().size(), response.getData().size()); - verify(batch, times(1)).send(batchEmails); + assertEquals("mock_id", response.getId()); } } diff --git a/src/test/java/com/resend/services/events/EventsTest.java b/src/test/java/com/resend/services/events/EventsTest.java index 101d348..9da4742 100644 --- a/src/test/java/com/resend/services/events/EventsTest.java +++ b/src/test/java/com/resend/services/events/EventsTest.java @@ -1,140 +1,188 @@ package com.resend.services.events; import com.resend.core.exception.ResendException; +import com.resend.core.net.AbstractHttpResponse; +import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.services.events.model.*; import com.resend.services.util.EventsUtil; +import okhttp3.MediaType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.when; +@SuppressWarnings("unchecked") public class EventsTest { + private static final String EVENT_ID = "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"; + + private static final String CREATE_EVENT_JSON = + "{\"object\":\"event\",\"id\":\"" + EVENT_ID + "\"}"; + + private static final String GET_EVENT_JSON = + "{\"object\":\"event\",\"id\":\"" + EVENT_ID + "\"," + + "\"name\":\"user.signup\"," + + "\"schema\":{\"user_id\":\"string\",\"plan\":\"string\"}," + + "\"created_at\":\"2024-12-01T10:00:00.000Z\",\"updated_at\":\"2024-12-02T10:00:00.000Z\"}"; + + private static final String LIST_EVENTS_JSON = + "{\"object\":\"list\",\"has_more\":false,\"data\":[" + + "{\"id\":\"1\",\"name\":\"user.signup\",\"created_at\":\"2024-12-01T10:00:00.000Z\"}," + + "{\"id\":\"2\",\"name\":\"user.verified\",\"created_at\":\"2024-12-02T10:00:00.000Z\"}," + + "{\"id\":\"3\",\"name\":\"order.created\",\"created_at\":\"2024-12-03T10:00:00.000Z\"}" + + "]}"; + + private static final String UPDATE_EVENT_JSON = + "{\"object\":\"event\",\"id\":\"" + EVENT_ID + "\"}"; + + private static final String REMOVE_EVENT_JSON = + "{\"object\":\"event\",\"id\":\"" + EVENT_ID + "\",\"deleted\":true}"; + + private static final String SEND_EVENT_JSON = + "{\"object\":\"event\",\"event\":\"user.signup\"}"; + @Mock + private IHttpClient httpClient; + private Events events; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); - events = mock(Events.class); + events = new Events("test-api-key", httpClient); } @Test public void testCreateEvent_Success() throws ResendException { - CreateEventResponseSuccess expectedResponse = EventsUtil.createEventResponse(); CreateEventOptions createOptions = EventsUtil.createEventRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, CREATE_EVENT_JSON, true); - when(events.create(createOptions)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/events"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); CreateEventResponseSuccess response = events.create(createOptions); - assertEquals(expectedResponse, response); - verify(events, times(1)).create(createOptions); + assertNotNull(response); + assertEquals(EVENT_ID, response.getId()); + assertEquals("event", response.getObject()); + } + + @Test + public void testCreateEvent_ApiError_ThrowsResendException() throws ResendException { + CreateEventOptions createOptions = EventsUtil.createEventRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(422, + "{\"name\":\"validation_error\",\"message\":\"Invalid event\"}", false); + + when(httpClient.perform(eq("/events"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); + + ResendException ex = assertThrows(ResendException.class, () -> events.create(createOptions)); + assertEquals(422, (int) ex.getStatusCode()); } @Test public void testGetEvent_Success() throws ResendException { - String eventId = "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"; - Event expectedResponse = EventsUtil.getEventResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, GET_EVENT_JSON, true); - when(events.get(eventId)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/events/" + EVENT_ID), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - Event response = events.get(eventId); + Event response = events.get(EVENT_ID); assertNotNull(response); - assertEquals(expectedResponse.getId(), response.getId()); - assertEquals(expectedResponse.getName(), response.getName()); - verify(events, times(1)).get(eventId); + assertEquals(EVENT_ID, response.getId()); + assertEquals("user.signup", response.getName()); } @Test public void testGetEventByName_Success() throws ResendException { String eventName = "user.signup"; - Event expectedResponse = EventsUtil.getEventResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, GET_EVENT_JSON, true); - when(events.get(eventName)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/events/" + eventName), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); Event response = events.get(eventName); assertNotNull(response); - assertEquals(expectedResponse.getName(), response.getName()); - verify(events, times(1)).get(eventName); + assertEquals("user.signup", response.getName()); } @Test public void testListEvents_Success() throws ResendException { - ListEventsResponseSuccess expectedResponse = EventsUtil.listEventsResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_EVENTS_JSON, true); - when(events.list()).thenReturn(expectedResponse); + when(httpClient.perform(eq("/events"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListEventsResponseSuccess response = events.list(); assertNotNull(response); - assertEquals(expectedResponse.getData().size(), response.getData().size()); - assertEquals(expectedResponse.getObject(), response.getObject()); - verify(events, times(1)).list(); + assertEquals(3, response.getData().size()); + assertEquals("list", response.getObject()); } @Test public void testListEventsWithPagination_Success() throws ResendException { - ListParams params = ListParams.builder() - .limit(10) - .build(); - - ListEventsResponseSuccess expectedResponse = EventsUtil.listEventsResponse(); + ListParams params = ListParams.builder().limit(10).build(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_EVENTS_JSON, true); - when(events.list(params)).thenReturn(expectedResponse); + when(httpClient.perform(startsWith("/events?"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListEventsResponseSuccess response = events.list(params); assertNotNull(response); - assertEquals(expectedResponse.getObject(), response.getObject()); - verify(events, times(1)).list(params); + assertEquals("list", response.getObject()); } @Test public void testUpdateEvent_Success() throws ResendException { - UpdateEventResponseSuccess expectedResponse = EventsUtil.updateEventResponse(); UpdateEventOptions updateOptions = EventsUtil.updateEventRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, UPDATE_EVENT_JSON, true); - when(events.update(updateOptions)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/events/" + EVENT_ID), anyString(), eq(HttpMethod.PATCH), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); UpdateEventResponseSuccess response = events.update(updateOptions); - assertEquals(expectedResponse, response); - verify(events, times(1)).update(updateOptions); + assertNotNull(response); + assertEquals(EVENT_ID, response.getId()); + assertEquals("event", response.getObject()); } @Test public void testRemoveEvent_Success() throws ResendException { - String eventId = "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"; - RemoveEventResponseSuccess expectedResponse = EventsUtil.removeEventResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, REMOVE_EVENT_JSON, true); - when(events.remove(eventId)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/events/" + EVENT_ID), anyString(), eq(HttpMethod.DELETE), eq(""), isNull())) + .thenReturn(httpResponse); - RemoveEventResponseSuccess response = events.remove(eventId); + RemoveEventResponseSuccess response = events.remove(EVENT_ID); - assertEquals(expectedResponse, response); + assertNotNull(response); assertTrue(response.getDeleted()); - verify(events, times(1)).remove(eventId); + assertEquals(EVENT_ID, response.getId()); } @Test public void testSendEvent_Success() throws ResendException { - SendEventResponseSuccess expectedResponse = EventsUtil.sendEventResponse(); SendEventOptions sendOptions = EventsUtil.sendEventRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, SEND_EVENT_JSON, true); - when(events.send(sendOptions)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/events/send"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); SendEventResponseSuccess response = events.send(sendOptions); - assertEquals(expectedResponse, response); + assertNotNull(response); assertEquals("user.signup", response.getEvent()); - verify(events, times(1)).send(sendOptions); } @Test @@ -143,14 +191,14 @@ public void testSendEventWithContactId_Success() throws ResendException { .event("user.signup") .contactId("contact_123") .build(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, SEND_EVENT_JSON, true); - SendEventResponseSuccess expectedResponse = EventsUtil.sendEventResponse(); - - when(events.send(sendOptions)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/events/send"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); SendEventResponseSuccess response = events.send(sendOptions); - assertEquals(expectedResponse, response); - verify(events, times(1)).send(sendOptions); + assertNotNull(response); + assertEquals("user.signup", response.getEvent()); } } diff --git a/src/test/java/com/resend/services/logs/LogsTest.java b/src/test/java/com/resend/services/logs/LogsTest.java index ef07dcb..2e30268 100644 --- a/src/test/java/com/resend/services/logs/LogsTest.java +++ b/src/test/java/com/resend/services/logs/LogsTest.java @@ -1,102 +1,144 @@ package com.resend.services.logs; import com.resend.core.exception.ResendException; +import com.resend.core.net.AbstractHttpResponse; +import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.services.logs.model.GetLogResponseSuccess; import com.resend.services.logs.model.ListLogsResponseSuccess; -import com.resend.services.util.LogsUtil; +import okhttp3.MediaType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.when; +@SuppressWarnings("unchecked") public class LogsTest { + private static final String GET_LOG_JSON = + "{\"id\":\"log_123\",\"created_at\":\"2024-01-01T00:00:00.000Z\"," + + "\"endpoint\":\"/emails\",\"method\":\"POST\",\"response_status\":200," + + "\"user_agent\":\"resend-java/4.13.0\",\"object\":\"log\"}"; + + private static final String LIST_LOGS_JSON = + "{\"object\":\"list\",\"has_more\":false,\"data\":[" + + "{\"id\":\"log_1\",\"created_at\":\"2024-01-01T00:00:00.000Z\",\"endpoint\":\"/emails\",\"method\":\"POST\",\"response_status\":200,\"user_agent\":\"resend-java/4.13.0\"}," + + "{\"id\":\"log_2\",\"created_at\":\"2024-01-02T00:00:00.000Z\",\"endpoint\":\"/domains\",\"method\":\"GET\",\"response_status\":200,\"user_agent\":\"resend-java/4.13.0\"}," + + "{\"id\":\"log_3\",\"created_at\":\"2024-01-03T00:00:00.000Z\",\"endpoint\":\"/contacts\",\"method\":\"DELETE\",\"response_status\":204}" + + "]}"; + @Mock + private IHttpClient httpClient; + private Logs logs; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); - logs = mock(Logs.class); + logs = new Logs("test-api-key", httpClient); } @Test public void testGetLog_Success() throws ResendException { String logId = "log_123"; - GetLogResponseSuccess expected = LogsUtil.getLogResponseSuccess(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, GET_LOG_JSON, true); - when(logs.get(logId)).thenReturn(expected); + when(httpClient.perform(eq("/logs/" + logId), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); GetLogResponseSuccess res = logs.get(logId); assertNotNull(res); - assertEquals(expected.getId(), res.getId()); - assertEquals(expected.getEndpoint(), res.getEndpoint()); - assertEquals(expected.getMethod(), res.getMethod()); - assertEquals(expected.getResponseStatus(), res.getResponseStatus()); - assertEquals(expected.getObject(), res.getObject()); - verify(logs, times(1)).get(logId); + assertEquals("log_123", res.getId()); + assertEquals("/emails", res.getEndpoint()); + assertEquals("POST", res.getMethod()); + assertEquals(200, (int) res.getResponseStatus()); + assertEquals("log", res.getObject()); + } + + @Test + public void testGetLog_ApiError_ThrowsResendException() throws ResendException { + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(404, + "{\"name\":\"not_found\",\"message\":\"Log not found\"}", false); + + when(httpClient.perform(eq("/logs/bad_id"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); + + ResendException ex = assertThrows(ResendException.class, () -> logs.get("bad_id")); + assertEquals(404, (int) ex.getStatusCode()); } @Test public void testListLogs_Success() throws ResendException { - ListLogsResponseSuccess expected = LogsUtil.createLogsListResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_LOGS_JSON, true); - when(logs.list()).thenReturn(expected); + when(httpClient.perform(eq("/logs"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListLogsResponseSuccess res = logs.list(); assertNotNull(res); - assertEquals(expected.getData().size(), res.getData().size()); - assertEquals(expected.getObject(), res.getObject()); - verify(logs, times(1)).list(); + assertEquals(3, res.getData().size()); + assertEquals("list", res.getObject()); + } + + @Test + public void testListLogs_ApiError_ThrowsResendException() throws ResendException { + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(500, + "{\"name\":\"internal_server_error\",\"message\":\"Server error\"}", false); + + when(httpClient.perform(eq("/logs"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); + + ResendException ex = assertThrows(ResendException.class, () -> logs.list()); + assertEquals(500, (int) ex.getStatusCode()); } @Test public void testListLogsWithPagination_Success() throws ResendException { ListParams params = ListParams.builder().limit(3).build(); - ListLogsResponseSuccess expected = LogsUtil.createLogsListResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_LOGS_JSON, true); - when(logs.list(params)).thenReturn(expected); + when(httpClient.perform(startsWith("/logs?"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListLogsResponseSuccess res = logs.list(params); assertNotNull(res); - assertEquals(params.getLimit(), res.getData().size()); - assertEquals(expected.getObject(), res.getObject()); - verify(logs, times(1)).list(params); + assertEquals(3, res.getData().size()); + assertEquals("list", res.getObject()); } @Test public void testListLogsWithAfterCursor_Success() throws ResendException { ListParams params = ListParams.builder().after("log_50").build(); - ListLogsResponseSuccess expected = LogsUtil.createLogsListResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_LOGS_JSON, true); - when(logs.list(params)).thenReturn(expected); + when(httpClient.perform(startsWith("/logs?"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListLogsResponseSuccess res = logs.list(params); assertNotNull(res); - assertEquals(expected.getData().size(), res.getData().size()); - verify(logs, times(1)).list(params); + assertEquals(3, res.getData().size()); } @Test public void testListLogsWithBeforeCursor_Success() throws ResendException { ListParams params = ListParams.builder().before("log_100").build(); - ListLogsResponseSuccess expected = LogsUtil.createLogsListResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_LOGS_JSON, true); - when(logs.list(params)).thenReturn(expected); + when(httpClient.perform(startsWith("/logs?"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListLogsResponseSuccess res = logs.list(params); assertNotNull(res); - assertEquals(expected.getData().size(), res.getData().size()); - verify(logs, times(1)).list(params); + assertEquals(3, res.getData().size()); } } diff --git a/src/test/java/com/resend/services/receiving/ReceivingTest.java b/src/test/java/com/resend/services/receiving/ReceivingTest.java index db94955..4aad593 100644 --- a/src/test/java/com/resend/services/receiving/ReceivingTest.java +++ b/src/test/java/com/resend/services/receiving/ReceivingTest.java @@ -1,124 +1,179 @@ package com.resend.services.receiving; import com.resend.core.exception.ResendException; +import com.resend.core.net.AbstractHttpResponse; +import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.services.receiving.model.*; -import com.resend.services.util.ReceivingUtil; +import okhttp3.MediaType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.when; -/** - * Test class for Receiving service. - */ +@SuppressWarnings("unchecked") public class ReceivingTest { + private static final String EMAIL_ID = "4ef9a417-02e9-4d39-ad75-9611e0fcc33c"; + private static final String ATTACHMENT_ID = "2a0c9ce0-3112-4728-976e-47ddcd16a318"; + + private static final String GET_EMAIL_JSON = + "{\"object\":\"email\",\"id\":\"" + EMAIL_ID + "\"," + + "\"to\":[\"delivered@resend.dev\"]," + + "\"from\":\"Acme \"," + + "\"created_at\":\"2023-04-03T22:13:42.674981+00:00\"," + + "\"subject\":\"Hello World\"," + + "\"html\":\"Congrats on sending your first email!\"," + + "\"bcc\":[],\"cc\":[],\"reply_to\":[]," + + "\"received_for\":[\"forwarded@example.com\"]," + + "\"message_id\":\"\"," + + "\"attachments\":[{\"id\":\"" + ATTACHMENT_ID + "\",\"filename\":\"avatar.png\",\"content_type\":\"image/png\",\"content_disposition\":\"inline\",\"content_id\":\"img001\"}]," + + "\"headers\":{\"return-path\":\"lucas.costa@resend.com\",\"mime-version\":\"1.0\"}}"; + + private static final String LIST_EMAILS_JSON = + "{\"object\":\"list\",\"has_more\":true,\"data\":[" + + "{\"id\":\"a39999a6-88e3-48b1-888b-beaabcde1b33\"," + + "\"to\":[\"recipient@example.com\"]," + + "\"from\":\"sender@example.com\"," + + "\"created_at\":\"2025-10-09 14:37:40.951732+00\"," + + "\"subject\":\"Hello World\"," + + "\"bcc\":[],\"cc\":[],\"reply_to\":[]," + + "\"message_id\":\"<111-222-333@email.provider.example.com>\"," + + "\"attachments\":[{\"id\":\"47e999c7-c89c-4999-bf32-aaaaa1c3ff21\",\"filename\":\"example.txt\",\"content_type\":\"text/plain\",\"content_disposition\":\"attachment\",\"size\":13}]}" + + "]}"; + + private static final String GET_ATTACHMENT_JSON = + "{\"object\":\"attachment\",\"id\":\"" + ATTACHMENT_ID + "\"," + + "\"filename\":\"avatar.png\",\"content_type\":\"image/png\"," + + "\"content_disposition\":\"inline\",\"content_id\":\"img001\"," + + "\"download_url\":\"https://inbound-cdn.resend.com/" + EMAIL_ID + "/attachments/" + ATTACHMENT_ID + "?some-params=example&signature=sig-123\"," + + "\"expires_at\":\"2025-10-17T14:29:41.521Z\"}"; + + private static final String LIST_ATTACHMENTS_JSON = + "{\"object\":\"list\",\"has_more\":false,\"data\":[" + + "{\"object\":\"attachment\",\"id\":\"" + ATTACHMENT_ID + "\"," + + "\"filename\":\"avatar.png\",\"content_type\":\"image/png\"," + + "\"content_disposition\":\"inline\",\"content_id\":\"img001\"," + + "\"download_url\":\"https://inbound-cdn.resend.com/" + EMAIL_ID + "/attachments/" + ATTACHMENT_ID + "?some-params=example&signature=sig-123\"," + + "\"expires_at\":\"2025-10-17T14:29:41.521Z\"}" + + "]}"; + @Mock + private IHttpClient httpClient; + private Receiving receiving; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); - receiving = mock(Receiving.class); + receiving = new Receiving("test-api-key", httpClient); } @Test public void testGetReceivedEmail_Success() throws ResendException { - ReceivedEmail expectedEmail = ReceivingUtil.createReceivedEmail(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, GET_EMAIL_JSON, true); - when(receiving.get(expectedEmail.getId())).thenReturn(expectedEmail); + when(httpClient.perform(eq("/emails/receiving/" + EMAIL_ID), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - ReceivedEmail result = receiving.get(expectedEmail.getId()); + ReceivedEmail result = receiving.get(EMAIL_ID); assertNotNull(result); - assertEquals(expectedEmail.getId(), result.getId()); - assertEquals(expectedEmail.getSubject(), result.getSubject()); - assertEquals(expectedEmail.getFrom(), result.getFrom()); - assertEquals(expectedEmail.getMessageId(), result.getMessageId()); - assertEquals(expectedEmail.getReceivedFor(), result.getReceivedFor()); - verify(receiving, times(1)).get(expectedEmail.getId()); + assertEquals(EMAIL_ID, result.getId()); + assertEquals("Hello World", result.getSubject()); + assertEquals("Acme ", result.getFrom()); + assertEquals("", result.getMessageId()); + assertEquals("forwarded@example.com", result.getReceivedFor().get(0)); + } + + @Test + public void testGetReceivedEmail_ApiError_ThrowsResendException() throws ResendException { + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(404, + "{\"name\":\"not_found\",\"message\":\"Email not found\"}", false); + + when(httpClient.perform(eq("/emails/receiving/bad_id"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); + + ResendException ex = assertThrows(ResendException.class, () -> receiving.get("bad_id")); + assertEquals(404, (int) ex.getStatusCode()); } @Test public void testListReceivedEmails_Success() throws ResendException { - ListReceivedEmailsResponse expectedResponse = ReceivingUtil.createListReceivedEmailsResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_EMAILS_JSON, true); - when(receiving.list()).thenReturn(expectedResponse); + when(httpClient.perform(eq("/emails/receiving"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListReceivedEmailsResponse result = receiving.list(); assertNotNull(result); - assertEquals(expectedResponse.getData().size(), result.getData().size()); - assertEquals(expectedResponse.hasMore(), result.hasMore()); + assertEquals(1, result.getData().size()); + assertTrue(result.hasMore()); assertEquals("list", result.getObject()); - verify(receiving, times(1)).list(); } @Test public void testListReceivedEmailsWithPagination_Success() throws ResendException { ListParams params = ListParams.builder().limit(10).build(); - ListReceivedEmailsResponse expectedResponse = ReceivingUtil.createListReceivedEmailsResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_EMAILS_JSON, true); - when(receiving.list(params)).thenReturn(expectedResponse); + when(httpClient.perform(startsWith("/emails/receiving?"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListReceivedEmailsResponse result = receiving.list(params); assertNotNull(result); - assertEquals(expectedResponse.getData().size(), result.getData().size()); - verify(receiving, times(1)).list(params); + assertEquals(1, result.getData().size()); } @Test public void testGetAttachment_Success() throws ResendException { - String emailId = "4ef9a417-02e9-4d39-ad75-9611e0fcc33c"; - String attachmentId = "2a0c9ce0-3112-4728-976e-47ddcd16a318"; - AttachmentDetails expectedDetails = ReceivingUtil.createAttachmentDetails(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, GET_ATTACHMENT_JSON, true); - when(receiving.getAttachment(emailId, attachmentId)).thenReturn(expectedDetails); + when(httpClient.perform(eq("/emails/receiving/" + EMAIL_ID + "/attachments/" + ATTACHMENT_ID), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - AttachmentDetails result = receiving.getAttachment(emailId, attachmentId); + AttachmentDetails result = receiving.getAttachment(EMAIL_ID, ATTACHMENT_ID); assertNotNull(result); - assertEquals(expectedDetails.getId(), result.getId()); - assertEquals(expectedDetails.getFilename(), result.getFilename()); - assertEquals(expectedDetails.getDownloadUrl(), result.getDownloadUrl()); + assertEquals(ATTACHMENT_ID, result.getId()); + assertEquals("avatar.png", result.getFilename()); + assertNotNull(result.getDownloadUrl()); assertEquals("attachment", result.getObject()); - verify(receiving, times(1)).getAttachment(emailId, attachmentId); } @Test public void testListAttachments_Success() throws ResendException { - String emailId = "4ef9a417-02e9-4d39-ad75-9611e0fcc33c"; - ListAttachmentsResponse expectedResponse = ReceivingUtil.createListAttachmentsResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_ATTACHMENTS_JSON, true); - when(receiving.listAttachments(emailId)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/emails/receiving/" + EMAIL_ID + "/attachments"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - ListAttachmentsResponse result = receiving.listAttachments(emailId); + ListAttachmentsResponse result = receiving.listAttachments(EMAIL_ID); assertNotNull(result); - assertEquals(expectedResponse.getData().size(), result.getData().size()); - assertEquals(expectedResponse.hasMore(), result.hasMore()); + assertEquals(1, result.getData().size()); + assertFalse(result.hasMore()); assertEquals("list", result.getObject()); - verify(receiving, times(1)).listAttachments(emailId); } @Test public void testListAttachmentsWithPagination_Success() throws ResendException { - String emailId = "4ef9a417-02e9-4d39-ad75-9611e0fcc33c"; ListParams params = ListParams.builder().limit(5).build(); - ListAttachmentsResponse expectedResponse = ReceivingUtil.createListAttachmentsResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_ATTACHMENTS_JSON, true); - when(receiving.listAttachments(emailId, params)).thenReturn(expectedResponse); + when(httpClient.perform(startsWith("/emails/receiving/" + EMAIL_ID + "/attachments?"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - ListAttachmentsResponse result = receiving.listAttachments(emailId, params); + ListAttachmentsResponse result = receiving.listAttachments(EMAIL_ID, params); assertNotNull(result); - assertEquals(expectedResponse.getData().size(), result.getData().size()); - verify(receiving, times(1)).listAttachments(emailId, params); + assertEquals(1, result.getData().size()); } } diff --git a/src/test/java/com/resend/services/segments/SegmentsTest.java b/src/test/java/com/resend/services/segments/SegmentsTest.java index 2b18042..08cafad 100644 --- a/src/test/java/com/resend/services/segments/SegmentsTest.java +++ b/src/test/java/com/resend/services/segments/SegmentsTest.java @@ -1,102 +1,131 @@ package com.resend.services.segments; import com.resend.core.exception.ResendException; +import com.resend.core.net.AbstractHttpResponse; +import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; -import com.resend.services.segments.model.CreateSegmentOptions; -import com.resend.services.segments.model.CreateSegmentResponseSuccess; -import com.resend.services.segments.model.GetSegmentResponseSuccess; -import com.resend.services.segments.model.ListSegmentsResponseSuccess; -import com.resend.services.segments.model.RemoveSegmentResponseSuccess; - +import com.resend.services.segments.model.*; import com.resend.services.util.SegmentsUtil; +import okhttp3.MediaType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.when; +@SuppressWarnings("unchecked") public class SegmentsTest { + private static final String CREATE_RESPONSE_JSON = "{\"id\":\"123\",\"name\":\"seg\",\"object\":\"audience\"}"; + private static final String REMOVE_RESPONSE_JSON = "{\"id\":\"123\",\"object\":\"audience\",\"deleted\":true}"; + private static final String GET_RESPONSE_JSON = + "{\"id\":\"123\",\"name\":\"seg\",\"created_at\":\"2023-04-08T00:11:13.110779+00:00\",\"object\":\"audience\"}"; + private static final String LIST_RESPONSE_JSON = + "{\"data\":[" + + "{\"id\":\"1\",\"name\":\"test1\",\"created_at\":\"2023-04-08T00:11:13.110779+00:00\"}," + + "{\"id\":\"2\",\"name\":\"test2\",\"created_at\":\"2023-04-08T00:11:13.110779+00:00\"}," + + "{\"id\":\"3\",\"name\":\"test3\",\"created_at\":\"2023-04-08T00:11:13.110779+00:00\"}" + + "],\"object\":\"list\",\"has_more\":true}"; + @Mock + private IHttpClient httpClient; + private Segments segments; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); - segments = mock(Segments.class); + segments = new Segments("test-api-key", httpClient); } @Test public void testCreateSegment_Success() throws ResendException { - CreateSegmentResponseSuccess expectedSegment = SegmentsUtil.createSegmentResponse(); CreateSegmentOptions param = SegmentsUtil.createSegmentRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, CREATE_RESPONSE_JSON, true); - when(segments.create(param)) - .thenReturn(expectedSegment); + when(httpClient.perform(eq("/segments"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); - CreateSegmentResponseSuccess createdSeg = segments.create(param); + CreateSegmentResponseSuccess created = segments.create(param); - assertEquals(createdSeg, expectedSegment); - verify(segments, times(1)).create(param); + assertNotNull(created); + assertEquals("123", created.getId()); + assertEquals("seg", created.getName()); + assertEquals("audience", created.getObject()); + } + + @Test + public void testCreateSegment_ApiError_ThrowsResendException() throws ResendException { + CreateSegmentOptions param = SegmentsUtil.createSegmentRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(422, "{\"name\":\"validation_error\",\"message\":\"Invalid\"}", false); + + when(httpClient.perform(eq("/segments"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); + + ResendException ex = assertThrows(ResendException.class, () -> segments.create(param)); + assertEquals(422, (int) ex.getStatusCode()); } @Test public void testDeleteSegment_Success() throws ResendException { String segmentId = "123"; - RemoveSegmentResponseSuccess removed = SegmentsUtil.removeSegmentsResponseSuccess(); - when(segments.remove(segmentId)) - .thenReturn(removed); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, REMOVE_RESPONSE_JSON, true); + + when(httpClient.perform(eq("/segments/" + segmentId), anyString(), eq(HttpMethod.DELETE), eq(""), isNull())) + .thenReturn(httpResponse); RemoveSegmentResponseSuccess res = segments.remove(segmentId); - assertEquals(removed, res); + assertNotNull(res); + assertEquals("123", res.getId()); + assertTrue(res.getDeleted()); } @Test public void testListSegments_Success() throws ResendException { - ListSegmentsResponseSuccess expectedResponse = SegmentsUtil.createSegmentsListResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_RESPONSE_JSON, true); - when(segments.list()) - .thenReturn(expectedResponse); + when(httpClient.perform(eq("/segments"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListSegmentsResponseSuccess res = segments.list(); assertNotNull(res); - assertEquals(expectedResponse.getData().size(), res.getData().size()); - assertEquals(expectedResponse.getObject(), res.getObject()); + assertEquals(3, res.getData().size()); + assertEquals("list", res.getObject()); } @Test - public void testListSegmentWithPagination_Success() throws ResendException { - ListParams params = ListParams.builder() - .limit(3).build(); - ListSegmentsResponseSuccess expectedResponse = SegmentsUtil.createSegmentsListResponse(); + public void testListSegmentsWithPagination_Success() throws ResendException { + ListParams params = ListParams.builder().limit(3).build(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_RESPONSE_JSON, true); - when(segments.list(params)) - .thenReturn(expectedResponse); + when(httpClient.perform(startsWith("/audiences?"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListSegmentsResponseSuccess res = segments.list(params); assertNotNull(res); - assertEquals(params.getLimit(), res.getData().size()); - assertEquals(expectedResponse.getObject(), res.getObject()); + assertEquals(3, res.getData().size()); + assertEquals("list", res.getObject()); } @Test public void testGetSegment_Success() throws ResendException { String segmentId = "123"; - GetSegmentResponseSuccess expected = SegmentsUtil.getSegmentResponseSuccess(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, GET_RESPONSE_JSON, true); - when(segments.get(segmentId)) - .thenReturn(expected); + when(httpClient.perform(eq("/segments/" + segmentId), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); GetSegmentResponseSuccess res = segments.get(segmentId); assertNotNull(res); - assertEquals(expected.getId(), res.getId()); - assertEquals(expected.getName(), res.getName()); + assertEquals("123", res.getId()); + assertEquals("seg", res.getName()); } } diff --git a/src/test/java/com/resend/services/templates/TemplatesTest.java b/src/test/java/com/resend/services/templates/TemplatesTest.java index c85e0b1..3451207 100644 --- a/src/test/java/com/resend/services/templates/TemplatesTest.java +++ b/src/test/java/com/resend/services/templates/TemplatesTest.java @@ -1,166 +1,195 @@ package com.resend.services.templates; import com.resend.core.exception.ResendException; +import com.resend.core.net.AbstractHttpResponse; +import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.services.templates.model.*; import com.resend.services.util.TemplatesUtil; +import okhttp3.MediaType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.when; -/** - * Unit tests for the Templates service. - */ +@SuppressWarnings("unchecked") public class TemplatesTest { + private static final String TEMPLATE_ID = "34a080c9-b17d-4187-ad80-5af20266e535"; + private static final String CREATE_RESPONSE_JSON = "{\"id\":\"49a3999c-0ce1-4ea6-ab68-afcd6dc2e794\",\"object\":\"template\"}"; + private static final String GET_RESPONSE_JSON = + "{\"id\":\"" + TEMPLATE_ID + "\",\"object\":\"template\",\"alias\":\"reset-password\"," + + "\"name\":\"reset-password\",\"status\":\"published\"," + + "\"created_at\":\"2023-10-06T23:47:56.678Z\",\"updated_at\":\"2023-10-06T23:47:56.678Z\"," + + "\"published_at\":\"2023-10-06T23:47:56.678Z\"," + + "\"from\":\"John Doe \",\"subject\":\"Hello, world!\"," + + "\"html\":\"

Hello, world!

\",\"text\":\"Hello, world!\",\"variables\":[]}"; + private static final String LIST_RESPONSE_JSON = + "{\"object\":\"list\",\"has_more\":false,\"data\":[" + + "{\"id\":\"e169aa45-1ecf-4183-9955-b1499d5701d3\",\"name\":\"reset-password\",\"status\":\"draft\"," + + "\"alias\":\"reset-password\",\"created_at\":\"2023-10-06T23:47:56.678Z\",\"updated_at\":\"2023-10-06T23:47:56.678Z\"}," + + "{\"id\":\"b7f9c2e1-1234-4abc-9def-567890abcdef\",\"name\":\"welcome-message\",\"status\":\"published\"," + + "\"alias\":\"welcome-message\",\"created_at\":\"2023-10-06T23:47:56.678Z\",\"updated_at\":\"2023-10-06T23:47:56.678Z\"}" + + "]}"; + private static final String UPDATE_RESPONSE_JSON = "{\"id\":\"" + TEMPLATE_ID + "\",\"object\":\"template\"}"; + private static final String DELETE_RESPONSE_JSON = "{\"object\":\"template\",\"id\":\"" + TEMPLATE_ID + "\",\"deleted\":true}"; + private static final String DUPLICATE_RESPONSE_JSON = "{\"object\":\"template\",\"id\":\"e169aa45-1ecf-4183-9955-b1499d5701d3\"}"; + private static final String PUBLISH_RESPONSE_JSON = "{\"id\":\"" + TEMPLATE_ID + "\",\"object\":\"template\"}"; + @Mock + private IHttpClient httpClient; + private Templates templates; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); - templates = mock(Templates.class); + templates = new Templates("test-api-key", httpClient); } @Test public void testCreateTemplate_Success() throws ResendException { CreateTemplateOptions createOptions = TemplatesUtil.createTemplateOptions(); - CreateTemplateResponseSuccess expectedResponse = TemplatesUtil.createTemplateResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, CREATE_RESPONSE_JSON, true); - when(templates.create(createOptions)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/templates"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); CreateTemplateResponseSuccess response = templates.create(createOptions); assertNotNull(response); - assertEquals(expectedResponse.getId(), response.getId()); - assertEquals(expectedResponse.getObject(), response.getObject()); - verify(templates, times(1)).create(createOptions); + assertEquals("49a3999c-0ce1-4ea6-ab68-afcd6dc2e794", response.getId()); + assertEquals("template", response.getObject()); + } + + @Test + public void testCreateTemplate_ApiError_ThrowsResendException() throws ResendException { + CreateTemplateOptions createOptions = TemplatesUtil.createTemplateOptions(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(422, "{\"name\":\"validation_error\",\"message\":\"Invalid\"}", false); + + when(httpClient.perform(eq("/templates"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); + + ResendException ex = assertThrows(ResendException.class, () -> templates.create(createOptions)); + assertEquals(422, (int) ex.getStatusCode()); } @Test public void testGetTemplate_Success() throws ResendException { - String templateId = "34a080c9-b17d-4187-ad80-5af20266e535"; - GetTemplateResponseSuccess expectedTemplate = TemplatesUtil.createTemplate(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, GET_RESPONSE_JSON, true); - when(templates.get(templateId)).thenReturn(expectedTemplate); + when(httpClient.perform(eq("/templates/" + TEMPLATE_ID), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - GetTemplateResponseSuccess retrievedTemplate = templates.get(templateId); + GetTemplateResponseSuccess response = templates.get(TEMPLATE_ID); - assertNotNull(retrievedTemplate); - assertEquals(expectedTemplate.getId(), retrievedTemplate.getId()); - assertEquals(expectedTemplate.getName(), retrievedTemplate.getName()); - assertEquals(expectedTemplate.getStatus(), retrievedTemplate.getStatus()); - verify(templates, times(1)).get(templateId); + assertNotNull(response); + assertEquals(TEMPLATE_ID, response.getId()); + assertEquals("reset-password", response.getName()); + assertEquals("published", response.getStatus()); } @Test public void testGetTemplateByAlias_Success() throws ResendException { String alias = "reset-password"; - GetTemplateResponseSuccess expectedTemplate = TemplatesUtil.createTemplate(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, GET_RESPONSE_JSON, true); - when(templates.get(alias)).thenReturn(expectedTemplate); + when(httpClient.perform(eq("/templates/" + alias), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - GetTemplateResponseSuccess retrievedTemplate = templates.get(alias); + GetTemplateResponseSuccess response = templates.get(alias); - assertNotNull(retrievedTemplate); - assertEquals(expectedTemplate.getAlias(), retrievedTemplate.getAlias()); - verify(templates, times(1)).get(alias); + assertNotNull(response); + assertEquals("reset-password", response.getAlias()); } @Test public void testListTemplates_Success() throws ResendException { - ListTemplatesResponseSuccess expectedResponse = TemplatesUtil.listTemplatesResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_RESPONSE_JSON, true); - when(templates.list()).thenReturn(expectedResponse); + when(httpClient.perform(eq("/templates"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListTemplatesResponseSuccess response = templates.list(); assertNotNull(response); - assertEquals(expectedResponse.getData().size(), response.getData().size()); - assertEquals(expectedResponse.getHasMore(), response.getHasMore()); - verify(templates, times(1)).list(); + assertEquals(2, response.getData().size()); + assertEquals(false, response.getHasMore()); } @Test public void testListTemplatesWithPagination_Success() throws ResendException { - ListParams params = ListParams.builder() - .limit(2) - .after("34a080c9-b17d-4187-ad80-5af20266e535") - .build(); - ListTemplatesResponseSuccess expectedResponse = TemplatesUtil.listTemplatesResponse(); + ListParams params = ListParams.builder().limit(2).after(TEMPLATE_ID).build(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_RESPONSE_JSON, true); - when(templates.list(params)).thenReturn(expectedResponse); + when(httpClient.perform(startsWith("/templates?"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListTemplatesResponseSuccess response = templates.list(params); assertNotNull(response); - assertEquals(expectedResponse.getData().size(), response.getData().size()); - verify(templates, times(1)).list(params); + assertEquals(2, response.getData().size()); } @Test public void testUpdateTemplate_Success() throws ResendException { - String templateId = "34a080c9-b17d-4187-ad80-5af20266e535"; UpdateTemplateOptions updateOptions = TemplatesUtil.updateTemplateOptions(); - UpdateTemplateResponseSuccess expectedResponse = TemplatesUtil.updateTemplateResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, UPDATE_RESPONSE_JSON, true); - when(templates.update(templateId, updateOptions)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/templates/" + TEMPLATE_ID), anyString(), eq(HttpMethod.PATCH), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); - UpdateTemplateResponseSuccess response = templates.update(templateId, updateOptions); + UpdateTemplateResponseSuccess response = templates.update(TEMPLATE_ID, updateOptions); assertNotNull(response); - assertEquals(expectedResponse.getId(), response.getId()); - assertEquals(expectedResponse.getObject(), response.getObject()); - verify(templates, times(1)).update(templateId, updateOptions); + assertEquals(TEMPLATE_ID, response.getId()); + assertEquals("template", response.getObject()); } @Test public void testDeleteTemplate_Success() throws ResendException { - String templateId = "34a080c9-b17d-4187-ad80-5af20266e535"; - DeleteTemplateResponseSuccess expectedResponse = TemplatesUtil.deleteTemplateResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, DELETE_RESPONSE_JSON, true); - when(templates.remove(templateId)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/templates/" + TEMPLATE_ID), anyString(), eq(HttpMethod.DELETE), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - DeleteTemplateResponseSuccess response = templates.remove(templateId); + DeleteTemplateResponseSuccess response = templates.remove(TEMPLATE_ID); assertNotNull(response); - assertEquals(expectedResponse.getId(), response.getId()); - assertEquals(expectedResponse.getDeleted(), response.getDeleted()); - verify(templates, times(1)).remove(templateId); + assertEquals(TEMPLATE_ID, response.getId()); + assertEquals(true, response.getDeleted()); } @Test public void testDuplicateTemplate_Success() throws ResendException { - String templateId = "34a080c9-b17d-4187-ad80-5af20266e535"; - DuplicateTemplateResponseSuccess expectedResponse = TemplatesUtil.duplicateTemplateResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, DUPLICATE_RESPONSE_JSON, true); - when(templates.duplicate(templateId)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/templates/" + TEMPLATE_ID + "/duplicate"), anyString(), eq(HttpMethod.POST), eq(""), any(MediaType.class))) + .thenReturn(httpResponse); - DuplicateTemplateResponseSuccess response = templates.duplicate(templateId); + DuplicateTemplateResponseSuccess response = templates.duplicate(TEMPLATE_ID); assertNotNull(response); - assertEquals(expectedResponse.getId(), response.getId()); - assertEquals(expectedResponse.getObject(), response.getObject()); - verify(templates, times(1)).duplicate(templateId); + assertEquals("e169aa45-1ecf-4183-9955-b1499d5701d3", response.getId()); + assertEquals("template", response.getObject()); } @Test public void testPublishTemplate_Success() throws ResendException { - String templateId = "34a080c9-b17d-4187-ad80-5af20266e535"; - PublishTemplateResponseSuccess expectedResponse = TemplatesUtil.publishTemplateResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, PUBLISH_RESPONSE_JSON, true); - when(templates.publish(templateId)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/templates/" + TEMPLATE_ID + "/publish"), anyString(), eq(HttpMethod.POST), eq(""), any(MediaType.class))) + .thenReturn(httpResponse); - PublishTemplateResponseSuccess response = templates.publish(templateId); + PublishTemplateResponseSuccess response = templates.publish(TEMPLATE_ID); assertNotNull(response); - assertEquals(expectedResponse.getId(), response.getId()); - assertEquals(expectedResponse.getObject(), response.getObject()); - verify(templates, times(1)).publish(templateId); + assertEquals(TEMPLATE_ID, response.getId()); + assertEquals("template", response.getObject()); } } diff --git a/src/test/java/com/resend/services/topics/TopicsTest.java b/src/test/java/com/resend/services/topics/TopicsTest.java index 3cfd048..351f01e 100644 --- a/src/test/java/com/resend/services/topics/TopicsTest.java +++ b/src/test/java/com/resend/services/topics/TopicsTest.java @@ -1,119 +1,151 @@ package com.resend.services.topics; import com.resend.core.exception.ResendException; +import com.resend.core.net.AbstractHttpResponse; +import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.services.topics.model.*; import com.resend.services.util.TopicsUtil; +import okhttp3.MediaType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.when; -/** - * Test class for Topics service. - */ +@SuppressWarnings("unchecked") public class TopicsTest { + private static final String TOPIC_ID = "b6d24b8e-af0b-4c3c-be0c-359bbd97381e"; + private static final String CREATE_RESPONSE_JSON = "{\"id\":\"" + TOPIC_ID + "\"}"; + private static final String GET_RESPONSE_JSON = + "{\"id\":\"" + TOPIC_ID + "\",\"name\":\"Weekly Newsletter\"," + + "\"description\":\"Weekly newsletter for our subscribers\"," + + "\"default_subscription\":\"opt_in\",\"created_at\":\"2023-04-08T00:11:13.110779+00:00\"}"; + private static final String UPDATE_RESPONSE_JSON = "{\"id\":\"" + TOPIC_ID + "\"}"; + private static final String REMOVE_RESPONSE_JSON = "{\"object\":\"topic\",\"id\":\"" + TOPIC_ID + "\",\"deleted\":true}"; + private static final String LIST_RESPONSE_JSON = + "{\"object\":\"list\",\"has_more\":false,\"data\":[" + + "{\"id\":\"" + TOPIC_ID + "\",\"name\":\"Weekly Newsletter\"," + + "\"description\":\"Weekly newsletter for our subscribers\"," + + "\"default_subscription\":\"opt_in\",\"created_at\":\"2023-04-08T00:11:13.110779+00:00\"}," + + "{\"id\":\"c7e35c9f-bg1c-5d4d-cf1d-460cce08492f\",\"name\":\"Monthly Updates\"," + + "\"description\":\"Monthly updates and announcements\"," + + "\"default_subscription\":\"opt_out\",\"created_at\":\"2023-04-09T00:11:13.110779+00:00\"}," + + "{\"id\":\"d8f46da0-ch2d-6e5e-dg2e-571ddf19503g\",\"name\":\"Product Launches\"," + + "\"description\":\"Get notified about new product launches\"," + + "\"default_subscription\":\"opt_in\",\"created_at\":\"2023-04-10T00:11:13.110779+00:00\"}" + + "]}"; + @Mock + private IHttpClient httpClient; + private Topics topics; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); - topics = mock(Topics.class); + topics = new Topics("test-api-key", httpClient); } @Test public void testCreateTopic_Success() throws ResendException { CreateTopicOptions createOptions = TopicsUtil.createTopicOptions(); - CreateTopicResponseSuccess expectedResponse = TopicsUtil.createTopicResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, CREATE_RESPONSE_JSON, true); - when(topics.create(createOptions)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/topics"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); CreateTopicResponseSuccess response = topics.create(createOptions); assertNotNull(response); - assertEquals(expectedResponse.getId(), response.getId()); - verify(topics, times(1)).create(createOptions); + assertEquals(TOPIC_ID, response.getId()); + } + + @Test + public void testCreateTopic_ApiError_ThrowsResendException() throws ResendException { + CreateTopicOptions createOptions = TopicsUtil.createTopicOptions(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(422, "{\"name\":\"validation_error\",\"message\":\"Invalid\"}", false); + + when(httpClient.perform(eq("/topics"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); + + ResendException ex = assertThrows(ResendException.class, () -> topics.create(createOptions)); + assertEquals(422, (int) ex.getStatusCode()); } @Test public void testGetTopic_Success() throws ResendException { - GetTopicResponseSuccess expectedTopic = TopicsUtil.createTopic(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, GET_RESPONSE_JSON, true); - when(topics.get(expectedTopic.getId())).thenReturn(expectedTopic); + when(httpClient.perform(eq("/topics/" + TOPIC_ID), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - GetTopicResponseSuccess retrievedTopic = topics.get(expectedTopic.getId()); + GetTopicResponseSuccess response = topics.get(TOPIC_ID); - assertNotNull(retrievedTopic); - assertEquals(expectedTopic.getId(), retrievedTopic.getId()); - assertEquals(expectedTopic.getName(), retrievedTopic.getName()); - assertEquals(expectedTopic.getDescription(), retrievedTopic.getDescription()); - assertEquals(expectedTopic.getDefaultSubscription(), retrievedTopic.getDefaultSubscription()); - verify(topics, times(1)).get(expectedTopic.getId()); + assertNotNull(response); + assertEquals(TOPIC_ID, response.getId()); + assertEquals("Weekly Newsletter", response.getName()); + assertEquals("opt_in", response.getDefaultSubscription()); } @Test public void testUpdateTopic_Success() throws ResendException { - String topicId = "b6d24b8e-af0b-4c3c-be0c-359bbd97381e"; UpdateTopicOptions updateOptions = TopicsUtil.updateTopicOptions(); - UpdateTopicResponseSuccess expectedResponse = TopicsUtil.updateTopicResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, UPDATE_RESPONSE_JSON, true); - when(topics.update(topicId, updateOptions)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/topics/" + TOPIC_ID), anyString(), eq(HttpMethod.PATCH), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); - UpdateTopicResponseSuccess response = topics.update(topicId, updateOptions); + UpdateTopicResponseSuccess response = topics.update(TOPIC_ID, updateOptions); assertNotNull(response); - assertEquals(expectedResponse.getId(), response.getId()); - verify(topics, times(1)).update(topicId, updateOptions); + assertEquals(TOPIC_ID, response.getId()); } @Test public void testRemoveTopic_Success() throws ResendException { - String topicId = "b6d24b8e-af0b-4c3c-be0c-359bbd97381e"; - RemoveTopicResponseSuccess expectedResponse = TopicsUtil.removeTopicResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, REMOVE_RESPONSE_JSON, true); - when(topics.remove(topicId)).thenReturn(expectedResponse); + when(httpClient.perform(eq("/topics/" + TOPIC_ID), anyString(), eq(HttpMethod.DELETE), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - RemoveTopicResponseSuccess response = topics.remove(topicId); + RemoveTopicResponseSuccess response = topics.remove(TOPIC_ID); assertNotNull(response); - assertEquals(expectedResponse.getId(), response.getId()); - assertEquals(true, response.isDeleted()); - verify(topics, times(1)).remove(topicId); + assertEquals(TOPIC_ID, response.getId()); + assertTrue(response.isDeleted()); } @Test public void testListTopics_Success() throws ResendException { - ListTopicsResponseSuccess expectedResponse = TopicsUtil.createListTopicsResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_RESPONSE_JSON, true); - when(topics.list()).thenReturn(expectedResponse); + when(httpClient.perform(eq("/topics"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListTopicsResponseSuccess response = topics.list(); assertNotNull(response); - assertEquals(expectedResponse.getData().size(), response.getData().size()); - assertEquals(expectedResponse.hasMore(), response.hasMore()); - verify(topics, times(1)).list(); + assertEquals(3, response.getData().size()); + assertEquals(false, response.hasMore()); } @Test public void testListTopicsWithPagination_Success() throws ResendException { - ListParams params = ListParams.builder() - .limit(3) - .build(); - ListTopicsResponseSuccess expectedResponse = TopicsUtil.createListTopicsResponse(); + ListParams params = ListParams.builder().limit(3).build(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_RESPONSE_JSON, true); - when(topics.list(params)).thenReturn(expectedResponse); + when(httpClient.perform(startsWith("/topics?"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListTopicsResponseSuccess response = topics.list(params); assertNotNull(response); - assertEquals(expectedResponse.getData().size(), response.getData().size()); - verify(topics, times(1)).list(params); + assertEquals(3, response.getData().size()); } } diff --git a/src/test/java/com/resend/services/webhooks/WebhooksTest.java b/src/test/java/com/resend/services/webhooks/WebhooksTest.java index 444f4db..481f350 100644 --- a/src/test/java/com/resend/services/webhooks/WebhooksTest.java +++ b/src/test/java/com/resend/services/webhooks/WebhooksTest.java @@ -1,10 +1,13 @@ package com.resend.services.webhooks; import com.resend.core.exception.ResendException; +import com.resend.core.net.AbstractHttpResponse; +import com.resend.core.net.HttpMethod; +import com.resend.core.net.IHttpClient; import com.resend.core.net.ListParams; import com.resend.services.util.WebhooksUtil; -import com.resend.services.webhooks.dto.WebhookDTO; import com.resend.services.webhooks.model.*; +import okhttp3.MediaType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; @@ -13,70 +16,105 @@ import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.util.Base64; +import java.util.Collections; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.mock; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.when; +@SuppressWarnings("unchecked") public class WebhooksTest { + private static final String WEBHOOK_ID = "4dd369bc-aa82-4ff3-97de-514ae3000ee0"; + + private static final String CREATE_WEBHOOK_JSON = + "{\"object\":\"webhook\",\"id\":\"" + WEBHOOK_ID + "\",\"signing_secret\":\"whsec_xxxxxxxxxx\"}"; + + private static final String UPDATE_WEBHOOK_JSON = + "{\"object\":\"webhook\",\"id\":\"" + WEBHOOK_ID + "\"}"; + + private static final String GET_WEBHOOK_JSON = + "{\"object\":\"webhook\",\"id\":\"" + WEBHOOK_ID + "\"," + + "\"created_at\":\"2023-08-22T15:28:00.000Z\",\"status\":\"enabled\"," + + "\"endpoint\":\"https://webhook.example.com/handler\"," + + "\"events\":[\"email.sent\",\"email.received\"]," + + "\"signing_secret\":\"whsec_xxxxxxxxxx\"}"; + + private static final String LIST_WEBHOOKS_JSON = + "{\"object\":\"list\",\"has_more\":false,\"data\":[" + + "{\"id\":\"7ab123cd-ef45-6789-abcd-ef0123456789\",\"created_at\":\"2023-09-10T10:15:30.000Z\",\"status\":\"disabled\",\"endpoint\":\"https://first-webhook.example.com/handler\",\"events\":[\"email.delivered\",\"email.bounced\"]}," + + "{\"id\":\"" + WEBHOOK_ID + "\",\"created_at\":\"2023-08-22T15:28:00.000Z\",\"status\":\"enabled\",\"endpoint\":\"https://second-webhook.example.com/receive\",\"events\":[\"email.received\"]}" + + "]}"; + + private static final String REMOVE_WEBHOOK_JSON = + "{\"object\":\"webhook\",\"id\":\"" + WEBHOOK_ID + "\",\"deleted\":true}"; + @Mock + private IHttpClient httpClient; + private Webhooks webhooks; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); - webhooks = mock(Webhooks.class); + webhooks = new Webhooks("test-api-key", httpClient); } @Test public void testCreateWebhook_Success() throws ResendException { - CreateWebhookResponseSuccess expectedResponse = WebhooksUtil.createWebhookResponse(); - CreateWebhookOptions request = WebhooksUtil.createWebhookRequest(); - when(webhooks.create(request)) - .thenReturn(expectedResponse); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, CREATE_WEBHOOK_JSON, true); + + when(httpClient.perform(eq("/webhooks"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); CreateWebhookResponseSuccess response = webhooks.create(request); assertNotNull(response); - assertEquals(expectedResponse.getId(), response.getId()); - assertEquals(expectedResponse.getObject(), response.getObject()); - assertEquals(expectedResponse.getSigningSecret(), response.getSigningSecret()); + assertEquals(WEBHOOK_ID, response.getId()); assertEquals("webhook", response.getObject()); - assertEquals("4dd369bc-aa82-4ff3-97de-514ae3000ee0", response.getId()); + assertEquals("whsec_xxxxxxxxxx", response.getSigningSecret()); } @Test - public void testUpdateWebhook_Success() throws ResendException { - UpdateWebhookResponseSuccess expectedResponse = WebhooksUtil.updateWebhookResponse(); - String webhookId = "4dd369bc-aa82-4ff3-97de-514ae3000ee0"; + public void testCreateWebhook_ApiError_ThrowsResendException() throws ResendException { + CreateWebhookOptions request = WebhooksUtil.createWebhookRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(422, + "{\"name\":\"validation_error\",\"message\":\"Invalid endpoint\"}", false); + + when(httpClient.perform(eq("/webhooks"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); + + ResendException ex = assertThrows(ResendException.class, () -> webhooks.create(request)); + assertEquals(422, (int) ex.getStatusCode()); + } + @Test + public void testUpdateWebhook_Success() throws ResendException { UpdateWebhookOptions request = WebhooksUtil.updateWebhookRequest(); - when(webhooks.update(webhookId, request)) - .thenReturn(expectedResponse); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, UPDATE_WEBHOOK_JSON, true); - UpdateWebhookResponseSuccess response = webhooks.update(webhookId, request); + when(httpClient.perform(eq("/webhooks/" + WEBHOOK_ID), anyString(), eq(HttpMethod.PATCH), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); + + UpdateWebhookResponseSuccess response = webhooks.update(WEBHOOK_ID, request); assertNotNull(response); - assertEquals(expectedResponse.getId(), response.getId()); - assertEquals(expectedResponse.getObject(), response.getObject()); + assertEquals(WEBHOOK_ID, response.getId()); assertEquals("webhook", response.getObject()); - assertEquals("4dd369bc-aa82-4ff3-97de-514ae3000ee0", response.getId()); } @Test public void testGetWebhook_Success() throws ResendException { - GetWebhookResponseSuccess expectedWebhook = WebhooksUtil.getWebhookResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, GET_WEBHOOK_JSON, true); - when(webhooks.get(expectedWebhook.getId())) - .thenReturn(expectedWebhook); + when(httpClient.perform(eq("/webhooks/" + WEBHOOK_ID), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); - GetWebhookResponseSuccess response = webhooks.get(expectedWebhook.getId()); + GetWebhookResponseSuccess response = webhooks.get(WEBHOOK_ID); assertNotNull(response); - assertEquals(expectedWebhook, response); - assertEquals(expectedWebhook.getId(), response.getId()); + assertEquals(WEBHOOK_ID, response.getId()); assertEquals("webhook", response.getObject()); assertEquals(WebhookStatus.ENABLED, response.getStatus()); assertEquals("https://webhook.example.com/handler", response.getEndpoint()); @@ -84,14 +122,14 @@ public void testGetWebhook_Success() throws ResendException { @Test public void testListWebhooks_Success() throws ResendException { - ListWebhooksResponseSuccess expectedResponse = WebhooksUtil.listWebhooksResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_WEBHOOKS_JSON, true); - when(webhooks.list()).thenReturn(expectedResponse); + when(httpClient.perform(eq("/webhooks"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListWebhooksResponseSuccess response = webhooks.list(); assertNotNull(response); - assertEquals(expectedResponse.getData().size(), response.getData().size()); assertEquals(2, response.getData().size()); assertEquals("list", response.getObject()); assertFalse(response.hasMore()); @@ -100,15 +138,14 @@ public void testListWebhooks_Success() throws ResendException { @Test public void testListWebhooksWithPagination_Success() throws ResendException { ListParams params = ListParams.builder().limit(1).build(); - ListWebhooksResponseSuccess expectedResponse = WebhooksUtil.listWebhooksResponse(); - WebhookDTO paginatedData = expectedResponse.getData().get(0); - ListWebhooksResponseSuccess paginatedResponse = new ListWebhooksResponseSuccess( - "list", - true, - java.util.Collections.singletonList(paginatedData) - ); + String paginatedJson = + "{\"object\":\"list\",\"has_more\":true,\"data\":[" + + "{\"id\":\"7ab123cd-ef45-6789-abcd-ef0123456789\",\"created_at\":\"2023-09-10T10:15:30.000Z\",\"status\":\"disabled\",\"endpoint\":\"https://first-webhook.example.com/handler\",\"events\":[\"email.delivered\"]}" + + "]}"; + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, paginatedJson, true); - when(webhooks.list(params)).thenReturn(paginatedResponse); + when(httpClient.perform(startsWith("/webhooks?"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); ListWebhooksResponseSuccess response = webhooks.list(params); @@ -119,16 +156,15 @@ public void testListWebhooksWithPagination_Success() throws ResendException { @Test public void testRemoveWebhook_Success() throws ResendException { - RemoveWebhookResponseSuccess expectedResponse = WebhooksUtil.removeWebhookResponse(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, REMOVE_WEBHOOK_JSON, true); - when(webhooks.remove(expectedResponse.getId())) - .thenReturn(expectedResponse); + when(httpClient.perform(eq("/webhooks/" + WEBHOOK_ID), anyString(), eq(HttpMethod.DELETE), eq(""), isNull())) + .thenReturn(httpResponse); - RemoveWebhookResponseSuccess response = webhooks.remove(expectedResponse.getId()); + RemoveWebhookResponseSuccess response = webhooks.remove(WEBHOOK_ID); assertNotNull(response); - assertEquals(expectedResponse, response); - assertEquals(expectedResponse.getId(), response.getId()); + assertEquals(WEBHOOK_ID, response.getId()); assertEquals("webhook", response.getObject()); assertTrue(response.getDeleted()); } @@ -137,16 +173,14 @@ public void testRemoveWebhook_Success() throws ResendException { public void testVerifyWebhook_Success() throws Exception { Webhooks webhooksService = new Webhooks("test-api-key"); - // Test data String secret = "whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw"; String payload = "{\"type\":\"email.sent\",\"created_at\":\"2024-01-01T00:00:00.000Z\"}"; String msgId = "msg_test123"; long currentTimestamp = System.currentTimeMillis() / 1000; String timestamp = String.valueOf(currentTimestamp); - // Generate valid signature String signedContent = msgId + "." + timestamp + "." + payload; - String secretKey = secret.substring(6); // Remove "whsec_" prefix + String secretKey = secret.substring(6); byte[] decodedSecret = Base64.getDecoder().decode(secretKey); Mac hmac = Mac.getInstance("HmacSHA256"); @@ -155,7 +189,6 @@ public void testVerifyWebhook_Success() throws Exception { byte[] hash = hmac.doFinal(signedContent.getBytes("UTF-8")); String signature = "v1," + Base64.getEncoder().encodeToString(hash); - // Create verification options VerifyWebhookOptions options = VerifyWebhookOptions.builder() .payload(payload) .addHeader("svix-id", msgId) @@ -164,7 +197,6 @@ public void testVerifyWebhook_Success() throws Exception { .secret(secret) .build(); - // Should not throw exception assertDoesNotThrow(() -> webhooksService.verify(options)); } @@ -178,7 +210,6 @@ public void testVerifyWebhook_InvalidSignature() { long currentTimestamp = System.currentTimeMillis() / 1000; String timestamp = String.valueOf(currentTimestamp); - // Invalid signature String invalidSignature = "v1,invalid_signature_here"; VerifyWebhookOptions options = VerifyWebhookOptions.builder() @@ -205,7 +236,6 @@ public void testVerifyWebhook_ExpiredTimestamp() { String payload = "{\"type\":\"email.sent\"}"; String msgId = "msg_test123"; - // Timestamp from 10 minutes ago (should fail 5-minute tolerance) long expiredTimestamp = (System.currentTimeMillis() / 1000) - 600; String timestamp = String.valueOf(expiredTimestamp); @@ -279,7 +309,6 @@ public void testVerifyWebhook_EmptySecret() { @Test public void testVerifyWebhook_MultipleSignatures() throws Exception { - // Test that verification works with multiple signatures in the header Webhooks webhooksService = new Webhooks("test-api-key"); String secret = "whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw"; @@ -288,7 +317,6 @@ public void testVerifyWebhook_MultipleSignatures() throws Exception { long currentTimestamp = System.currentTimeMillis() / 1000; String timestamp = String.valueOf(currentTimestamp); - // Generate valid signature String signedContent = msgId + "." + timestamp + "." + payload; String secretKey = secret.substring(6); byte[] decodedSecret = Base64.getDecoder().decode(secretKey); @@ -299,7 +327,6 @@ public void testVerifyWebhook_MultipleSignatures() throws Exception { byte[] hash = hmac.doFinal(signedContent.getBytes("UTF-8")); String validSignature = Base64.getEncoder().encodeToString(hash); - // Multiple signatures: one invalid, one valid String multipleSignatures = "v1,invalid_sig v1," + validSignature; VerifyWebhookOptions options = VerifyWebhookOptions.builder() @@ -310,7 +337,6 @@ public void testVerifyWebhook_MultipleSignatures() throws Exception { .secret(secret) .build(); - // Should not throw exception (one valid signature is enough) assertDoesNotThrow(() -> webhooksService.verify(options)); } }