diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..7f24d4b --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,20 @@ +repos: + - repo: local + hooks: + - id: spotless-apply + name: Spotless Code Formatter (Apply) + entry: mvn spotless:apply + language: system + pass_filenames: false + stages: [pre-commit] + verbose: true + + # some complex rules like missing parentheses in a if, loop statements for a single line are not applied by + # mvn spotless:apply so added another check that will block the commit + - id: spotless-check + name: Spotless Code Formatter (Check) + entry: mvn spotless:check + language: system + pass_filenames: false + stages: [pre-commit] + verbose: true diff --git a/README.md b/README.md index c50e5eb..c025da0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # PhonePe B2B Payment Gateway SDK for Java -[![Maven Central](https://img.shields.io/badge/Maven%20Central-v2.1.7-blue)](https://maven-badges.herokuapp.com/maven-central/com.phonepe/pg-sdk-java) +[![Maven Central](https://img.shields.io/badge/Maven%20Central-v2.1.8-blue)](https://maven-badges.herokuapp.com/maven-central/com.phonepe/pg-sdk-java) ![Java](https://img.shields.io/badge/Java-17%2B-orange) [![License](https://img.shields.io/badge/License-Apache%202.0-green.svg)](LICENSE) @@ -36,7 +36,7 @@ Add the dependency to your project's POM file: com.phonepe pg-sdk-java - 2.1.7 + 2.1.8 ``` @@ -46,7 +46,7 @@ Add the following to your project's build.gradle file: ```gradle dependencies { - implementation 'com.phonepe:pg-sdk-java:2.1.7' + implementation 'com.phonepe:pg-sdk-java:2.1.8' } ``` diff --git a/formatter.xml b/formatter.xml new file mode 100644 index 0000000..bd267b4 --- /dev/null +++ b/formatter.xml @@ -0,0 +1,380 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 3c355cf..72ed3ba 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.phonepe pg-sdk-java - 2.1.7 + 2.1.8 B2B JAVA SDK https://github.com/PhonePe/phonepe-pg-sdk-java Phonepe PG JAVA SDK @@ -97,6 +97,11 @@ Aditi Billore aditi.billore3@gmail.com + + Yogesh-Kamble + Yogesh Kamble + ypk282000@gmail.com + @@ -133,7 +138,39 @@ + + + src/main/resources + true + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.1 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.4.0 + + + attach-javadocs + + jar + + + + com.diffplug.spotless spotless-maven-plugin @@ -141,6 +178,11 @@ origin/main + + + ${project.basedir}/formatter.xml + 4.26 + src/main/java/**/*.java src/test/java/**/*.java @@ -170,6 +212,25 @@ + + org.jacoco + jacoco-maven-plugin + 0.8.11 + + + + prepare-agent + + + + report + test + + report + + + + @@ -316,6 +377,12 @@ + + + src/main/resources + true + + org.sonatype.central diff --git a/src/main/java/com/phonepe/sdk/pg/common/constants/Headers.java b/src/main/java/com/phonepe/sdk/pg/common/constants/Headers.java index b879b75..89ee09d 100644 --- a/src/main/java/com/phonepe/sdk/pg/common/constants/Headers.java +++ b/src/main/java/com/phonepe/sdk/pg/common/constants/Headers.java @@ -15,15 +15,34 @@ */ package com.phonepe.sdk.pg.common.constants; +import java.io.InputStream; +import java.util.Properties; import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; @UtilityClass +@Slf4j public class Headers { + private final Properties properties = new Properties(); + private final String PROPERTIES_FILE_NAME = "/sdk.properties"; + + static { + try (InputStream input = Headers.class.getResourceAsStream(PROPERTIES_FILE_NAME)) { + if (input == null) { + log.error("Could not find {}", PROPERTIES_FILE_NAME); + } else { + properties.load(input); + } + } catch (Exception e) { + log.error("Failed to load SDK properties: {}", e.getMessage()); + } + } + public static final String API_VERSION = "V2"; - public static final String SUBSCRIPTION_API_VERSION = "2.1.7"; + public static final String SUBSCRIPTION_API_VERSION = properties.getProperty("sdk.version"); public static final String INTEGRATION = "API"; - public static final String SDK_VERSION = "2.1.7"; + public static final String SDK_VERSION = properties.getProperty("sdk.version"); public static final String SDK_TYPE = "BACKEND_JAVA_SDK"; public static final String SOURCE = "x-source"; public static final String SOURCE_VERSION = "x-source-version"; @@ -32,4 +51,4 @@ public class Headers { public static final String OAUTH_AUTHORIZATION = "Authorization"; public static final String CONTENT_TYPE = "Content-Type"; public static final String ACCEPT = "Accept"; -} \ No newline at end of file +} diff --git a/src/main/java/com/phonepe/sdk/pg/common/http/HttpCommand.java b/src/main/java/com/phonepe/sdk/pg/common/http/HttpCommand.java index 0dfaf2b..d72f6c6 100644 --- a/src/main/java/com/phonepe/sdk/pg/common/http/HttpCommand.java +++ b/src/main/java/com/phonepe/sdk/pg/common/http/HttpCommand.java @@ -99,7 +99,7 @@ private HttpUrl prepareHttpURL(String url) { /** * Executes the HTTP request and returns the response. * - * @return the response object + * @return the response object */ @SneakyThrows public T execute() { diff --git a/src/main/java/com/phonepe/sdk/pg/payments/v2/CustomCheckoutClient.java b/src/main/java/com/phonepe/sdk/pg/payments/v2/CustomCheckoutClient.java index 1e3ea54..1cec884 100644 --- a/src/main/java/com/phonepe/sdk/pg/payments/v2/CustomCheckoutClient.java +++ b/src/main/java/com/phonepe/sdk/pg/payments/v2/CustomCheckoutClient.java @@ -42,13 +42,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.concurrent.ConcurrentHashMap; import lombok.SneakyThrows; public class CustomCheckoutClient extends BaseClient { - private static final ConcurrentHashMap cachedInstances = - new ConcurrentHashMap<>(); private List headers; private CustomCheckoutClient( @@ -102,20 +99,8 @@ public static CustomCheckoutClient getInstance( boolean shouldPublishEvents) throws PhonePeException { final boolean shouldPublishInProd = shouldPublishEvents && env == Env.PRODUCTION; - final String requestedClientSHA = - CommonUtils.calculateSha256( - clientId, - clientSecret, - clientVersion, - env, - shouldPublishInProd, - FlowType.PG); - - return cachedInstances.computeIfAbsent( - requestedClientSHA, - key -> - new CustomCheckoutClient( - clientId, clientSecret, clientVersion, env, shouldPublishInProd)); + return new CustomCheckoutClient( + clientId, clientSecret, clientVersion, env, shouldPublishInProd); } /** diff --git a/src/main/java/com/phonepe/sdk/pg/payments/v2/StandardCheckoutClient.java b/src/main/java/com/phonepe/sdk/pg/payments/v2/StandardCheckoutClient.java index 1ec1c38..a22405c 100644 --- a/src/main/java/com/phonepe/sdk/pg/payments/v2/StandardCheckoutClient.java +++ b/src/main/java/com/phonepe/sdk/pg/payments/v2/StandardCheckoutClient.java @@ -42,14 +42,11 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.concurrent.ConcurrentHashMap; import lombok.SneakyThrows; /** The StandardCheckout client class provides methods for interacting with the PhonePe APIs. */ public class StandardCheckoutClient extends BaseClient { - private static final ConcurrentHashMap cachedInstances = - new ConcurrentHashMap<>(); private List headers; private StandardCheckoutClient( @@ -103,20 +100,8 @@ public static StandardCheckoutClient getInstance( boolean shouldPublishEvents) throws PhonePeException { final boolean shouldPublishInProd = shouldPublishEvents && env == Env.PRODUCTION; - final String requestedClientSHA = - CommonUtils.calculateSha256( - clientId, - clientSecret, - clientVersion, - env, - shouldPublishInProd, - FlowType.PG_CHECKOUT); - - return cachedInstances.computeIfAbsent( - requestedClientSHA, - key -> - new StandardCheckoutClient( - clientId, clientSecret, clientVersion, env, shouldPublishInProd)); + return new StandardCheckoutClient( + clientId, clientSecret, clientVersion, env, shouldPublishInProd); } /** diff --git a/src/main/java/com/phonepe/sdk/pg/payments/v2/models/request/CreateSdkOrderRequest.java b/src/main/java/com/phonepe/sdk/pg/payments/v2/models/request/CreateSdkOrderRequest.java index cbbd59b..066236c 100644 --- a/src/main/java/com/phonepe/sdk/pg/payments/v2/models/request/CreateSdkOrderRequest.java +++ b/src/main/java/com/phonepe/sdk/pg/payments/v2/models/request/CreateSdkOrderRequest.java @@ -35,6 +35,7 @@ public class CreateSdkOrderRequest { private PaymentFlow paymentFlow; private Long expireAfter; private List constraints; + private Boolean disablePaymentRetry; /** * Builds SDK order Request @@ -54,11 +55,13 @@ public CreateSdkOrderRequest( MetaInfo metaInfo, String message, String redirectUrl, - Long expireAfter) { + Long expireAfter, + Boolean disablePaymentRetry) { this.merchantOrderId = merchantOrderId; this.amount = amount; this.metaInfo = metaInfo; this.expireAfter = expireAfter; + this.disablePaymentRetry = disablePaymentRetry; MerchantUrls merchantUrls = MerchantUrls.builder().redirectUrl(redirectUrl).build(); this.setPaymentFlow( PgCheckoutPaymentFlow.builder() @@ -83,12 +86,14 @@ public CreateSdkOrderRequest( long amount, MetaInfo metaInfo, List constraints, - Long expireAfter) { + Long expireAfter, + Boolean disablePaymentRetry) { this.merchantOrderId = merchantOrderId; this.amount = amount; this.metaInfo = metaInfo; this.expireAfter = expireAfter; this.constraints = constraints; + this.disablePaymentRetry = disablePaymentRetry; this.setPaymentFlow(PgPaymentFlow.builder().build()); } } diff --git a/src/main/java/com/phonepe/sdk/pg/subscription/v2/SubscriptionClient.java b/src/main/java/com/phonepe/sdk/pg/subscription/v2/SubscriptionClient.java index 3640064..165f65c 100644 --- a/src/main/java/com/phonepe/sdk/pg/subscription/v2/SubscriptionClient.java +++ b/src/main/java/com/phonepe/sdk/pg/subscription/v2/SubscriptionClient.java @@ -41,13 +41,10 @@ import com.phonepe.sdk.pg.subscription.v2.models.response.SubscriptionStatusResponseV2; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.ConcurrentHashMap; import lombok.SneakyThrows; public class SubscriptionClient extends BaseClient { - private static final ConcurrentHashMap cachedInstances = - new ConcurrentHashMap<>(); private List headers; private SubscriptionClient( @@ -102,20 +99,8 @@ public static SubscriptionClient getInstance( boolean shouldPublishEvents) throws PhonePeException { final boolean shouldPublishInProd = shouldPublishEvents && env == Env.PRODUCTION; - final String requestedClientSHA = - CommonUtils.calculateSha256( - clientId, - clientSecret, - clientVersion, - env, - shouldPublishInProd, - FlowType.SUBSCRIPTION); - - return cachedInstances.computeIfAbsent( - requestedClientSHA, - key -> - new SubscriptionClient( - clientId, clientSecret, clientVersion, env, shouldPublishInProd)); + return new SubscriptionClient( + clientId, clientSecret, clientVersion, env, shouldPublishInProd); } /** diff --git a/src/main/resources/sdk.properties b/src/main/resources/sdk.properties new file mode 100644 index 0000000..325bf76 --- /dev/null +++ b/src/main/resources/sdk.properties @@ -0,0 +1 @@ +sdk.version=${project.version} \ No newline at end of file diff --git a/src/test/CreateOrderTest.java b/src/test/CreateOrderTest.java index 92eb03d..de78503 100644 --- a/src/test/CreateOrderTest.java +++ b/src/test/CreateOrderTest.java @@ -123,4 +123,197 @@ void testCreateOrderBadRequest() { Assertions.assertEquals(400, phonePeException.getHttpStatusCode()); Assertions.assertEquals("Bad Request", phonePeException.getCode()); } + + @Test + void testCreateOrderWithDisablePaymentRetryTrue() { + String redirectUrl = "https://google.com"; + + CreateSdkOrderRequest createSdkOrderRequest = + CreateSdkOrderRequest.StandardCheckoutBuilder() + .merchantOrderId(merchantOrderId) + .amount(amount) + .redirectUrl(redirectUrl) + .disablePaymentRetry(true) + .build(); + final String url = StandardCheckoutConstants.CREATE_ORDER_API; + CreateSdkOrderResponse createSdkOrderResponse = + CreateSdkOrderResponse.builder() + .expireAt(1432423) + .orderId("orderId") + .token("token") + .state("PENDING") + .build(); + + addStubForPostRequest( + url, + getHeaders(), + createSdkOrderRequest, + HttpStatus.SC_OK, + Maps.newHashMap(), + createSdkOrderResponse); + + CreateSdkOrderResponse actual = + standardCheckoutClient.createSdkOrder(createSdkOrderRequest); + Assertions.assertEquals(actual, createSdkOrderResponse); + Assertions.assertTrue(createSdkOrderRequest.getDisablePaymentRetry()); + } + + @Test + void testCreateOrderWithDisablePaymentRetryFalse() { + String redirectUrl = "https://google.com"; + + CreateSdkOrderRequest createSdkOrderRequest = + CreateSdkOrderRequest.StandardCheckoutBuilder() + .merchantOrderId(merchantOrderId) + .amount(amount) + .redirectUrl(redirectUrl) + .disablePaymentRetry(false) + .build(); + final String url = StandardCheckoutConstants.CREATE_ORDER_API; + CreateSdkOrderResponse createSdkOrderResponse = + CreateSdkOrderResponse.builder() + .expireAt(1432423) + .orderId("orderId") + .token("token") + .state("PENDING") + .build(); + + addStubForPostRequest( + url, + getHeaders(), + createSdkOrderRequest, + HttpStatus.SC_OK, + Maps.newHashMap(), + createSdkOrderResponse); + + CreateSdkOrderResponse actual = + standardCheckoutClient.createSdkOrder(createSdkOrderRequest); + Assertions.assertEquals(actual, createSdkOrderResponse); + Assertions.assertFalse(createSdkOrderRequest.getDisablePaymentRetry()); + } + + @Test + void testCreateOrderWithDisablePaymentRetryNull() { + String redirectUrl = "https://google.com"; + + CreateSdkOrderRequest createSdkOrderRequest = + CreateSdkOrderRequest.StandardCheckoutBuilder() + .merchantOrderId(merchantOrderId) + .amount(amount) + .redirectUrl(redirectUrl) + .build(); + final String url = StandardCheckoutConstants.CREATE_ORDER_API; + CreateSdkOrderResponse createSdkOrderResponse = + CreateSdkOrderResponse.builder() + .expireAt(1432423) + .orderId("orderId") + .token("token") + .state("PENDING") + .build(); + + addStubForPostRequest( + url, + getHeaders(), + createSdkOrderRequest, + HttpStatus.SC_OK, + Maps.newHashMap(), + createSdkOrderResponse); + + CreateSdkOrderResponse actual = + standardCheckoutClient.createSdkOrder(createSdkOrderRequest); + Assertions.assertEquals(actual, createSdkOrderResponse); + Assertions.assertNull(createSdkOrderRequest.getDisablePaymentRetry()); + } + + @Test + void testCreateOrderCustomCheckoutWithDisablePaymentRetryTrue() { + + CreateSdkOrderRequest createSdkOrderRequest = + CreateSdkOrderRequest.CustomCheckoutBuilder() + .merchantOrderId(merchantOrderId) + .amount(amount) + .disablePaymentRetry(true) + .build(); + final String url = CustomCheckoutConstants.CREATE_ORDER_API; + CreateSdkOrderResponse createSdkOrderResponse = + CreateSdkOrderResponse.builder() + .expireAt(1432423) + .orderId("orderId") + .token("token") + .state("PENDING") + .build(); + + addStubForPostRequest( + url, + getHeaders(), + createSdkOrderRequest, + HttpStatus.SC_OK, + Maps.newHashMap(), + createSdkOrderResponse); + + CreateSdkOrderResponse actual = customCheckoutClient.createSdkOrder(createSdkOrderRequest); + Assertions.assertEquals(actual, createSdkOrderResponse); + Assertions.assertTrue(createSdkOrderRequest.getDisablePaymentRetry()); + } + + @Test + void testCreateOrderCustomCheckoutWithDisablePaymentRetryFalse() { + + CreateSdkOrderRequest createSdkOrderRequest = + CreateSdkOrderRequest.CustomCheckoutBuilder() + .merchantOrderId(merchantOrderId) + .amount(amount) + .disablePaymentRetry(false) + .build(); + final String url = CustomCheckoutConstants.CREATE_ORDER_API; + CreateSdkOrderResponse createSdkOrderResponse = + CreateSdkOrderResponse.builder() + .expireAt(1432423) + .orderId("orderId") + .token("token") + .state("PENDING") + .build(); + + addStubForPostRequest( + url, + getHeaders(), + createSdkOrderRequest, + HttpStatus.SC_OK, + Maps.newHashMap(), + createSdkOrderResponse); + + CreateSdkOrderResponse actual = customCheckoutClient.createSdkOrder(createSdkOrderRequest); + Assertions.assertEquals(actual, createSdkOrderResponse); + Assertions.assertFalse(createSdkOrderRequest.getDisablePaymentRetry()); + } + + @Test + void testCreateOrderCustomCheckoutWithDisablePaymentRetryNull() { + + CreateSdkOrderRequest createSdkOrderRequest = + CreateSdkOrderRequest.CustomCheckoutBuilder() + .merchantOrderId(merchantOrderId) + .amount(amount) + .build(); + final String url = CustomCheckoutConstants.CREATE_ORDER_API; + CreateSdkOrderResponse createSdkOrderResponse = + CreateSdkOrderResponse.builder() + .expireAt(1432423) + .orderId("orderId") + .token("token") + .state("PENDING") + .build(); + + addStubForPostRequest( + url, + getHeaders(), + createSdkOrderRequest, + HttpStatus.SC_OK, + Maps.newHashMap(), + createSdkOrderResponse); + + CreateSdkOrderResponse actual = customCheckoutClient.createSdkOrder(createSdkOrderRequest); + Assertions.assertEquals(actual, createSdkOrderResponse); + Assertions.assertNull(createSdkOrderRequest.getDisablePaymentRetry()); + } } diff --git a/src/test/SingletonCustomCheckoutTest.java b/src/test/SingletonCustomCheckoutTest.java deleted file mode 100644 index a2c9198..0000000 --- a/src/test/SingletonCustomCheckoutTest.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright (c) 2025 Original Author(s), PhonePe India Pvt. Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching; - -import com.google.common.collect.Maps; -import com.phonepe.sdk.pg.Env; -import com.phonepe.sdk.pg.common.models.request.PgPaymentRequest; -import com.phonepe.sdk.pg.common.models.response.PgPaymentResponse; -import com.phonepe.sdk.pg.common.tokenhandler.OAuthResponse; -import com.phonepe.sdk.pg.payments.v2.CustomCheckoutClient; -import com.phonepe.sdk.pg.payments.v2.customcheckout.CustomCheckoutConstants; -import okhttp3.FormBody; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import wiremock.org.apache.http.HttpStatus; - -public class SingletonCustomCheckoutTest extends BaseSetup { - - FormBody formBody = - new FormBody.Builder() - .add("client_id", clientId) - .add("client_secret", clientSecret) - .add("grant_type", "client_credentials") - .add("client_version", String.valueOf(clientVersion)) - .build(); - - @Test - void testSingletonViaGetInstance() { - CustomCheckoutClient customCheckoutClient1 = - CustomCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - CustomCheckoutClient customCheckoutClient2 = - CustomCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - - Assertions.assertEquals(customCheckoutClient1, customCheckoutClient2); - } - - @Test - void testSingletonWithDiffParameters() { - CustomCheckoutClient customCheckoutClient1 = - CustomCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - CustomCheckoutClient customCheckoutClient2 = CustomCheckoutClient.getInstance( - "clientId2", "clientSecret2", 1, Env.TEST); - Assertions.assertNotEquals(customCheckoutClient1, customCheckoutClient2); - Assertions.assertNotNull(customCheckoutClient2); - } - - @Test - void testMultipleSameClientSingleAuthCall() { - wireMockServer.resetRequests(); - customCheckoutClient.getTokenService().setOAuthResponse(null); - PgPaymentRequest pgPaymentRequest = - PgPaymentRequest.UpiQrRequestBuilder() - .merchantOrderId("MerchantOrderId") - .amount(100) - .build(); - - PgPaymentResponse pgPaymentResponse = - PgPaymentResponse.builder() - .orderId(String.valueOf(java.time.Instant.now().getEpochSecond())) - .state("PENDING") - .expireAt(java.time.Instant.now().getEpochSecond()) - .qrData("qrData") - .build(); - - long currentTime = java.time.Instant.now().getEpochSecond(); - OAuthResponse oAuthResponse = - OAuthResponse.builder() - .accessToken("accessToken") - .encryptedAccessToken("encryptedAccessToken") - .expiresAt(currentTime + 200) - .expiresIn(453543) - .issuedAt(currentTime) - .refreshToken("refreshToken") - .tokenType("O-Bearer") - .sessionExpiresAt(currentTime + 200) - .build(); - - CustomCheckoutClient customCheckoutClient1 = - CustomCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - CustomCheckoutClient customCheckoutClient2 = - CustomCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - CustomCheckoutClient customCheckoutClient3 = - CustomCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - CustomCheckoutClient customCheckoutClient4 = - CustomCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - - final String url = CustomCheckoutConstants.PAY_API; - addStubForPostRequest( - url, - getHeaders(), - pgPaymentRequest, - HttpStatus.SC_OK, - Maps.newHashMap(), - pgPaymentResponse); - addStubForFormDataPostRequest( - authUrl, - getAuthHeaders(), - formBody, - HttpStatus.SC_OK, - Maps.newHashMap(), - oAuthResponse); - PgPaymentResponse actual = customCheckoutClient1.pay(pgPaymentRequest); - actual = customCheckoutClient2.pay(pgPaymentRequest); - actual = customCheckoutClient3.pay(pgPaymentRequest); - actual = customCheckoutClient4.pay(pgPaymentRequest); - - wireMockServer.verify(1, postRequestedFor(urlPathMatching(authUrl))); - } - - @Test - void testMultipleDifferentClientMultipleAuthCall() { - String clientId = "clientId_for_auth1"; - String clientSecret = "clientSecret_for_auth1"; - int clientVersion = 1; - - String clientId1 = "clientId1"; - String clientSecret1 = "clientSecret1"; - - FormBody mockFormBody = - new FormBody.Builder() - .add("client_id", clientId) - .add("client_secret", clientSecret) - .add("grant_type", "client_credentials") - .add("client_version", String.valueOf(clientVersion)) - .build(); - - wireMockServer.resetRequests(); - String redirectUrl = "https://redirectUrl.com"; - PgPaymentRequest pgPaymentRequest = - PgPaymentRequest.UpiQrRequestBuilder() - .merchantOrderId("MerchantOrderId") - .amount(100) - .build(); - - PgPaymentResponse pgPaymentResponse = - PgPaymentResponse.builder() - .orderId(String.valueOf(java.time.Instant.now().getEpochSecond())) - .state("PENDING") - .expireAt(java.time.Instant.now().getEpochSecond()) - .qrData("qrData") - .build(); - - long currentTime = java.time.Instant.now().getEpochSecond(); - OAuthResponse oAuthResponse = - OAuthResponse.builder() - .accessToken("accessToken") - .encryptedAccessToken("encryptedAccessToken") - .expiresAt(currentTime + 200) - .expiresIn(453543) - .issuedAt(currentTime) - .refreshToken("refreshToken") - .tokenType("O-Bearer") - .sessionExpiresAt(currentTime + 200) - .build(); - - CustomCheckoutClient customCheckoutClient1 = - CustomCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - CustomCheckoutClient customCheckoutClient2 = - CustomCheckoutClient.getInstance(clientId1, clientSecret1, clientVersion, env); - - final String url = CustomCheckoutConstants.PAY_API; - addStubForPostRequest( - url, - getHeaders(), - pgPaymentRequest, - HttpStatus.SC_OK, - Maps.newHashMap(), - pgPaymentResponse); - - FormBody mockFormBody1 = - new FormBody.Builder() - .add("client_id", clientId1) - .add("client_secret", clientSecret1) - .add("grant_type", "client_credentials") - .add("client_version", String.valueOf(clientVersion)) - .build(); - - addStubForFormDataPostRequest( - authUrl, - getAuthHeaders(), - mockFormBody, - HttpStatus.SC_OK, - Maps.newHashMap(), - oAuthResponse); - - addStubForFormDataPostRequest( - authUrl, - getAuthHeaders(), - mockFormBody1, - HttpStatus.SC_OK, - Maps.newHashMap(), - oAuthResponse); - - customCheckoutClient1.pay(pgPaymentRequest); - customCheckoutClient2.pay(pgPaymentRequest); - - wireMockServer.verify(2, postRequestedFor(urlPathMatching(authUrl))); - } - - @Test - void testSingletonWithDifferentEnvironments() { - CustomCheckoutClient customCheckoutClientProd = - CustomCheckoutClient.getInstance(clientId, clientSecret, clientVersion, Env.PRODUCTION); - CustomCheckoutClient customCheckoutClientSandbox = - CustomCheckoutClient.getInstance(clientId, clientSecret, clientVersion, Env.SANDBOX); - - Assertions.assertNotEquals(customCheckoutClientProd, customCheckoutClientSandbox); - - CustomCheckoutClient customCheckoutClientProd2 = - CustomCheckoutClient.getInstance(clientId, clientSecret, clientVersion, Env.PRODUCTION); - Assertions.assertEquals(customCheckoutClientProd, customCheckoutClientProd2); - - CustomCheckoutClient customCheckoutClientSandbox2 = - CustomCheckoutClient.getInstance(clientId, clientSecret, clientVersion, Env.SANDBOX); - Assertions.assertEquals(customCheckoutClientSandbox, customCheckoutClientSandbox2); - } -} diff --git a/src/test/SingletonStandardCheckoutTest.java b/src/test/SingletonStandardCheckoutTest.java deleted file mode 100644 index 0ce88af..0000000 --- a/src/test/SingletonStandardCheckoutTest.java +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (c) 2025 Original Author(s), PhonePe India Pvt. Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching; -import static javax.ws.rs.core.MediaType.APPLICATION_JSON; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; -import com.phonepe.sdk.pg.Env; -import com.phonepe.sdk.pg.common.constants.Headers; -import com.phonepe.sdk.pg.common.tokenhandler.OAuthResponse; -import com.phonepe.sdk.pg.payments.v2.StandardCheckoutClient; -import com.phonepe.sdk.pg.payments.v2.models.request.StandardCheckoutPayRequest; -import com.phonepe.sdk.pg.payments.v2.models.response.StandardCheckoutPayResponse; -import com.phonepe.sdk.pg.payments.v2.standardcheckout.StandardCheckoutConstants; -import java.util.Map; -import okhttp3.FormBody; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import wiremock.org.apache.http.HttpStatus; - -public class SingletonStandardCheckoutTest extends BaseSetup { - - FormBody formBody = - new FormBody.Builder() - .add("client_id", clientId) - .add("client_secret", clientSecret) - .add("grant_type", "client_credentials") - .add("client_version", String.valueOf(clientVersion)) - .build(); - - @Test - void testSingletonViaGetInstance() { - StandardCheckoutClient standardCheckoutClient1 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - StandardCheckoutClient standardCheckoutClient2 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - - Assertions.assertEquals(standardCheckoutClient1, standardCheckoutClient2); - } - - @Test - void testSingletonWithDiffParameters() { - StandardCheckoutClient standardCheckoutClient1 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - StandardCheckoutClient standardCheckoutClient2 = - StandardCheckoutClient.getInstance("clientId2", "clientSecret2", 1, Env.TEST); - Assertions.assertNotEquals(standardCheckoutClient1, standardCheckoutClient2); - Assertions.assertNotNull(standardCheckoutClient1); - Assertions.assertNotNull(standardCheckoutClient2); - } - - @Test - void testMultipleSameClientSingleAuthCall() { - wireMockServer.resetRequests(); - standardCheckoutClient.getTokenService().setOAuthResponse(null); - String redirectUrl = "https://redirectUrl.com"; - StandardCheckoutPayRequest standardCheckoutPayRequest = - StandardCheckoutPayRequest.builder() - .merchantOrderId("merchantOrderId") - .amount(100) - .redirectUrl(redirectUrl) - .build(); - - StandardCheckoutPayResponse standardCheckoutResponse = - StandardCheckoutPayResponse.builder() - .orderId(String.valueOf(java.time.Instant.now().getEpochSecond())) - .state("PENDING") - .expireAt(java.time.Instant.now().getEpochSecond()) - .redirectUrl("https://google.com") - .build(); - - long currentTime = java.time.Instant.now().getEpochSecond(); - OAuthResponse oAuthResponse = - OAuthResponse.builder() - .accessToken("accessToken") - .encryptedAccessToken("encryptedAccessToken") - .expiresAt(currentTime + 200) - .expiresIn(453543) - .issuedAt(currentTime) - .refreshToken("refreshToken") - .tokenType("O-Bearer") - .sessionExpiresAt(currentTime + 200) - .build(); - - StandardCheckoutClient standardCheckoutClient1 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - StandardCheckoutClient standardCheckoutClient2 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - StandardCheckoutClient standardCheckoutClient3 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - StandardCheckoutClient standardCheckoutClient4 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - - final String url = StandardCheckoutConstants.PAY_API; - addStubForPostRequest( - url, - getHeaders(), - standardCheckoutPayRequest, - HttpStatus.SC_OK, - Maps.newHashMap(), - standardCheckoutResponse); - addStubForFormDataPostRequest( - authUrl, - getAuthHeaders(), - formBody, - HttpStatus.SC_OK, - Maps.newHashMap(), - oAuthResponse); - StandardCheckoutPayResponse actual = - standardCheckoutClient1.pay(standardCheckoutPayRequest); - actual = standardCheckoutClient2.pay(standardCheckoutPayRequest); - actual = standardCheckoutClient3.pay(standardCheckoutPayRequest); - actual = standardCheckoutClient4.pay(standardCheckoutPayRequest); - - wireMockServer.verify(1, postRequestedFor(urlPathMatching(authUrl))); - } - - @Test - void testMultipleDifferentClientSingleAuthCall() { - String clientId = "clientId_for_auth1"; - String clientSecret = "clientSecret_for_auth1"; - String clientId1 = "clientId_for_auth2"; - String clientSecret1 = "clientSecret_for_auth2"; - int clientVersion = 1; - - FormBody mockFormBody = - new FormBody.Builder() - .add("client_id", clientId) - .add("client_secret", clientSecret) - .add("grant_type", "client_credentials") - .add("client_version", String.valueOf(clientVersion)) - .build(); - - FormBody mockFormBody1 = - new FormBody.Builder() - .add("client_id", clientId1) - .add("client_secret", clientSecret1) - .add("grant_type", "client_credentials") - .add("client_version", String.valueOf(clientVersion)) - .build(); - - wireMockServer.resetRequests(); - String redirectUrl = "https://redirectUrl.com"; - StandardCheckoutPayRequest standardCheckoutPayRequest = - StandardCheckoutPayRequest.builder() - .merchantOrderId("merchantOrderId") - .amount(100) - .redirectUrl(redirectUrl) - .build(); - - StandardCheckoutPayResponse standardCheckoutResponse = - StandardCheckoutPayResponse.builder() - .orderId(String.valueOf(java.time.Instant.now().getEpochSecond())) - .state("PENDING") - .expireAt(java.time.Instant.now().getEpochSecond()) - .redirectUrl("https://google.com") - .build(); - - long currentTime = java.time.Instant.now().getEpochSecond(); - OAuthResponse oAuthResponse = - OAuthResponse.builder() - .accessToken("accessToken") - .encryptedAccessToken("encryptedAccessToken") - .expiresAt(currentTime + 200) - .expiresIn(453543) - .issuedAt(currentTime) - .refreshToken("refreshToken") - .tokenType("O-Bearer") - .sessionExpiresAt(currentTime + 200) - .build(); - - StandardCheckoutClient standardCheckoutClient1 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - StandardCheckoutClient standardCheckoutClient2 = - StandardCheckoutClient.getInstance(clientId1, clientSecret1, clientVersion, env); - - final String url = StandardCheckoutConstants.PAY_API; - addStubForPostRequest( - url, - getHeaders(), - standardCheckoutPayRequest, - HttpStatus.SC_OK, - Maps.newHashMap(), - standardCheckoutResponse); - addStubForFormDataPostRequest( - authUrl, - getAuthHeaders(), - mockFormBody, - HttpStatus.SC_OK, - Maps.newHashMap(), - oAuthResponse); - addStubForFormDataPostRequest( - authUrl, - getAuthHeaders(), - mockFormBody1, - HttpStatus.SC_OK, - Maps.newHashMap(), - oAuthResponse); - standardCheckoutClient1.pay(standardCheckoutPayRequest); - standardCheckoutClient2.pay(standardCheckoutPayRequest); - - wireMockServer.verify(2, postRequestedFor(urlPathMatching(authUrl))); - } - - public Map getHeaders() { - return ImmutableMap.builder() - .put(Headers.CONTENT_TYPE, APPLICATION_JSON) - .put(Headers.SOURCE, Headers.INTEGRATION) - .put(Headers.SOURCE_VERSION, Headers.API_VERSION) - .put(Headers.SOURCE_PLATFORM, Headers.SDK_TYPE) - .put(Headers.SOURCE_PLATFORM_VERSION, Headers.SDK_VERSION) - .put(Headers.OAUTH_AUTHORIZATION, "O-Bearer accessToken") - .build(); - } - - @Test - void testSingletonWithDifferentEnvironments() { - StandardCheckoutClient standardCheckoutClientProd = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, Env.PRODUCTION); - StandardCheckoutClient standardCheckoutClientSandbox = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, Env.SANDBOX); - - Assertions.assertNotEquals(standardCheckoutClientProd, standardCheckoutClientSandbox); - - StandardCheckoutClient standardCheckoutClientProd2 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, Env.PRODUCTION); - Assertions.assertEquals(standardCheckoutClientProd, standardCheckoutClientProd2); - - StandardCheckoutClient standardCheckoutClientSandbox2 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, Env.SANDBOX); - Assertions.assertEquals(standardCheckoutClientSandbox, standardCheckoutClientSandbox2); - } -} diff --git a/src/test/SingletonSubscriptionClientTest.java b/src/test/SingletonSubscriptionClientTest.java deleted file mode 100644 index 76ad08d..0000000 --- a/src/test/SingletonSubscriptionClientTest.java +++ /dev/null @@ -1,61 +0,0 @@ - -/* - * Copyright (c) 2025 Original Author(s), PhonePe India Pvt. Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import com.phonepe.sdk.pg.Env; -import com.phonepe.sdk.pg.subscription.v2.SubscriptionClient; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -class SingletonSubscriptionClientTest extends BaseSetup { - - @Test - void testSingletonViaGetInstance() { - SubscriptionClient subscriptionClient1 = - SubscriptionClient.getInstance(clientId, clientSecret, clientVersion, env); - SubscriptionClient subscriptionClient2 = - SubscriptionClient.getInstance(clientId, clientSecret, clientVersion, env); - - Assertions.assertEquals(subscriptionClient1, subscriptionClient2); - } - - @Test - void testSingletonWithDiffParameters() { - SubscriptionClient subscriptionClient1 = - SubscriptionClient.getInstance(clientId, clientSecret, clientVersion, env); - SubscriptionClient subscriptionClient2 = SubscriptionClient.getInstance( - "clientId2", "clientSecret2", 1, Env.TEST); - Assertions.assertNotEquals(subscriptionClient1, subscriptionClient2); - Assertions.assertNotNull(subscriptionClient2); - } - - @Test - void testSingletonWithDifferentEnvironments() { - SubscriptionClient subscriptionClientProd = - SubscriptionClient.getInstance(clientId, clientSecret, clientVersion, Env.PRODUCTION); - SubscriptionClient subscriptionClientSandbox = - SubscriptionClient.getInstance(clientId, clientSecret, clientVersion, Env.SANDBOX); - - Assertions.assertNotEquals(subscriptionClientProd, subscriptionClientSandbox); - - SubscriptionClient subscriptionClientProd2 = - SubscriptionClient.getInstance(clientId, clientSecret, clientVersion, Env.PRODUCTION); - Assertions.assertEquals(subscriptionClientProd, subscriptionClientProd2); - - SubscriptionClient subscriptionClientSandbox2 = - SubscriptionClient.getInstance(clientId, clientSecret, clientVersion, Env.SANDBOX); - Assertions.assertEquals(subscriptionClientSandbox, subscriptionClientSandbox2); - } -} diff --git a/src/test/SingletonTest.java b/src/test/SingletonTest.java deleted file mode 100644 index 9b94cb6..0000000 --- a/src/test/SingletonTest.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2025 Original Author(s), PhonePe India Pvt. Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching; - -import com.google.common.collect.Maps; -import com.phonepe.sdk.pg.Env; -import com.phonepe.sdk.pg.common.tokenhandler.OAuthResponse; -import com.phonepe.sdk.pg.payments.v2.StandardCheckoutClient; -import com.phonepe.sdk.pg.payments.v2.models.request.StandardCheckoutPayRequest; -import com.phonepe.sdk.pg.payments.v2.models.response.StandardCheckoutPayResponse; -import com.phonepe.sdk.pg.payments.v2.standardcheckout.StandardCheckoutConstants; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import wiremock.org.apache.http.HttpStatus; - -public class SingletonTest extends BaseSetupWithOAuth { - - @Test - void testSingletonViaGetInstance() { - StandardCheckoutClient standardCheckoutClient1 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - StandardCheckoutClient standardCheckoutClient2 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - - Assertions.assertEquals(standardCheckoutClient1, standardCheckoutClient2); - } - - @Test - void testSingletonWithDiffParameters() { - StandardCheckoutClient standardCheckoutClient1 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - StandardCheckoutClient standardCheckoutClient2 = StandardCheckoutClient.getInstance( - "clientId2", "clientSecret2", 1, Env.TEST); - Assertions.assertNotNull(standardCheckoutClient2); - Assertions.assertNotEquals(standardCheckoutClient1, standardCheckoutClient2); - } - - @Test - void testMultipleClientSingleAuthCall() { - wireMockServer.resetRequests(); - standardCheckoutClient.getTokenService().setOAuthResponse(null); - String redirectUrl = "https://redirectUrl.com"; - StandardCheckoutPayRequest standardCheckoutPayRequest = - StandardCheckoutPayRequest.builder() - .merchantOrderId("merchantOrderId") - .amount(100) - .redirectUrl(redirectUrl) - .build(); - - StandardCheckoutPayResponse standardCheckoutResponse = - StandardCheckoutPayResponse.builder() - .orderId(String.valueOf(java.time.Instant.now().getEpochSecond())) - .state("PENDING") - .expireAt(java.time.Instant.now().getEpochSecond()) - .redirectUrl("https://google.com") - .build(); - - long currentTime = java.time.Instant.now().getEpochSecond(); - OAuthResponse oAuthResponse = - OAuthResponse.builder() - .accessToken("accessToken") - .encryptedAccessToken("encryptedAccessToken") - .expiresAt(currentTime + 200) - .expiresIn(453543) - .issuedAt(currentTime) - .refreshToken("refreshToken") - .tokenType("O-Bearer") - .sessionExpiresAt(currentTime + 200) - .build(); - - StandardCheckoutClient standardCheckoutClient1 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - StandardCheckoutClient standardCheckoutClient2 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - StandardCheckoutClient standardCheckoutClient3 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - StandardCheckoutClient standardCheckoutClient4 = - StandardCheckoutClient.getInstance(clientId, clientSecret, clientVersion, env); - - final String url = StandardCheckoutConstants.PAY_API; - addStubForPostRequest( - url, - getHeaders(), - standardCheckoutPayRequest, - HttpStatus.SC_OK, - Maps.newHashMap(), - standardCheckoutResponse); - addStubForFormDataPostRequest( - authUrl, - getAuthHeaders(), - formBody, - HttpStatus.SC_OK, - Maps.newHashMap(), - oAuthResponse); - standardCheckoutClient1.pay(standardCheckoutPayRequest); - standardCheckoutClient2.pay(standardCheckoutPayRequest); - standardCheckoutClient3.pay(standardCheckoutPayRequest); - standardCheckoutClient4.pay(standardCheckoutPayRequest); - - wireMockServer.verify(1, postRequestedFor(urlPathMatching(authUrl))); - } -} diff --git a/src/test/headers/HeadersTest.java b/src/test/headers/HeadersTest.java new file mode 100644 index 0000000..97dd23e --- /dev/null +++ b/src/test/headers/HeadersTest.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2025 Original Author(s), PhonePe India Pvt. Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package headers; + +import org.junit.jupiter.api.Assertions; + +import com.phonepe.sdk.pg.common.constants.Headers; +import java.io.InputStream; +import java.util.Properties; +import org.junit.jupiter.api.Test; + +class HeadersTest { + + /** + * Test that SDK_VERSION is loaded from properties file and not null + */ + @Test + void testSDKVersionIsLoadedFromProperties() { + Assertions.assertNotNull(Headers.SDK_VERSION, "SDK_VERSION should not be null"); + Assertions.assertFalse(Headers.SDK_VERSION.isEmpty(), "SDK_VERSION should not be empty"); + } + + /** + * Test that SDK_VERSION follows semantic versioning format (e.g., 2.1.8) + */ + @Test + void testSDKVersionFormat() { + Assertions.assertTrue( + Headers.SDK_VERSION.matches("\\d+\\.\\d+\\.\\d+"), + "SDK_VERSION should match semantic version format (X.Y.Z). Got: " + + Headers.SDK_VERSION); + } + + /** + * Test that SUBSCRIPTION_API_VERSION is loaded from properties file and not null + */ + @Test + void testSubscriptionAPIVersionIsLoadedFromProperties() { + Assertions.assertNotNull( + Headers.SUBSCRIPTION_API_VERSION, "SUBSCRIPTION_API_VERSION should not be null"); + Assertions.assertFalse( + Headers.SUBSCRIPTION_API_VERSION.isEmpty(), + "SUBSCRIPTION_API_VERSION should not be empty"); + } + + /** + * Test that both SDK_VERSION and SUBSCRIPTION_API_VERSION have the same value + * since they both load from the same property + */ + @Test + void testSDKVersionMatchesSubscriptionAPIVersion() { + Assertions.assertEquals( + Headers.SDK_VERSION, + Headers.SUBSCRIPTION_API_VERSION, + "SDK_VERSION and SUBSCRIPTION_API_VERSION should have the same value"); + } + + /** + * Test that properties file exists and is accessible in classpath + */ + @Test + void testPropertiesFileExists() { + InputStream input = getClass().getResourceAsStream("/sdk.properties"); + Assertions.assertNotNull(input, "sdk.properties file should exist in classpath"); + } + + /** + * Test that properties file has been filtered by Maven + * (i.e., ${project.version} has been replaced with actual version) + */ + @Test + void testPropertiesFileIsFiltered() throws Exception { + Properties properties = new Properties(); + try (InputStream input = getClass().getResourceAsStream("/sdk.properties")) { + Assertions.assertNotNull(input, "sdk.properties should be available"); + properties.load(input); + + String version = properties.getProperty("sdk.version"); + Assertions.assertNotNull(version, "sdk.version property should exist"); + Assertions.assertFalse( + version.contains("${"), + "sdk.version should not contain unresolved Maven placeholders like ${project.version}. Got: " + + version); + Assertions.assertTrue( + version.matches("\\d+\\.\\d+\\.\\d+"), + "sdk.version should be in semantic version format. Got: " + version); + } + } + + /** + * Test that all header constants are not null + */ + @Test + void testAllConstantsAreNotNull() { + Assertions.assertNotNull(Headers.API_VERSION, "API_VERSION should not be null"); + Assertions.assertNotNull( + Headers.SUBSCRIPTION_API_VERSION, "SUBSCRIPTION_API_VERSION should not be null"); + Assertions.assertNotNull(Headers.INTEGRATION, "INTEGRATION should not be null"); + Assertions.assertNotNull(Headers.SDK_VERSION, "SDK_VERSION should not be null"); + Assertions.assertNotNull(Headers.SDK_TYPE, "SDK_TYPE should not be null"); + Assertions.assertNotNull(Headers.SOURCE, "SOURCE should not be null"); + Assertions.assertNotNull(Headers.SOURCE_VERSION, "SOURCE_VERSION should not be null"); + Assertions.assertNotNull(Headers.SOURCE_PLATFORM, "SOURCE_PLATFORM should not be null"); + Assertions.assertNotNull( + Headers.SOURCE_PLATFORM_VERSION, "SOURCE_PLATFORM_VERSION should not be null"); + Assertions.assertNotNull(Headers.OAUTH_AUTHORIZATION, "OAUTH_AUTHORIZATION should not be null"); + Assertions.assertNotNull(Headers.CONTENT_TYPE, "CONTENT_TYPE should not be null"); + Assertions.assertNotNull(Headers.ACCEPT, "ACCEPT should not be null"); + } + + /** + * Test that SDK_VERSION matches the expected format and contains valid version parts + */ + @Test + void testSDKVersionComponents() { + String[] versionParts = Headers.SDK_VERSION.split("\\."); + Assertions.assertEquals( + 3, + versionParts.length, + "SDK_VERSION should have 3 parts (major.minor.patch). Got: " + + Headers.SDK_VERSION); + + // Each part should be a number + for (int i = 0; i < versionParts.length; i++) { + Assertions.assertTrue( + versionParts[i].matches("\\d+"), + "Version part " + i + " should be numeric. Got: " + versionParts[i]); + } + } + + /** + * Test that Header values don't contain common problematic characters + */ + @Test + void testHeaderValuesDoNotContainProblematicCharacters() { + // Version values should not contain whitespace or special characters + Assertions.assertFalse( + Headers.SDK_VERSION.contains(" "), + "SDK_VERSION should not contain whitespace"); + Assertions.assertFalse( + Headers.SUBSCRIPTION_API_VERSION.contains(" "), + "SUBSCRIPTION_API_VERSION should not contain whitespace"); + Assertions.assertFalse( + Headers.API_VERSION.contains(" "), "API_VERSION should not contain whitespace"); + } + + /** + * Integration test: Verify that properties loaded in static block + * match what can be read directly from the file + */ + @Test + void testStaticBlockPropertiesMatchFileContent() throws Exception { + Properties fileProperties = new Properties(); + try (InputStream input = getClass().getResourceAsStream("/sdk.properties")) { + fileProperties.load(input); + } + + String fileVersion = fileProperties.getProperty("sdk.version"); + Assertions.assertEquals( + Headers.SDK_VERSION, + fileVersion, + "SDK_VERSION from Headers should match sdk.version from file"); + Assertions.assertEquals( + Headers.SUBSCRIPTION_API_VERSION, + fileVersion, + "SUBSCRIPTION_API_VERSION from Headers should match sdk.version from file"); + } +}