diff --git a/src/test/java/com/gr4vy/sdk/backoffice/MerchantAccountsTest.java b/src/test/java/com/gr4vy/sdk/backoffice/MerchantAccountsTest.java index 65c43b5a..c86f7d7f 100644 --- a/src/test/java/com/gr4vy/sdk/backoffice/MerchantAccountsTest.java +++ b/src/test/java/com/gr4vy/sdk/backoffice/MerchantAccountsTest.java @@ -44,6 +44,7 @@ void list() throws Exception { void create() throws Exception { Gr4vy client = Harness.client(); String id = randomMerchantId(); + Harness.trackMerchantAccount(id, Harness.merchant().privateKey); MerchantAccount account = client.merchantAccounts().create() .request(MerchantAccountCreate.builder() .id(id) diff --git a/src/test/java/com/gr4vy/sdk/util/Harness.java b/src/test/java/com/gr4vy/sdk/util/Harness.java index 82df406b..0924d7da 100644 --- a/src/test/java/com/gr4vy/sdk/util/Harness.java +++ b/src/test/java/com/gr4vy/sdk/util/Harness.java @@ -1,16 +1,25 @@ package com.gr4vy.sdk.util; import java.io.IOException; +import java.net.URI; +import java.net.URLEncoder; import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.SecureRandom; import java.time.Duration; +import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import org.junit.jupiter.api.Assumptions; +import com.gr4vy.sdk.Auth; import com.gr4vy.sdk.BearerSecuritySource; import com.gr4vy.sdk.Gr4vy; import com.gr4vy.sdk.Gr4vy.AvailableServers; @@ -66,6 +75,9 @@ private static final class NoKeyException extends RuntimeException { private static TestMerchant shared; private static RuntimeException setupError; + /** Merchant account id to signing key, deleted when the JVM exits. */ + private static final Map createdMerchantAccounts = new LinkedHashMap<>(); + /** * Returns the shard's shared merchant, provisioning it on first use. Skips * the calling test when no signing key is available; any other setup failure @@ -154,6 +166,8 @@ private static TestMerchant setup() throws Exception { String privateKey = loadPrivateKey(); String merchantAccountId = randomMerchantId(); + trackMerchantAccount(merchantAccountId, privateKey); + // Admin client (not merchant-scoped) creates the merchant account. Gr4vy admin = newClient(privateKey, null); CreateMerchantAccountResponse res = admin.merchantAccounts().create() @@ -184,4 +198,51 @@ private static TestMerchant setup() throws Exception { return new TestMerchant(merchant, account.id(), privateKey); } + + /** + * Records a merchant account the suite created so it is deleted when the JVM + * exits (each CI shard is its own JVM). + */ + public static synchronized void trackMerchantAccount(String merchantAccountId, String privateKey) { + if (createdMerchantAccounts.isEmpty()) { + Runtime.getRuntime().addShutdownHook(new Thread(Harness::deleteCreatedMerchantAccounts)); + } + createdMerchantAccounts.put(merchantAccountId, privateKey); + } + + private static void deleteCreatedMerchantAccounts() { + List> accounts; + synchronized (Harness.class) { + accounts = new ArrayList<>(createdMerchantAccounts.entrySet()); + } + for (Map.Entry account : accounts) { + deleteMerchantAccount(account.getKey(), account.getValue()); + } + } + + /** + * Deletes a merchant account. The endpoint is not in the public spec, so it is + * called directly. Failures are logged, never thrown: cleanup must not fail the run. + */ + private static void deleteMerchantAccount(String merchantAccountId, String privateKey) { + try { + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(Checkout.API_BASE_URL + "/merchant-accounts/" + + URLEncoder.encode(merchantAccountId, StandardCharsets.UTF_8))) + .timeout(Duration.ofSeconds(30)) + .header("Authorization", "Bearer " + Auth.getToken(privateKey)) + .DELETE() + .build(); + HttpResponse response = HttpClient.newBuilder() + .connectTimeout(Duration.ofSeconds(20)) + .build() + .send(request, HttpResponse.BodyHandlers.discarding()); + if (response.statusCode() != 204 && response.statusCode() != 404) { + System.err.println("Failed to delete merchant account " + merchantAccountId + + ": HTTP " + response.statusCode()); + } + } catch (Exception e) { + System.err.println("Failed to delete merchant account " + merchantAccountId + ": " + e); + } + } }