From 5462eb53aaefcb10c9857ad90d3220038ca7df8c Mon Sep 17 00:00:00 2001 From: Kewyn Akshlley Date: Sun, 28 Jun 2026 22:04:48 -0300 Subject: [PATCH 1/2] feat: Add domain claims endpoints --- .../resend/services/domains/DomainClaims.java | 79 +++++++ .../com/resend/services/domains/Domains.java | 9 + .../domains/model/ClaimDomainOptions.java | 200 ++++++++++++++++++ .../domains/model/DomainClaimRecord.java | 81 +++++++ .../model/DomainClaimResponseSuccess.java | 186 ++++++++++++++++ .../services/domains/DomainClaimsTest.java | 73 +++++++ .../com/resend/services/util/DomainsUtil.java | 31 +++ 7 files changed, 659 insertions(+) create mode 100644 src/main/java/com/resend/services/domains/DomainClaims.java create mode 100644 src/main/java/com/resend/services/domains/model/ClaimDomainOptions.java create mode 100644 src/main/java/com/resend/services/domains/model/DomainClaimRecord.java create mode 100644 src/main/java/com/resend/services/domains/model/DomainClaimResponseSuccess.java create mode 100644 src/test/java/com/resend/services/domains/DomainClaimsTest.java diff --git a/src/main/java/com/resend/services/domains/DomainClaims.java b/src/main/java/com/resend/services/domains/DomainClaims.java new file mode 100644 index 0000000..1fbe1b9 --- /dev/null +++ b/src/main/java/com/resend/services/domains/DomainClaims.java @@ -0,0 +1,79 @@ +package com.resend.services.domains; + +import com.resend.core.exception.ResendException; +import com.resend.core.net.AbstractHttpResponse; +import com.resend.core.net.HttpMethod; +import com.resend.core.service.BaseService; +import com.resend.services.domains.model.ClaimDomainOptions; +import com.resend.services.domains.model.DomainClaimResponseSuccess; +import okhttp3.MediaType; + +/** + * Represents the Resend Domain Claims module. + */ +public final class DomainClaims extends BaseService { + + /** + * Constructs an instance of the {@code DomainClaims} class. + * + * @param apiKey The apiKey used for authentication. + */ + public DomainClaims(final String apiKey) { + super(apiKey); + } + + /** + * Claims a domain already verified by another team. + * + * @param claimDomainOptions The request object containing the domain claim details. + * @return A DomainClaimResponseSuccess representing the created claim. + * @throws ResendException If an error occurs during the domain claim process. + */ + public DomainClaimResponseSuccess create(ClaimDomainOptions claimDomainOptions) throws ResendException { + String payload = super.resendMapper.writeValue(claimDomainOptions); + AbstractHttpResponse response = httpClient.perform("/domains/claim", super.apiKey, HttpMethod.POST, payload, MediaType.get("application/json")); + + if (!response.isSuccessful()) { + throw new ResendException(response.getCode(), response.getBody()); + } + + String responseBody = response.getBody(); + return resendMapper.readValue(responseBody, DomainClaimResponseSuccess.class); + } + + /** + * Retrieves the latest claim for a domain. + * + * @param domainId The placeholder domain ID returned when the claim was created. + * @return A DomainClaimResponseSuccess representing the current claim state. + * @throws ResendException If an error occurs during the retrieval process. + */ + public DomainClaimResponseSuccess get(String domainId) throws ResendException { + AbstractHttpResponse response = httpClient.perform("/domains/" + domainId + "/claim", super.apiKey, HttpMethod.GET, null, MediaType.get("application/json")); + + if (!response.isSuccessful()) { + throw new ResendException(response.getCode(), response.getBody()); + } + + String responseBody = response.getBody(); + return resendMapper.readValue(responseBody, DomainClaimResponseSuccess.class); + } + + /** + * Triggers DNS verification for a domain claim. + * + * @param domainId The placeholder domain ID returned when the claim was created. + * @return A DomainClaimResponseSuccess representing the claim after verification is triggered. + * @throws ResendException If an error occurs during the verification process. + */ + public DomainClaimResponseSuccess verify(String domainId) throws ResendException { + AbstractHttpResponse response = httpClient.perform("/domains/" + domainId + "/claim/verify", super.apiKey, HttpMethod.POST, "", null); + + if (!response.isSuccessful()) { + throw new ResendException(response.getCode(), response.getBody()); + } + + String responseBody = response.getBody(); + return resendMapper.readValue(responseBody, DomainClaimResponseSuccess.class); + } +} diff --git a/src/main/java/com/resend/services/domains/Domains.java b/src/main/java/com/resend/services/domains/Domains.java index a3976f9..cb43638 100644 --- a/src/main/java/com/resend/services/domains/Domains.java +++ b/src/main/java/com/resend/services/domains/Domains.java @@ -136,6 +136,15 @@ public UpdateDomainResponseSuccess update(UpdateDomainOptions updateDomainOption return resendMapper.readValue(responseBody, UpdateDomainResponseSuccess.class); } + /** + * Returns a DomainClaims object that can be used to interact with the Domain Claims service. + * + * @return A DomainClaims object. + */ + public DomainClaims claims() { + return new DomainClaims(apiKey); + } + /** * Deletes a domain based on the provided domain ID and returns a RemoveDomainResponse. * diff --git a/src/main/java/com/resend/services/domains/model/ClaimDomainOptions.java b/src/main/java/com/resend/services/domains/model/ClaimDomainOptions.java new file mode 100644 index 0000000..61a253d --- /dev/null +++ b/src/main/java/com/resend/services/domains/model/ClaimDomainOptions.java @@ -0,0 +1,200 @@ +package com.resend.services.domains.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents a request to claim a domain already verified by another team. + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ClaimDomainOptions { + + @JsonProperty("name") + private final String name; + + @JsonProperty("region") + private final String region; + + @JsonProperty("custom_return_path") + private final String customReturnPath; + + @JsonProperty("open_tracking") + private final Boolean openTracking; + + @JsonProperty("click_tracking") + private final Boolean clickTracking; + + @JsonProperty("tracking_subdomain") + private final String trackingSubdomain; + + /** + * Constructs a ClaimDomainOptions object using the provided builder. + * + * @param builder The builder to construct the ClaimDomainOptions from. + */ + public ClaimDomainOptions(Builder builder) { + this.name = builder.name; + this.region = builder.region; + this.customReturnPath = builder.customReturnPath; + this.openTracking = builder.openTracking; + this.clickTracking = builder.clickTracking; + this.trackingSubdomain = builder.trackingSubdomain; + } + + /** + * Get the domain name to claim. + * + * @return The domain name. + */ + public String getName() { + return name; + } + + /** + * Get the region where emails will be sent from. + * + * @return The region. + */ + public String getRegion() { + return region; + } + + /** + * Get the custom return path subdomain. + * + * @return The custom return path. + */ + public String getCustomReturnPath() { + return customReturnPath; + } + + /** + * Get whether open tracking is enabled. + * + * @return The open tracking setting. + */ + public Boolean getOpenTracking() { + return openTracking; + } + + /** + * Get whether click tracking is enabled. + * + * @return The click tracking setting. + */ + public Boolean getClickTracking() { + return clickTracking; + } + + /** + * Get the subdomain used for click and open tracking. + * + * @return The tracking subdomain. + */ + public String getTrackingSubdomain() { + return trackingSubdomain; + } + + /** + * Create a new builder instance for constructing ClaimDomainOptions objects. + * + * @return A new builder instance. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder class for constructing ClaimDomainOptions objects. + */ + public static class Builder { + + /** + * Creates a new Builder instance. + */ + public Builder() { + } + + private String name; + private String region; + private String customReturnPath; + private Boolean openTracking; + private Boolean clickTracking; + private String trackingSubdomain; + + /** + * Set the domain name to claim. + * + * @param name The domain name. + * @return The builder instance. + */ + public Builder name(String name) { + this.name = name; + return this; + } + + /** + * Set the region where emails will be sent from. + * + * @param region The region. + * @return The builder instance. + */ + public Builder region(String region) { + this.region = region; + return this; + } + + /** + * Set the custom return path subdomain. + * + * @param customReturnPath The custom return path. + * @return The builder instance. + */ + public Builder customReturnPath(String customReturnPath) { + this.customReturnPath = customReturnPath; + return this; + } + + /** + * Set whether open tracking is enabled. + * + * @param openTracking The open tracking setting. + * @return The builder instance. + */ + public Builder openTracking(Boolean openTracking) { + this.openTracking = openTracking; + return this; + } + + /** + * Set whether click tracking is enabled. + * + * @param clickTracking The click tracking setting. + * @return The builder instance. + */ + public Builder clickTracking(Boolean clickTracking) { + this.clickTracking = clickTracking; + return this; + } + + /** + * Set the subdomain used for click and open tracking. + * + * @param trackingSubdomain The tracking subdomain. + * @return The builder instance. + */ + public Builder trackingSubdomain(String trackingSubdomain) { + this.trackingSubdomain = trackingSubdomain; + return this; + } + + /** + * Build a new ClaimDomainOptions object. + * + * @return A new ClaimDomainOptions object. + */ + public ClaimDomainOptions build() { + return new ClaimDomainOptions(this); + } + } +} diff --git a/src/main/java/com/resend/services/domains/model/DomainClaimRecord.java b/src/main/java/com/resend/services/domains/model/DomainClaimRecord.java new file mode 100644 index 0000000..cfa82f2 --- /dev/null +++ b/src/main/java/com/resend/services/domains/model/DomainClaimRecord.java @@ -0,0 +1,81 @@ +package com.resend.services.domains.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents the TXT DNS record returned within a domain claim response. + */ +public class DomainClaimRecord { + + @JsonProperty("type") + private String type; + + @JsonProperty("name") + private String name; + + @JsonProperty("value") + private String value; + + @JsonProperty("ttl") + private String ttl; + + /** + * Default constructor. + */ + public DomainClaimRecord() { + } + + /** + * Constructs a DomainClaimRecord with all fields. + * + * @param type The DNS record type. + * @param name The DNS record name. + * @param value The DNS record value. + * @param ttl The TTL for the DNS record. + */ + public DomainClaimRecord(final String type, + final String name, + final String value, + final String ttl) { + this.type = type; + this.name = name; + this.value = value; + this.ttl = ttl; + } + + /** + * Get the DNS record type. + * + * @return The DNS record type. + */ + public String getType() { + return type; + } + + /** + * Get the DNS record name. + * + * @return The DNS record name. + */ + public String getName() { + return name; + } + + /** + * Get the DNS record value. + * + * @return The DNS record value. + */ + public String getValue() { + return value; + } + + /** + * Get the TTL for the DNS record. + * + * @return The TTL. + */ + public String getTtl() { + return ttl; + } +} diff --git a/src/main/java/com/resend/services/domains/model/DomainClaimResponseSuccess.java b/src/main/java/com/resend/services/domains/model/DomainClaimResponseSuccess.java new file mode 100644 index 0000000..3176f9a --- /dev/null +++ b/src/main/java/com/resend/services/domains/model/DomainClaimResponseSuccess.java @@ -0,0 +1,186 @@ +package com.resend.services.domains.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents a domain claim response returned by the claim, get, and verify claim endpoints. + */ +public class DomainClaimResponseSuccess { + + @JsonProperty("object") + private String object; + + @JsonProperty("id") + private String id; + + @JsonProperty("name") + private String name; + + @JsonProperty("status") + private String status; + + @JsonProperty("domain_id") + private String domainId; + + @JsonProperty("region") + private String region; + + @JsonProperty("record") + private DomainClaimRecord record; + + @JsonProperty("blocked_reason") + private String blockedReason; + + @JsonProperty("failure_reason") + private String failureReason; + + @JsonProperty("created_at") + private String createdAt; + + @JsonProperty("expires_at") + private String expiresAt; + + /** + * Default constructor. + */ + public DomainClaimResponseSuccess() { + } + + /** + * Constructs a DomainClaimResponseSuccess with all fields. + * + * @param object The object type identifier. + * @param id The claim ID. + * @param name The domain name. + * @param status The claim status. + * @param domainId The placeholder domain ID. + * @param region The region. + * @param record The TXT DNS record for verification. + * @param blockedReason The reason the claim is blocked, if any. + * @param failureReason The reason the claim failed, if any. + * @param createdAt The creation timestamp. + * @param expiresAt The expiration timestamp. + */ + public DomainClaimResponseSuccess(final String object, + final String id, + final String name, + final String status, + final String domainId, + final String region, + final DomainClaimRecord record, + final String blockedReason, + final String failureReason, + final String createdAt, + final String expiresAt) { + this.object = object; + this.id = id; + this.name = name; + this.status = status; + this.domainId = domainId; + this.region = region; + this.record = record; + this.blockedReason = blockedReason; + this.failureReason = failureReason; + this.createdAt = createdAt; + this.expiresAt = expiresAt; + } + + /** + * Get the object type identifier. + * + * @return The object type. + */ + public String getObject() { + return object; + } + + /** + * Get the claim ID. + * + * @return The claim ID. + */ + public String getId() { + return id; + } + + /** + * Get the domain name. + * + * @return The domain name. + */ + public String getName() { + return name; + } + + /** + * Get the claim status. + * + * @return The claim status. + */ + public String getStatus() { + return status; + } + + /** + * Get the placeholder domain ID. + * + * @return The domain ID. + */ + public String getDomainId() { + return domainId; + } + + /** + * Get the region. + * + * @return The region. + */ + public String getRegion() { + return region; + } + + /** + * Get the TXT DNS record for verification. + * + * @return The DNS record. + */ + public DomainClaimRecord getRecord() { + return record; + } + + /** + * Get the reason the claim is blocked, if any. + * + * @return The blocked reason. + */ + public String getBlockedReason() { + return blockedReason; + } + + /** + * Get the reason the claim failed, if any. + * + * @return The failure reason. + */ + public String getFailureReason() { + return failureReason; + } + + /** + * Get the creation timestamp. + * + * @return The creation timestamp. + */ + public String getCreatedAt() { + return createdAt; + } + + /** + * Get the expiration timestamp. + * + * @return The expiration timestamp. + */ + public String getExpiresAt() { + return expiresAt; + } +} diff --git a/src/test/java/com/resend/services/domains/DomainClaimsTest.java b/src/test/java/com/resend/services/domains/DomainClaimsTest.java new file mode 100644 index 0000000..3d371c4 --- /dev/null +++ b/src/test/java/com/resend/services/domains/DomainClaimsTest.java @@ -0,0 +1,73 @@ +package com.resend.services.domains; + +import com.resend.core.exception.ResendException; +import com.resend.services.domains.model.ClaimDomainOptions; +import com.resend.services.domains.model.DomainClaimResponseSuccess; +import com.resend.services.util.DomainsUtil; +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.mock; +import static org.mockito.Mockito.when; + +public class DomainClaimsTest { + + @Mock + private DomainClaims domainClaims; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + domainClaims = mock(DomainClaims.class); + } + + @Test + public void testClaimDomain_Success() throws ResendException { + DomainClaimResponseSuccess expected = DomainsUtil.claimDomainResponse(); + ClaimDomainOptions request = DomainsUtil.claimDomainRequest(); + + when(domainClaims.create(request)).thenReturn(expected); + + DomainClaimResponseSuccess response = domainClaims.create(request); + + assertNotNull(response); + assertEquals(expected.getId(), response.getId()); + assertEquals(expected.getName(), response.getName()); + assertEquals(expected.getStatus(), response.getStatus()); + assertEquals(expected.getDomainId(), response.getDomainId()); + } + + @Test + public void testGetDomainClaim_Success() throws ResendException { + DomainClaimResponseSuccess expected = DomainsUtil.claimDomainResponse(); + String domainId = expected.getDomainId(); + + when(domainClaims.get(domainId)).thenReturn(expected); + + DomainClaimResponseSuccess response = domainClaims.get(domainId); + + assertNotNull(response); + assertEquals(expected.getId(), response.getId()); + assertEquals(expected.getDomainId(), response.getDomainId()); + assertEquals(expected.getStatus(), response.getStatus()); + } + + @Test + public void testVerifyDomainClaim_Success() throws ResendException { + DomainClaimResponseSuccess expected = DomainsUtil.claimDomainResponse(); + String domainId = expected.getDomainId(); + + when(domainClaims.verify(domainId)).thenReturn(expected); + + DomainClaimResponseSuccess response = domainClaims.verify(domainId); + + assertNotNull(response); + assertEquals(expected.getId(), response.getId()); + assertEquals(expected.getDomainId(), response.getDomainId()); + assertEquals(expected.getStatus(), response.getStatus()); + } +} diff --git a/src/test/java/com/resend/services/util/DomainsUtil.java b/src/test/java/com/resend/services/util/DomainsUtil.java index c6145f8..3ac7468 100644 --- a/src/test/java/com/resend/services/util/DomainsUtil.java +++ b/src/test/java/com/resend/services/util/DomainsUtil.java @@ -5,6 +5,8 @@ import java.util.ArrayList; import java.util.List; + + public class DomainsUtil { public static final CreateDomainOptions createDomainRequest() { @@ -101,4 +103,33 @@ public static final UpdateDomainOptions updateDomainRequest() { .build(); } + public static final ClaimDomainOptions claimDomainRequest() { + return ClaimDomainOptions.builder() + .name("example.com") + .region("us-east-1") + .build(); + } + + public static final DomainClaimResponseSuccess claimDomainResponse() { + DomainClaimRecord record = new DomainClaimRecord( + "TXT", + "example.com", + "resend-domain-verification=3f8a1c2d4e5b6a7f8091a2b3c4d5e6f7", + "Auto" + ); + return new DomainClaimResponseSuccess( + "domain_claim", + "dacf4072-4119-4d88-932f-6c6126d3a9d1", + "example.com", + "pending", + "d91cd9bd-1176-453e-8fc1-35364d380206", + "us-east-1", + record, + null, + null, + "2026-06-16 17:12:02.059593+00", + "2026-06-23 17:12:02.059593+00" + ); + } + } From b76212f8de3db5043f46414390ca7cb5de7e0f61 Mon Sep 17 00:00:00 2001 From: Kewyn Akshlley Date: Tue, 30 Jun 2026 09:49:20 -0300 Subject: [PATCH 2/2] fix: remove duplicate fields, add null guard, and fix tautological tests in DomainClaims --- .../com/resend/core/service/BaseService.java | 12 +++ .../resend/services/domains/DomainClaims.java | 14 ++++ .../model/DomainClaimResponseSuccess.java | 68 +-------------- .../services/domains/DomainClaimsTest.java | 84 ++++++++++++++----- 4 files changed, 90 insertions(+), 88 deletions(-) diff --git a/src/main/java/com/resend/core/service/BaseService.java b/src/main/java/com/resend/core/service/BaseService.java index 5f9841a..0875f0a 100644 --- a/src/main/java/com/resend/core/service/BaseService.java +++ b/src/main/java/com/resend/core/service/BaseService.java @@ -37,6 +37,18 @@ public BaseService(final String apiKey) { this.resendMapper = new ResendMapper(); } + /** + * Constructs a BaseService instance with a provided HTTP client, intended for testing. + * + * @param apiKey The apiKey to use. + * @param httpClient The HTTP client to use. + */ + protected BaseService(final String apiKey, final IHttpClient httpClient) { + this.apiKey = apiKey; + this.httpClient = httpClient; + this.resendMapper = new ResendMapper(); + } + /** * Gets the HTTP client associated with this service instance. * diff --git a/src/main/java/com/resend/services/domains/DomainClaims.java b/src/main/java/com/resend/services/domains/DomainClaims.java index 1fbe1b9..343f776 100644 --- a/src/main/java/com/resend/services/domains/DomainClaims.java +++ b/src/main/java/com/resend/services/domains/DomainClaims.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.domains.model.ClaimDomainOptions; import com.resend.services.domains.model.DomainClaimResponseSuccess; @@ -22,6 +23,16 @@ public DomainClaims(final String apiKey) { super(apiKey); } + /** + * Package-private constructor for testing, allowing injection of a mock HTTP client. + * + * @param apiKey The apiKey used for authentication. + * @param httpClient The HTTP client to use. + */ + DomainClaims(final String apiKey, final IHttpClient httpClient) { + super(apiKey, httpClient); + } + /** * Claims a domain already verified by another team. * @@ -30,6 +41,9 @@ public DomainClaims(final String apiKey) { * @throws ResendException If an error occurs during the domain claim process. */ public DomainClaimResponseSuccess create(ClaimDomainOptions claimDomainOptions) throws ResendException { + if (claimDomainOptions == null) { + throw new ResendException("claimDomainOptions must not be null"); + } String payload = super.resendMapper.writeValue(claimDomainOptions); AbstractHttpResponse response = httpClient.perform("/domains/claim", super.apiKey, HttpMethod.POST, payload, MediaType.get("application/json")); diff --git a/src/main/java/com/resend/services/domains/model/DomainClaimResponseSuccess.java b/src/main/java/com/resend/services/domains/model/DomainClaimResponseSuccess.java index 3176f9a..1751c83 100644 --- a/src/main/java/com/resend/services/domains/model/DomainClaimResponseSuccess.java +++ b/src/main/java/com/resend/services/domains/model/DomainClaimResponseSuccess.java @@ -5,26 +5,14 @@ /** * Represents a domain claim response returned by the claim, get, and verify claim endpoints. */ -public class DomainClaimResponseSuccess { +public class DomainClaimResponseSuccess extends AbstractDomain { @JsonProperty("object") private String object; - @JsonProperty("id") - private String id; - - @JsonProperty("name") - private String name; - - @JsonProperty("status") - private String status; - @JsonProperty("domain_id") private String domainId; - @JsonProperty("region") - private String region; - @JsonProperty("record") private DomainClaimRecord record; @@ -34,9 +22,6 @@ public class DomainClaimResponseSuccess { @JsonProperty("failure_reason") private String failureReason; - @JsonProperty("created_at") - private String createdAt; - @JsonProperty("expires_at") private String expiresAt; @@ -72,16 +57,12 @@ public DomainClaimResponseSuccess(final String object, final String failureReason, final String createdAt, final String expiresAt) { + super(id, name, createdAt, status, region); this.object = object; - this.id = id; - this.name = name; - this.status = status; this.domainId = domainId; - this.region = region; this.record = record; this.blockedReason = blockedReason; this.failureReason = failureReason; - this.createdAt = createdAt; this.expiresAt = expiresAt; } @@ -94,33 +75,6 @@ public String getObject() { return object; } - /** - * Get the claim ID. - * - * @return The claim ID. - */ - public String getId() { - return id; - } - - /** - * Get the domain name. - * - * @return The domain name. - */ - public String getName() { - return name; - } - - /** - * Get the claim status. - * - * @return The claim status. - */ - public String getStatus() { - return status; - } - /** * Get the placeholder domain ID. * @@ -130,15 +84,6 @@ public String getDomainId() { return domainId; } - /** - * Get the region. - * - * @return The region. - */ - public String getRegion() { - return region; - } - /** * Get the TXT DNS record for verification. * @@ -166,15 +111,6 @@ public String getFailureReason() { return failureReason; } - /** - * Get the creation timestamp. - * - * @return The creation timestamp. - */ - public String getCreatedAt() { - return createdAt; - } - /** * Get the expiration timestamp. * diff --git a/src/test/java/com/resend/services/domains/DomainClaimsTest.java b/src/test/java/com/resend/services/domains/DomainClaimsTest.java index 3d371c4..a0901de 100644 --- a/src/test/java/com/resend/services/domains/DomainClaimsTest.java +++ b/src/test/java/com/resend/services/domains/DomainClaimsTest.java @@ -1,73 +1,113 @@ package com.resend.services.domains; 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.domains.model.ClaimDomainOptions; import com.resend.services.domains.model.DomainClaimResponseSuccess; 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 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 DomainClaimsTest { + private static final String CLAIM_RESPONSE_JSON = "{" + + "\"object\":\"domain_claim\"," + + "\"id\":\"dacf4072-4119-4d88-932f-6c6126d3a9d1\"," + + "\"name\":\"example.com\"," + + "\"status\":\"pending\"," + + "\"domain_id\":\"d91cd9bd-1176-453e-8fc1-35364d380206\"," + + "\"region\":\"us-east-1\"," + + "\"record\":{\"type\":\"TXT\",\"name\":\"example.com\"," + + "\"value\":\"resend-domain-verification=3f8a1c2d4e5b6a7f8091a2b3c4d5e6f7\",\"ttl\":\"Auto\"}," + + "\"created_at\":\"2026-06-16 17:12:02.059593+00\"," + + "\"expires_at\":\"2026-06-23 17:12:02.059593+00\"" + + "}"; + @Mock + private IHttpClient httpClient; + private DomainClaims domainClaims; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); - domainClaims = mock(DomainClaims.class); + domainClaims = new DomainClaims("test-api-key", httpClient); } @Test public void testClaimDomain_Success() throws ResendException { - DomainClaimResponseSuccess expected = DomainsUtil.claimDomainResponse(); ClaimDomainOptions request = DomainsUtil.claimDomainRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, CLAIM_RESPONSE_JSON, true); - when(domainClaims.create(request)).thenReturn(expected); + when(httpClient.perform(eq("/domains/claim"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); DomainClaimResponseSuccess response = domainClaims.create(request); assertNotNull(response); - assertEquals(expected.getId(), response.getId()); - assertEquals(expected.getName(), response.getName()); - assertEquals(expected.getStatus(), response.getStatus()); - assertEquals(expected.getDomainId(), response.getDomainId()); + assertEquals("dacf4072-4119-4d88-932f-6c6126d3a9d1", response.getId()); + assertEquals("example.com", response.getName()); + assertEquals("pending", response.getStatus()); + assertEquals("d91cd9bd-1176-453e-8fc1-35364d380206", response.getDomainId()); + } + + @Test + public void testClaimDomain_NullOptions_ThrowsResendException() { + assertThrows(ResendException.class, () -> domainClaims.create(null)); + } + + @Test + public void testClaimDomain_ApiError_ThrowsResendException() throws ResendException { + ClaimDomainOptions request = DomainsUtil.claimDomainRequest(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(422, + "{\"name\":\"validation_error\",\"message\":\"Invalid domain name\"}", false); + + when(httpClient.perform(eq("/domains/claim"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); + + ResendException ex = assertThrows(ResendException.class, () -> domainClaims.create(request)); + assertEquals(422, (int) ex.getStatusCode()); } @Test public void testGetDomainClaim_Success() throws ResendException { - DomainClaimResponseSuccess expected = DomainsUtil.claimDomainResponse(); - String domainId = expected.getDomainId(); + String domainId = "d91cd9bd-1176-453e-8fc1-35364d380206"; + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, CLAIM_RESPONSE_JSON, true); - when(domainClaims.get(domainId)).thenReturn(expected); + when(httpClient.perform(eq("/domains/" + domainId + "/claim"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); DomainClaimResponseSuccess response = domainClaims.get(domainId); assertNotNull(response); - assertEquals(expected.getId(), response.getId()); - assertEquals(expected.getDomainId(), response.getDomainId()); - assertEquals(expected.getStatus(), response.getStatus()); + assertEquals("dacf4072-4119-4d88-932f-6c6126d3a9d1", response.getId()); + assertEquals("d91cd9bd-1176-453e-8fc1-35364d380206", response.getDomainId()); + assertEquals("pending", response.getStatus()); } @Test public void testVerifyDomainClaim_Success() throws ResendException { - DomainClaimResponseSuccess expected = DomainsUtil.claimDomainResponse(); - String domainId = expected.getDomainId(); + String domainId = "d91cd9bd-1176-453e-8fc1-35364d380206"; + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, CLAIM_RESPONSE_JSON, true); - when(domainClaims.verify(domainId)).thenReturn(expected); + when(httpClient.perform(eq("/domains/" + domainId + "/claim/verify"), anyString(), eq(HttpMethod.POST), eq(""), isNull())) + .thenReturn(httpResponse); DomainClaimResponseSuccess response = domainClaims.verify(domainId); assertNotNull(response); - assertEquals(expected.getId(), response.getId()); - assertEquals(expected.getDomainId(), response.getDomainId()); - assertEquals(expected.getStatus(), response.getStatus()); + assertEquals("dacf4072-4119-4d88-932f-6c6126d3a9d1", response.getId()); + assertEquals("d91cd9bd-1176-453e-8fc1-35364d380206", response.getDomainId()); + assertEquals("pending", response.getStatus()); } }