Skip to content

Commit

Permalink
Merge pull request #51 from alexanderjordanbaker/ImproveUnitTestCoverage
Browse files Browse the repository at this point in the history
Improve unit test coverage
  • Loading branch information
alexanderjordanbaker authored Nov 3, 2023
2 parents bc8a168 + ff4fad1 commit 06e21ee
Show file tree
Hide file tree
Showing 47 changed files with 1,077 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ public HistoryResponse getTransactionHistory(String transactionId, String revisi
* @see <a href="https://developer.apple.com/documentation/appstoreserverapi/get_transaction_info">Get Transaction Info</a>
*/
public TransactionInfoResponse getTransactionInfo(String transactionId) throws APIException, IOException {
return makeHttpCall("inApps/v1/transactions/" + transactionId, "GET", Map.of(), null, TransactionInfoResponse.class);
return makeHttpCall("/inApps/v1/transactions/" + transactionId, "GET", Map.of(), null, TransactionInfoResponse.class);
}

/**
Expand Down
17 changes: 14 additions & 3 deletions src/main/java/com/apple/itunes/storekit/model/StatusResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class StatusResponse {
private static final String SERIALIZED_NAME_APP_APPLE_ID = "appAppleId";
private static final String SERIALIZED_NAME_DATA = "data";
@SerializedName(SERIALIZED_NAME_ENVIRONMENT)
private Environment environment;
private String environment;
@SerializedName(SERIALIZED_NAME_BUNDLE_ID)
private String bundleId;
@SerializedName(SERIALIZED_NAME_APP_APPLE_ID)
Expand All @@ -32,7 +32,7 @@ public StatusResponse() {
}

public StatusResponse environment(Environment environment) {
this.environment = environment;
this.environment = environment != null ? environment.getValue() : null;
return this;
}

Expand All @@ -43,11 +43,22 @@ public StatusResponse environment(Environment environment) {
* @see <a href="https://developer.apple.com/documentation/appstoreserverapi/environment">environment</a>
**/
public Environment getEnvironment() {
return environment != null ? Environment.fromValue(environment) : null;
}

/**
* @see #getEnvironment()
*/
public String getRawEnvironment() {
return environment;
}

public void setEnvironment(Environment environment) {
this.environment = environment;
this.environment = environment != null ? environment.getValue() : null;
}

public void setRawEnvironment(String rawEnvironment) {
this.environment = rawEnvironment;
}

public StatusResponse bundleId(String bundleId) {
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@

package com.apple.itunes.storekit.client;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.io.InputStream;

class BearerTokenAuthenticatorTest {

@Test
void testConstructor() throws Exception {
try (InputStream key = this.getClass().getClassLoader().getResourceAsStream("testSigningKey.p8")) {
new BearerTokenAuthenticator(new String(key.readAllBytes()), "keyId", "issuerId", "bundleId");
void testCreatingToken() throws Exception {
try (InputStream key = this.getClass().getClassLoader().getResourceAsStream("certs/testSigningKey.p8")) {
var tokenGenerator = new BearerTokenAuthenticator(new String(key.readAllBytes()), "keyId", "issuerId", "bundleId");
String token = tokenGenerator.generateToken();
Assertions.assertNotNull(token);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) 2023 Apple Inc. Licensed under MIT License.

package com.apple.itunes.storekit.migration;

import com.apple.itunes.storekit.util.TestingUtility;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.io.IOException;

public class ReceiptUtilityTest {

private static final String APP_RECEIPT_EXPECTED_TRANSACTION_ID = "0";
private static final String TRANSACTION_RECEIPT_EXPECTED_TRANSACTION_ID = "33993399";

@Test
public void testXcodeAppReceiptExtractionWithNoTransactions() throws IOException {
String receipt = TestingUtility.readFile("xcode/xcode-app-receipt-empty");

ReceiptUtility util = new ReceiptUtility();
String extractedTransactionId = util.extractTransactionIdFromAppReceipt(receipt);

Assertions.assertNull(extractedTransactionId);
}

@Test
public void testXcodeAppReceiptExtractionWithTransactions() throws IOException {
String receipt = TestingUtility.readFile("xcode/xcode-app-receipt-with-transaction");

ReceiptUtility util = new ReceiptUtility();
String extractedTransactionId = util.extractTransactionIdFromAppReceipt(receipt);

Assertions.assertEquals(APP_RECEIPT_EXPECTED_TRANSACTION_ID, extractedTransactionId);
}

@Test
public void testTransactionReceiptExtraction() throws IOException {
String receipt = TestingUtility.readFile("mock_signed_data/legacyTransaction");

ReceiptUtility util = new ReceiptUtility();
String extractedTransactionId = util.extractTransactionIdFromTransactionReceipt(receipt);

Assertions.assertEquals(TRANSACTION_RECEIPT_EXPECTED_TRANSACTION_ID, extractedTransactionId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) 2023 Apple Inc. Licensed under MIT License.

package com.apple.itunes.storekit.model;


import com.apple.itunes.storekit.util.SignedDataCreator;
import com.apple.itunes.storekit.util.TestingUtility;
import com.apple.itunes.storekit.verification.VerificationException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.UUID;

public class AppTransactionTest {

@Test
public void testAppTransactionDecoding() throws IOException, NoSuchAlgorithmException, VerificationException {
String signedAppTransaction = SignedDataCreator.createSignedDataFromJson("models/appTransaction.json");

AppTransaction appTransaction = TestingUtility.getSignedPayloadVerifier().verifyAndDecodeAppTransaction(signedAppTransaction);

Assertions.assertEquals(Environment.LOCAL_TESTING, appTransaction.getReceiptType());
Assertions.assertEquals("LocalTesting", appTransaction.getRawReceiptType());
Assertions.assertEquals(531412, appTransaction.getAppAppleId());
Assertions.assertEquals("com.example", appTransaction.getBundleId());
Assertions.assertEquals("1.2.3", appTransaction.getApplicationVersion());
Assertions.assertEquals(512, appTransaction.versionExternalIdentifier());
Assertions.assertEquals(1698148900000L, appTransaction.getReceiptCreationDate());
Assertions.assertEquals(1698148800000L, appTransaction.originalPurchaseDate());
Assertions.assertEquals("1.1.2", appTransaction.getOriginalApplicationVersion());
Assertions.assertEquals("device_verification_value", appTransaction.getDeviceVerification());
Assertions.assertEquals(UUID.fromString("48ccfa42-7431-4f22-9908-7e88983e105a"), appTransaction.getDeviceVerificationNonce());
Assertions.assertEquals(1698148700000L, appTransaction.getPreorderDate());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) 2023 Apple Inc. Licensed under MIT License.

package com.apple.itunes.storekit.model;

import com.apple.itunes.storekit.util.SignedDataCreator;
import com.apple.itunes.storekit.util.TestingUtility;
import com.apple.itunes.storekit.verification.VerificationException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;

public class JWSRenewalInfoDecodedPayloadTest {

@Test
public void testRenewalInfoDecoding() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, VerificationException {
String signedRenewalInfo = SignedDataCreator.createSignedDataFromJson("models/signedRenewalInfo.json");

JWSRenewalInfoDecodedPayload renewalInfo = TestingUtility.getSignedPayloadVerifier().verifyAndDecodeRenewalInfo(signedRenewalInfo);

Assertions.assertEquals(ExpirationIntent.CUSTOMER_CANCELLED, renewalInfo.getExpirationIntent());
Assertions.assertEquals(1, renewalInfo.getRawExpirationIntent());
Assertions.assertEquals("12345", renewalInfo.getOriginalTransactionId());
Assertions.assertEquals("com.example.product.2", renewalInfo.getAutoRenewProductId());
Assertions.assertEquals("com.example.product", renewalInfo.getProductId());
Assertions.assertEquals(AutoRenewStatus.ON, renewalInfo.getAutoRenewStatus());
Assertions.assertEquals(1, renewalInfo.getRawAutoRenewStatus());
Assertions.assertTrue(renewalInfo.getIsInBillingRetryPeriod());
Assertions.assertEquals(PriceIncreaseStatus.CUSTOMER_HAS_NOT_RESPONDED, renewalInfo.getPriceIncreaseStatus());
Assertions.assertEquals(0, renewalInfo.getRawPriceIncreaseStatus());
Assertions.assertEquals(1698148900000L, renewalInfo.getGracePeriodExpiresDate());
Assertions.assertEquals(OfferType.PROMOTIONAL_OFFER, renewalInfo.getOfferType());
Assertions.assertEquals(2, renewalInfo.getRawOfferType());
Assertions.assertEquals("abc.123", renewalInfo.getOfferIdentifier());
Assertions.assertEquals(1698148800000L, renewalInfo.getSignedDate());
Assertions.assertEquals(Environment.LOCAL_TESTING, renewalInfo.getEnvironment());
Assertions.assertEquals("LocalTesting", renewalInfo.getRawEnvironment());
Assertions.assertEquals(1698148800000L, renewalInfo.getRecentSubscriptionStartDate());
Assertions.assertEquals(1698148850000L, renewalInfo.getRenewalDate());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) 2023 Apple Inc. Licensed under MIT License.

package com.apple.itunes.storekit.model;

import com.apple.itunes.storekit.util.SignedDataCreator;
import com.apple.itunes.storekit.util.TestingUtility;
import com.apple.itunes.storekit.verification.VerificationException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.UUID;

public class JWSTransactionDecodedPayloadTest {

@Test
public void testTransactionDecoding() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, VerificationException {
String signedTransaction = SignedDataCreator.createSignedDataFromJson("models/signedTransaction.json");

JWSTransactionDecodedPayload transaction = TestingUtility.getSignedPayloadVerifier().verifyAndDecodeTransaction(signedTransaction);

Assertions.assertEquals("12345", transaction.getOriginalTransactionId());
Assertions.assertEquals("23456", transaction.getTransactionId());
Assertions.assertEquals("34343", transaction.getWebOrderLineItemId());
Assertions.assertEquals("com.example", transaction.getBundleId());
Assertions.assertEquals("com.example.product", transaction.getProductId());
Assertions.assertEquals("55555", transaction.getSubscriptionGroupIdentifier());
Assertions.assertEquals(1698148800000L, transaction.getOriginalPurchaseDate());
Assertions.assertEquals(1698148900000L, transaction.getPurchaseDate());
Assertions.assertEquals(1698148950000L, transaction.getRevocationDate());
Assertions.assertEquals(1698149000000L, transaction.getExpiresDate());
Assertions.assertEquals(1, transaction.getQuantity());
Assertions.assertEquals(Type.AUTO_RENEWABLE_SUBSCRIPTION, transaction.getType());
Assertions.assertEquals("Auto-Renewable Subscription", transaction.getRawType());
Assertions.assertEquals(UUID.fromString("7e3fb20b-4cdb-47cc-936d-99d65f608138"), transaction.getAppAccountToken());
Assertions.assertEquals(InAppOwnershipType.PURCHASED, transaction.getInAppOwnershipType());
Assertions.assertEquals("PURCHASED", transaction.getRawInAppOwnershipType());
Assertions.assertEquals(1698148900000L, transaction.getSignedDate());
Assertions.assertEquals(RevocationReason.REFUNDED_DUE_TO_ISSUE, transaction.getRevocationReason());
Assertions.assertEquals(1, transaction.getRawRevocationReason());
Assertions.assertEquals("abc.123", transaction.getOfferIdentifier());
Assertions.assertTrue(transaction.getIsUpgraded());
Assertions.assertEquals(OfferType.INTRODUCTORY_OFFER, transaction.getOfferType());
Assertions.assertEquals(1, transaction.getRawOfferType());
Assertions.assertEquals("USA", transaction.getStorefront());
Assertions.assertEquals("143441", transaction.getStorefrontId());
Assertions.assertEquals(TransactionReason.PURCHASE, transaction.getTransactionReason());
Assertions.assertEquals("PURCHASE", transaction.getRawTransactionReason());
Assertions.assertEquals(Environment.LOCAL_TESTING, transaction.getEnvironment());
Assertions.assertEquals("LocalTesting", transaction.getRawEnvironment());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright (c) 2023 Apple Inc. Licensed under MIT License.

package com.apple.itunes.storekit.model;

import com.apple.itunes.storekit.util.SignedDataCreator;
import com.apple.itunes.storekit.util.TestingUtility;
import com.apple.itunes.storekit.verification.VerificationException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.List;
import java.util.UUID;

public class ResponseBodyV2DecodedPayloadTest {

@Test
public void testNotificationDecoding() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, VerificationException {
String signedNotification = SignedDataCreator.createSignedDataFromJson("models/signedNotification.json");

ResponseBodyV2DecodedPayload notification = TestingUtility.getSignedPayloadVerifier().verifyAndDecodeNotification(signedNotification);

Assertions.assertEquals(NotificationTypeV2.SUBSCRIBED, notification.getNotificationType());
Assertions.assertEquals("SUBSCRIBED", notification.getRawNotificationType());
Assertions.assertEquals(Subtype.INITIAL_BUY, notification.getSubtype());
Assertions.assertEquals("INITIAL_BUY", notification.getRawSubtype());
Assertions.assertEquals("002e14d5-51f5-4503-b5a8-c3a1af68eb20", notification.getNotificationUUID());
Assertions.assertEquals("2.0", notification.getVersion());
Assertions.assertEquals(1698148900000L, notification.getSignedDate());
Assertions.assertNotNull(notification.getData());
Assertions.assertNull(notification.getSummary());
Assertions.assertEquals(Environment.LOCAL_TESTING, notification.getData().getEnvironment());
Assertions.assertEquals("LocalTesting", notification.getData().getRawEnvironment());
Assertions.assertEquals(41234L, notification.getData().getAppAppleId());
Assertions.assertEquals("com.example", notification.getData().getBundleId());
Assertions.assertEquals("1.2.3", notification.getData().getBundleVersion());
Assertions.assertEquals("signed_transaction_info_value", notification.getData().getSignedTransactionInfo());
Assertions.assertEquals("signed_renewal_info_value", notification.getData().getSignedRenewalInfo());
Assertions.assertEquals(Status.ACTIVE, notification.getData().getStatus());
Assertions.assertEquals(1, notification.getData().getRawStatus());
}

@Test
public void testSummaryNotificationDecoding() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, VerificationException {
String signedNotification = SignedDataCreator.createSignedDataFromJson("models/signedSummaryNotification.json");

ResponseBodyV2DecodedPayload notification = TestingUtility.getSignedPayloadVerifier().verifyAndDecodeNotification(signedNotification);

Assertions.assertEquals(NotificationTypeV2.RENEWAL_EXTENSION, notification.getNotificationType());
Assertions.assertEquals("RENEWAL_EXTENSION", notification.getRawNotificationType());
Assertions.assertEquals(Subtype.SUMMARY, notification.getSubtype());
Assertions.assertEquals("SUMMARY", notification.getRawSubtype());
Assertions.assertEquals("002e14d5-51f5-4503-b5a8-c3a1af68eb20", notification.getNotificationUUID());
Assertions.assertEquals("2.0", notification.getVersion());
Assertions.assertEquals(1698148900000L, notification.getSignedDate());
Assertions.assertNull(notification.getData());
Assertions.assertNotNull(notification.getSummary());
Assertions.assertEquals(Environment.LOCAL_TESTING, notification.getSummary().getEnvironment());
Assertions.assertEquals("LocalTesting", notification.getSummary().getRawEnvironment());
Assertions.assertEquals(41234L, notification.getSummary().getAppAppleId());
Assertions.assertEquals("com.example", notification.getSummary().getBundleId());
Assertions.assertEquals("com.example.product", notification.getSummary().getProductId());
Assertions.assertEquals("efb27071-45a4-4aca-9854-2a1e9146f265", notification.getSummary().getRequestIdentifier());
Assertions.assertEquals(List.of("CAN", "USA", "MEX"), notification.getSummary().getStorefrontCountryCodes());
Assertions.assertEquals(5, notification.getSummary().getSucceededCount());
Assertions.assertEquals(2, notification.getSummary().getFailedCount());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@

package com.apple.itunes.storekit.offers;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.io.InputStream;
import java.util.UUID;

class PromotionalOfferSignatureCreatorTest {

@Test
void testConstructor() throws Exception {
try (InputStream key = this.getClass().getClassLoader().getResourceAsStream("testSigningKey.p8")) {
new PromotionalOfferSignatureCreator(new String(key.readAllBytes()), "keyId", "bundleId");
void testSignatureCreator() throws Exception {
try (InputStream key = this.getClass().getClassLoader().getResourceAsStream("certs/testSigningKey.p8")) {
PromotionalOfferSignatureCreator signatureCreator = new PromotionalOfferSignatureCreator(new String(key.readAllBytes()), "keyId", "bundleId");
String signature = signatureCreator.createSignature("productId", "offerId", "applicationUsername", UUID.fromString("20fba8a0-2b80-4a7d-a17f-85c1854727f8"), 1698148900000L);
Assertions.assertNotNull(signature);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) 2023 Apple Inc. Licensed under MIT License.

package com.apple.itunes.storekit.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.ECDSAKeyProvider;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyFactory;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.ECPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Map;

public class SignedDataCreator {

public static String createSignedDataFromJson(String path) throws IOException, NoSuchAlgorithmException {
String json = TestingUtility.readFile(path);
KeyPairGenerator ec = KeyPairGenerator.getInstance("EC");
ec.initialize(256);
return JWT.create()
.withPayload(json)
.sign(Algorithm.ECDSA256((ECPrivateKey) ec.generateKeyPair().getPrivate()));
}
}
Loading

0 comments on commit 06e21ee

Please sign in to comment.