diff --git a/annotations/build.gradle b/annotations/build.gradle index 848b52127..82191e90d 100644 --- a/annotations/build.gradle +++ b/annotations/build.gradle @@ -5,3 +5,9 @@ plugins { group = projectGroup version = projectVersion +java { + toolchain { + // must stay on Java 11 due to compatibility issues + languageVersion.set(JavaLanguageVersion.of(11)) + } +} diff --git a/buildSrc/src/main/groovy/shared-build-conventions.gradle b/buildSrc/src/main/groovy/shared-build-conventions.gradle index d529d6ed1..2fc9f213b 100644 --- a/buildSrc/src/main/groovy/shared-build-conventions.gradle +++ b/buildSrc/src/main/groovy/shared-build-conventions.gradle @@ -7,6 +7,11 @@ repositories { mavenCentral() } +dependencies { + compileOnly 'org.projectlombok:lombok:1.18.36' + annotationProcessor 'org.projectlombok:lombok:1.18.36' +} + java { toolchain { languageVersion.set(JavaLanguageVersion.of(17)) diff --git a/currencies/build.gradle b/currencies/build.gradle index d525bb2c3..816743178 100644 --- a/currencies/build.gradle +++ b/currencies/build.gradle @@ -7,5 +7,21 @@ version = projectVersion dependencies { implementation("org.slf4j:slf4j-api:1.7.28") - testImplementation("junit:junit:4.13.1") + // keeping junit 4 for compatibility, will be removed soon, use junit 5 instead + testImplementation("junit:junit:4.13.2") + testImplementation("org.junit.vintage:junit-vintage-engine:5.11.4") + + testImplementation("org.junit.jupiter:junit-jupiter-api:5.11.4") + testImplementation("org.junit.jupiter:junit-jupiter-engine:5.11.4") +} + +java { + toolchain { + // must stay on Java 11 due to compatibility issues + languageVersion.set(JavaLanguageVersion.of(11)) + } +} + +test { + useJUnitPlatform() } \ No newline at end of file diff --git a/currencies/src/main/java/com/generalbytes/batm/common/currencies/FiatCurrency.java b/currencies/src/main/java/com/generalbytes/batm/common/currencies/FiatCurrency.java index fcdb24fb6..cf7900794 100644 --- a/currencies/src/main/java/com/generalbytes/batm/common/currencies/FiatCurrency.java +++ b/currencies/src/main/java/com/generalbytes/batm/common/currencies/FiatCurrency.java @@ -38,6 +38,7 @@ public enum FiatCurrency { BAM("Bosnia and Herzegovina convertible mark"), BGN("Bulgarian lev"), BHD("Bahraini dinar"), + BOB("Bolivian boliviano"), BRL("Brazilian real"), BSD("Bahamian Dollar"), CAD("Canadian dollar"), diff --git a/currencies/src/test/java/com/generalbytes/batm/common/currencies/CryptoCurrencyTest.java b/currencies/src/test/java/com/generalbytes/batm/common/currencies/CryptoCurrencyTest.java index de5590eac..0807abea8 100644 --- a/currencies/src/test/java/com/generalbytes/batm/common/currencies/CryptoCurrencyTest.java +++ b/currencies/src/test/java/com/generalbytes/batm/common/currencies/CryptoCurrencyTest.java @@ -1,52 +1,51 @@ package com.generalbytes.batm.common.currencies; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Arrays; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.*; -public class CryptoCurrencyTest { +class CryptoCurrencyTest { @Test - public void getCurrencyName() { + void getCurrencyName() { assertEquals("Bitcoin", CryptoCurrency.BTC.getCurrencyName()); assertEquals("Litecoin", CryptoCurrency.LTC.getCurrencyName()); assertEquals("Bitcoin Cash", CryptoCurrency.BCH.getCurrencyName()); for (CryptoCurrency c : CryptoCurrency.values()) { - assertTrue(c.name() + " name", c.getCurrencyName() != null && !c.getCurrencyName().isEmpty()); + assertTrue(c.getCurrencyName() != null && !c.getCurrencyName().isEmpty(), c.name() + " name"); } } @Test - public void getCode() { + void getCode() { assertEquals("BTC", CryptoCurrency.BTC.getCode()); assertEquals("$PAC", CryptoCurrency.PAC.getCode()); for (CryptoCurrency c : CryptoCurrency.values()) { - assertTrue(c.name() + " code", c.getCode() != null && c.getCurrencyName().length() > 1); - assertEquals(c.name() + " code should be uppercase", c.getCode(), c.getCode().toUpperCase()); - assertEquals(c.name() + " enum should be uppercase", c.name(), c.name().toUpperCase()); + assertTrue(c.getCode() != null && c.getCurrencyName().length() > 1, c.name() + " code"); + assertEquals(c.getCode(), c.getCode().toUpperCase(), c.name() + " code should be uppercase"); + assertEquals(c.name(), c.name().toUpperCase(), c.name() + " enum should be uppercase"); } } @Test - public void getCodes() { + void getCodes() { assertEquals(CryptoCurrency.values().length, CryptoCurrency.getCodes().size()); assertTrue(CryptoCurrency.getCodes().containsAll(Arrays.asList("BTC", "$PAC", "LTC"))); } @Test - public void valueOfCode() { + void valueOfCode() { assertEquals(CryptoCurrency.BTC, CryptoCurrency.valueOfCode("btc")); assertEquals(CryptoCurrency.BTC, CryptoCurrency.valueOfCode("BTC")); assertEquals(CryptoCurrency.PAC, CryptoCurrency.valueOfCode("$pAC")); assertEquals(CryptoCurrency.PAC, CryptoCurrency.valueOfCode("$PAC")); } - @Test(expected = IllegalArgumentException.class) - public void valueOfException() { - CryptoCurrency.valueOf("$PAC"); + @Test + void valueOfException() { + assertThrows(IllegalArgumentException.class, () -> CryptoCurrency.valueOf("$PAC")); + } } diff --git a/currencies/src/test/java/com/generalbytes/batm/common/currencies/FiatCurrencyTest.java b/currencies/src/test/java/com/generalbytes/batm/common/currencies/FiatCurrencyTest.java index ccd7f4905..e042108f0 100644 --- a/currencies/src/test/java/com/generalbytes/batm/common/currencies/FiatCurrencyTest.java +++ b/currencies/src/test/java/com/generalbytes/batm/common/currencies/FiatCurrencyTest.java @@ -1,33 +1,35 @@ package com.generalbytes.batm.common.currencies; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Arrays; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; -public class FiatCurrencyTest { + +class FiatCurrencyTest { @Test - public void getCurrencyName() { + void getCurrencyName() { assertEquals("Czech koruna", FiatCurrency.CZK.getCurrencyName()); for (FiatCurrency c : FiatCurrency.values()) { - assertTrue(c.name() + " name", c.getCurrencyName() != null && !c.getCurrencyName().isEmpty()); + assertTrue(c.getCurrencyName() != null && !c.getCurrencyName().isEmpty(), c.name() + " name"); } } @Test - public void getCode() { + void getCode() { assertEquals("CZK", FiatCurrency.CZK.getCode()); for (FiatCurrency c : FiatCurrency.values()) { - assertTrue(c.name() + " code", c.getCurrencyName().length() > 1); - assertEquals(c.name() + " code should be uppercase", c.getCode(), c.getCode().toUpperCase()); - assertEquals(c.name() + " enum should be uppercase", c.name(), c.name().toUpperCase()); + assertTrue(c.getCurrencyName().length() > 1, c.name() + " code"); + assertEquals(c.getCode(), c.getCode().toUpperCase(), c.name() + " code should be uppercase"); + assertEquals(c.name(), c.name().toUpperCase(), c.name() + " enum should be uppercase"); } } @Test - public void getCodes() { + void getCodes() { assertEquals(FiatCurrency.values().length, FiatCurrency.getCodes().size()); assertTrue(FiatCurrency.getCodes().containsAll(Arrays.asList("CZK", "EUR", "USD"))); } diff --git a/dependencySubstitutions.txt b/dependencySubstitutions.txt index 57fbad937..070536768 100644 --- a/dependencySubstitutions.txt +++ b/dependencySubstitutions.txt @@ -39,4 +39,5 @@ substitute module: 'org.bouncycastle:bcprov-jdk15to18', versions: ['1.66'], toVe substitute module: 'com.fasterxml.jackson.datatype:jackson-datatype-guava', versions: ['2.11.2'], toVersion: '2.12.0' substitute module: 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8', versions: ['2.11.2'], toVersion: '2.12.0' substitute module: 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310', versions: ['2.11.2'], toVersion: '2.12.0' - +substitute module: 'junit:junit', versions: ['4.13.1'], toVersion: '4.13.2' +substitute module: 'org.junit.jupiter:junit-jupiter-api', versions: ['5.9.1'], toVersion: '5.11.4' diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 6c03f2ab4..09cfdfbf9 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -1197,6 +1197,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1729,6 +1758,14 @@ + + + + + + + + @@ -1753,6 +1790,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1864,6 +1949,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1917,6 +2047,14 @@ + + + + + + + + diff --git a/server_extensions_api/build.gradle b/server_extensions_api/build.gradle index 9686dd886..3e327d68d 100644 --- a/server_extensions_api/build.gradle +++ b/server_extensions_api/build.gradle @@ -26,6 +26,15 @@ dependencies { implementation("com.sun.mail:javax.mail:1.6.2") implementation("com.vdurmont:emoji-java:3.1.3") //for chat emojis - testImplementation("junit:junit:4.13.1") + // keeping junit 4 for compatibility, will be removed soon, use junit 5 instead + testImplementation("junit:junit:4.13.2") + testImplementation("org.junit.vintage:junit-vintage-engine:5.11.4") + + testImplementation("org.junit.jupiter:junit-jupiter-api:5.11.4") + testImplementation("org.junit.jupiter:junit-jupiter-engine:5.11.4") testImplementation("org.assertj:assertj-core:3.19.0") } + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/IExtension.java b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/IExtension.java index 4fa3cb2b8..d5b9a734a 100644 --- a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/IExtension.java +++ b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/IExtension.java @@ -26,6 +26,7 @@ import com.generalbytes.batm.server.extensions.communication.IPhoneLookupProvider; import com.generalbytes.batm.server.extensions.communication.voicecall.IVoiceCallProvider; import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleProvider; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleProviderFactory; import com.generalbytes.batm.server.extensions.travelrule.IWalletTypeEvaluationProvider; import com.generalbytes.batm.server.extensions.watchlist.IWatchList; @@ -190,10 +191,15 @@ public interface IExtension { Set getVoiceCallProviders(); /** - * Returns set of external Travel Rule Providers. - * @return {@link Set} of {@link ITravelRuleProvider}. + * Returns the list of all Travel Rule Provider Factories. + *

+ * Each factory is responsible for creating instance of {@link ITravelRuleProvider} based on the implementation. + * + * @return Set of all Travel Rule Provider Factories. */ - default Set getTravelRuleProviders() { return Collections.emptySet(); } + default Set getTravelRuleProviderFactories() { + return Collections.emptySet(); + } /** * @return Validators that can be used to validate SSNs diff --git a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/IExtensionContext.java b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/IExtensionContext.java index a508ca0e6..f48c74ee3 100644 --- a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/IExtensionContext.java +++ b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/IExtensionContext.java @@ -1,5 +1,5 @@ /************************************************************************************* - * Copyright (C) 2014-2024 GENERAL BYTES s.r.o. All rights reserved. + * Copyright (C) 2014-2025 GENERAL BYTES s.r.o. All rights reserved. * * This software may be distributed and modified under the terms of the GNU * General Public License version 2 (GPL2) as published by the Free Software @@ -28,6 +28,8 @@ import com.generalbytes.batm.server.extensions.exceptions.CashbackException; import com.generalbytes.batm.server.extensions.exceptions.SellException; import com.generalbytes.batm.server.extensions.exceptions.UpdateException; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleProviderIdentification; +import com.generalbytes.batm.server.extensions.travelrule.IVaspIdentification; import com.generalbytes.batm.server.extensions.watchlist.WatchListQuery; import com.generalbytes.batm.server.extensions.watchlist.WatchListResult; @@ -891,4 +893,19 @@ default List getCustomStrings(String serialNumber, String customS */ ReceiptData getReceiptData(ReceiptTransferMethod receiptTransferMethod, ITransactionDetails transactionDetails, String template); + /** + * Returns the list of all configured Travel Rule Providers. + * + * @return List of all configured Travel Rule Providers. + */ + default List getTravelRuleProviders() { return new ArrayList<>(); } + + /** + * Returns the list of all VASPs of given Travel Rule Provider. + * + * @param travelRuleProviderId ID of Travel Rule Provider. + * @return List of all VASPs. + */ + default List getVasps(long travelRuleProviderId) { return new ArrayList<>(); } + } diff --git a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/ITransactionPreparation.java b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/ITransactionPreparation.java index 9e50c7f05..5a24f7bd4 100644 --- a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/ITransactionPreparation.java +++ b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/ITransactionPreparation.java @@ -30,7 +30,9 @@ public interface ITransactionPreparation { int TYPE_BUY_CRYPTO = 0; int TYPE_SELL_CRYPTO = 1; int TYPE_WITHDRAW_CASH = 2; - + int TYPE_CASHBACK = 3; + int TYPE_ORDER_CRYPTO = 4; + int TYPE_DEPOSIT_CASH = 5; int RESPONSE_WITHDRAWAL_NOT_POSSIBLE = 0; int RESPONSE_WITHDRAWAL_POSSIBLE = 1; diff --git a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/ITransactionRequest.java b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/ITransactionRequest.java index 4b59a9e71..2b2b038fd 100644 --- a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/ITransactionRequest.java +++ b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/ITransactionRequest.java @@ -31,6 +31,7 @@ public interface ITransactionRequest { int TYPE_WITHDRAW_CASH = 2; int TYPE_CASHBACK = 3; int TYPE_ORDER_CRYPTO = 4; + int TYPE_DEPOSIT_CASH = 5; /** * Server time of the transaction diff --git a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleNaturalPerson.java b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleNaturalPerson.java new file mode 100644 index 000000000..6a5c256f2 --- /dev/null +++ b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleNaturalPerson.java @@ -0,0 +1,37 @@ +/************************************************************************************* + * Copyright (C) 2014-2025 GENERAL BYTES s.r.o. All rights reserved. + * + * This software may be distributed and modified under the terms of the GNU + * General Public License version 2 (GPL2) as published by the Free Software + * Foundation and appearing in the file GPL2.TXT included in the packaging of + * this file. Please note that GPL2 Section 2[b] requires that all works based + * on this software must also be made publicly available under the terms of + * the GPL2 ("Copyleft"). + * + * Contact information + * ------------------- + * + * GENERAL BYTES s.r.o. + * Web : http://www.generalbytes.com + * + ************************************************************************************/ +package com.generalbytes.batm.server.extensions.travelrule; + +/** + * Represents either an Originator or a Beneficiary in a Travel Rule transfer. + */ +public interface ITravelRuleNaturalPerson { + + /** + * Get the name data of this natural person. + * + * @return An {@link ITravelRuleNaturalPersonName} object with the name data. + */ + ITravelRuleNaturalPersonName getName(); + + /** + * Get the public id of the Identity related to this natural person. + */ + String getIdentityPublicId(); + +} diff --git a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleNaturalPersonName.java b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleNaturalPersonName.java new file mode 100644 index 000000000..69554b32c --- /dev/null +++ b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleNaturalPersonName.java @@ -0,0 +1,53 @@ +/************************************************************************************* + * Copyright (C) 2014-2025 GENERAL BYTES s.r.o. All rights reserved. + * + * This software may be distributed and modified under the terms of the GNU + * General Public License version 2 (GPL2) as published by the Free Software + * Foundation and appearing in the file GPL2.TXT included in the packaging of + * this file. Please note that GPL2 Section 2[b] requires that all works based + * on this software must also be made publicly available under the terms of + * the GPL2 ("Copyleft"). + * + * Contact information + * ------------------- + * + * GENERAL BYTES s.r.o. + * Web : http://www.generalbytes.com + * + ************************************************************************************/ +package com.generalbytes.batm.server.extensions.travelrule; + +/** + * Holds the name data for a natural person. + */ +public interface ITravelRuleNaturalPersonName { + + /** + * Get the primary name of the natural person. + */ + String getPrimaryName(); + + /** + * Get the secondary name of the natural person. + */ + String getSecondaryName(); + + /** + * Get the type of the name. + * + *

Possible Values:

+ *
    + *
  • {@code ALIA} - Alias name - A name other than the legal name by which a natural person is also known.
  • + *
  • {@code BIRT} - Name at birth - The name given to a natural person at birth.
  • + *
  • {@code MAID} - Maiden name - The original name of a natural person who has changed their name after marriage.
  • + *
  • {@code LEGL} - Legal name - The name that identifies a natural person for legal, official or administrative purposes.
  • + *
  • {@code MISC} - Unspecified - A name by which a natural person may be known but which cannot otherwise be categorized + * or the category of which the sender is unable to determine.
  • + *
  • {@code null}
  • + *
+ * + * @return The type of the name. Can be null. + */ + String getNameType(); + +} diff --git a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleProvider.java b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleProvider.java index 022ad64c8..92544a0b4 100644 --- a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleProvider.java +++ b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleProvider.java @@ -1,5 +1,5 @@ /************************************************************************************* - * Copyright (C) 2014-2024 GENERAL BYTES s.r.o. All rights reserved. + * Copyright (C) 2014-2025 GENERAL BYTES s.r.o. All rights reserved. * * This software may be distributed and modified under the terms of the GNU * General Public License version 2 (GPL2) as published by the Free Software @@ -17,6 +17,8 @@ ************************************************************************************/ package com.generalbytes.batm.server.extensions.travelrule; +import java.util.List; + /** * A Travel Rule Provider definition that makes it possible to connect to an external provider using its API. * Provider is responsible for implementing compliance checks and procedures necessary to ensure adherence to the Travel Rule regulations. @@ -29,6 +31,69 @@ public interface ITravelRuleProvider { */ String getName(); - // TODO: implement methods + /** + * Get information about a crypto wallet. + * + * @param walletEvaluationRequest The wallet's context data. + * @return Information about the wallet. + */ + ITravelRuleWalletInfo getWalletInfo(IIdentityWalletEvaluationRequest walletEvaluationRequest); + + /** + * Get all available VASPs. + * + * @return List of all available VASPs. + * @throws TravelRuleProviderException In case of failure. + */ + List getAllVasps(); + + /** + * Create a new transfer, sending a message to the Beneficiary VASP. + * + * @param outgoingTransferData The data to create a transfer from. + * @return {@link ITravelRuleTransferInfo} of the new transfer or null in case of failure. + */ + ITravelRuleTransferInfo createTransfer(ITravelRuleTransferData outgoingTransferData); + + /** + * Register a new listener for transfer status updates. + * + *

Whenever the status of a transfer, related to the given VASP, changes, the method + * {@link ITravelRuleTransferUpdateListener#onTransferStatusUpdate(ITravelRuleTransferStatusUpdateEvent)} + * on this listener will be called.

+ * + * @param listener The listener. + * @return True if the listener was successfully registered, false otherwise. + */ + boolean registerStatusUpdateListener(ITravelRuleTransferUpdateListener listener); + /** + * Unregister an existing listener for transfer status updates. + * + * @return True if the listener was successfully unregistered, false otherwise. + */ + boolean unregisterStatusUpdateListener(); + + /** + * Update an existing transfer. + * + * @param request The request. + * @return The updated transfer info or null if the update fails. + */ + ITravelRuleTransferInfo updateTransfer(ITravelRuleTransferUpdateRequest request); + + /** + * Called when the provider configuration changes. + * + * This may be used to update the provider's state or credentials used to call the provider's API. + */ + void notifyProviderConfigurationChanged(); + + /** + * Test whether the Travel Rule Provider is configured correctly. + * This test is called by the user from CAS. + * + * @return {@code True} if configuration is valid, otherwise {@code false}. + */ + boolean testProviderConfiguration(); } diff --git a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleProviderCredentials.java b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleProviderCredentials.java new file mode 100644 index 000000000..b54d09ac0 --- /dev/null +++ b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleProviderCredentials.java @@ -0,0 +1,23 @@ +package com.generalbytes.batm.server.extensions.travelrule; + +/** + * This interface serves as a container for the minimal set of information needed + * to authenticate and identify a travel rule provider. + */ +public interface ITravelRuleProviderCredentials { + /** + * The client ID configured for the travel rule provider, used to authenticate with the travel rule provider + */ + String getClientId(); + + /** + * Client secret configured for the travel rule provider, used to authenticate with the travel rule provider + */ + String getClientSecret(); + + /** + * The configured VASP used based on the travel rule settings for this provider. Represents operator's VASP DID. + * Value depends on the context and may differ when different travel rule settings are used. + */ + String getVaspDid(); +} diff --git a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleProviderFactory.java b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleProviderFactory.java new file mode 100644 index 000000000..f7f8614b6 --- /dev/null +++ b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleProviderFactory.java @@ -0,0 +1,26 @@ +package com.generalbytes.batm.server.extensions.travelrule; + +/** + * Factory for creating travel rule provider instances. + */ +public interface ITravelRuleProviderFactory { + + /** + * Returns the name of the provider. Used to correctly select what type of provider to create. + * + * @return name of the provider as defined by {@link ITravelRuleProvider#getName()} + */ + String getProviderName(); + + /** + * Creates a new instance of the provider. The provider should be configured with the given credentials. + *

+ * Warning: This method is called for every interaction with the provider. + * Reason is to provide use case specific credentials to the provider. Implementation should be fast and reuse heavy objects. + * Also don't share credentials between different instances of the provider as they might differ. + * + * @param credentials credentials for the provider currently used when processing transaction + * @return new instance of the provider + */ + ITravelRuleProvider getProvider(ITravelRuleProviderCredentials credentials); +} \ No newline at end of file diff --git a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleProviderIdentification.java b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleProviderIdentification.java new file mode 100644 index 000000000..ae5546a61 --- /dev/null +++ b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleProviderIdentification.java @@ -0,0 +1,46 @@ +/************************************************************************************* + * Copyright (C) 2014-2025 GENERAL BYTES s.r.o. All rights reserved. + * + * This software may be distributed and modified under the terms of the GNU + * General Public License version 2 (GPL2) as published by the Free Software + * Foundation and appearing in the file GPL2.TXT included in the packaging of + * this file. Please note that GPL2 Section 2[b] requires that all works based + * on this software must also be made publicly available under the terms of + * the GPL2 ("Copyleft"). + * + * Contact information + * ------------------- + * + * GENERAL BYTES s.r.o. + * Web : http://www.generalbytes.com + * + ************************************************************************************/ +package com.generalbytes.batm.server.extensions.travelrule; + +import com.generalbytes.batm.server.extensions.IOrganization; + +/** + * An object that identifies the Travel Rule Provider. + */ +public interface ITravelRuleProviderIdentification { + + /** + * @return ID of Travel Rule Provider. + */ + long getId(); + + /** + * It is used as an identifier in CAS. Different organizations may have Travel Rule Provider with the same name. + * + * @return Name of Travel Rule Provider. + */ + String getName(); + + /** + * It serves to clearly identify the right Travel Rule Provider. + * + * @return Data about organization. + */ + IOrganization getOrganization(); + +} diff --git a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleTransferData.java b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleTransferData.java new file mode 100644 index 000000000..4ac144ffa --- /dev/null +++ b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleTransferData.java @@ -0,0 +1,105 @@ +/************************************************************************************* + * Copyright (C) 2014-2025 GENERAL BYTES s.r.o. All rights reserved. + * + * This software may be distributed and modified under the terms of the GNU + * General Public License version 2 (GPL2) as published by the Free Software + * Foundation and appearing in the file GPL2.TXT included in the packaging of + * this file. Please note that GPL2 Section 2[b] requires that all works based + * on this software must also be made publicly available under the terms of + * the GPL2 ("Copyleft"). + * + * Contact information + * ------------------- + * + * GENERAL BYTES s.r.o. + * Web : http://www.generalbytes.com + * + ************************************************************************************/ +package com.generalbytes.batm.server.extensions.travelrule; + +import java.math.BigDecimal; + +/** + * Represents the data required to create a new Travel Rule transfer and send it to a beneficiary VASP + * or receive one from an originator VASP. + */ +public interface ITravelRuleTransferData { + + /** + * Get the unique identifier for this transfer, generated on the server side. + * + *

This identifier serves as a critical reference for this transfer in server-side operations. + * It is essential for tracking, managing, and identifying the transfer in the system.

+ */ + String getPublicId(); + + /** + * Get data about the originator involved in this transfer. + * + * @return An {@link ITravelRuleNaturalPerson} object representing the originator. + */ + ITravelRuleNaturalPerson getOriginator(); + + /** + * Get data about the beneficiary involved in this transfer. + * + * @return An {@link ITravelRuleNaturalPerson} object representing the beneficiary. + */ + ITravelRuleNaturalPerson getBeneficiary(); + + /** + * Get data about the originator's VASP. + * + * @return An {@link ITravelRuleVasp} object representing the originator's VASP. + */ + ITravelRuleVasp getOriginatorVasp(); + + /** + * Get data about the beneficiary's VASP. + * + * @return An {@link ITravelRuleVasp} object representing the beneficiary's VASP. + */ + ITravelRuleVasp getBeneficiaryVasp(); + + /** + * Get the cryptocurrency used in the transaction. + * + *

The cryptocurrency is specified as a ticker symbol, e.g., "BTC" for Bitcoin.

+ */ + String getTransactionAsset(); + + /** + * Get the amount of the cryptocurrency asset being transferred. + * + *

The amount is expressed in base units of the asset.

+ */ + long getTransactionAmount(); + + /** + * Get the destination crypto address. + * + *

This address specifies where the asset is being sent.

+ */ + String getDestinationAddress(); + + /** + * Get the amount in a fiat currency. + */ + BigDecimal getFiatAmount(); + + /** + * Get the fiat currency used. + * + *

This specifies the currency in which the fiat amount is denominated, + * represented as a currency code (e.g., "USD" for US Dollars).

+ */ + String getFiatCurrency(); + + /** + * Get the blockchain transaction hash. + * + * @return The transaction hash or null if it is not known yet. + */ + String getTransactionHash(); + +} diff --git a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleTransferInfo.java b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleTransferInfo.java new file mode 100644 index 000000000..ae15c959c --- /dev/null +++ b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleTransferInfo.java @@ -0,0 +1,32 @@ +/************************************************************************************* + * Copyright (C) 2014-2025 GENERAL BYTES s.r.o. All rights reserved. + * + * This software may be distributed and modified under the terms of the GNU + * General Public License version 2 (GPL2) as published by the Free Software + * Foundation and appearing in the file GPL2.TXT included in the packaging of + * this file. Please note that GPL2 Section 2[b] requires that all works based + * on this software must also be made publicly available under the terms of + * the GPL2 ("Copyleft"). + * + * Contact information + * ------------------- + * + * GENERAL BYTES s.r.o. + * Web : http://www.generalbytes.com + * + ************************************************************************************/ +package com.generalbytes.batm.server.extensions.travelrule; + +/** + * Holds information about a Travel Rule transfer. + */ +public interface ITravelRuleTransferInfo { + + /** + * Get the provider-generated identifier of the transfer. + * + *

This identifier is generated by the Travel Rule provider at which the transfer was created.

+ */ + String getId(); + +} diff --git a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleTransferStatusUpdateEvent.java b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleTransferStatusUpdateEvent.java new file mode 100644 index 000000000..1d10a076e --- /dev/null +++ b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleTransferStatusUpdateEvent.java @@ -0,0 +1,44 @@ +/************************************************************************************* + * Copyright (C) 2014-2025 GENERAL BYTES s.r.o. All rights reserved. + * + * This software may be distributed and modified under the terms of the GNU + * General Public License version 2 (GPL2) as published by the Free Software + * Foundation and appearing in the file GPL2.TXT included in the packaging of + * this file. Please note that GPL2 Section 2[b] requires that all works based + * on this software must also be made publicly available under the terms of + * the GPL2 ("Copyleft"). + * + * Contact information + * ------------------- + * + * GENERAL BYTES s.r.o. + * Web : http://www.generalbytes.com + * + ************************************************************************************/ +package com.generalbytes.batm.server.extensions.travelrule; + +/** + * This event is responsible for holding information about a transfer status change. + * + *

This is passed to {@link ITravelRuleTransferUpdateListener} whenever a transfer changes status.

+ */ +public interface ITravelRuleTransferStatusUpdateEvent { + + /** + * Get the public id of the affected transfer. + * + *

This is the id provided by server at create.

+ * + * @return The public id. + * @see ITravelRuleTransferData#getPublicId() + */ + String getTransferPublicId(); + + /** + * Get the new status of the updated transfer. + * + * @return The new status. + */ + TravelRuleProviderTransferStatus getNewTransferStatus(); + +} diff --git a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleTransferUpdateListener.java b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleTransferUpdateListener.java new file mode 100644 index 000000000..c680877e1 --- /dev/null +++ b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleTransferUpdateListener.java @@ -0,0 +1,41 @@ +/************************************************************************************* + * Copyright (C) 2014-2025 GENERAL BYTES s.r.o. All rights reserved. + * + * This software may be distributed and modified under the terms of the GNU + * General Public License version 2 (GPL2) as published by the Free Software + * Foundation and appearing in the file GPL2.TXT included in the packaging of + * this file. Please note that GPL2 Section 2[b] requires that all works based + * on this software must also be made publicly available under the terms of + * the GPL2 ("Copyleft"). + * + * Contact information + * ------------------- + * + * GENERAL BYTES s.r.o. + * Web : http://www.generalbytes.com + * + ************************************************************************************/ +package com.generalbytes.batm.server.extensions.travelrule; + +/** + * Represents a listener for transfer status updates. + * + *

This listener can be registered to a Travel Rule Provider to receive updates on transfer statuses.

+ * + * @see ITravelRuleProvider#registerStatusUpdateListener(ITravelRuleTransferUpdateListener) + */ +@FunctionalInterface +public interface ITravelRuleTransferUpdateListener { + + /** + * Call this method whenever a transfer changes status to one that can be represented with {@link TravelRuleProviderTransferStatus}. + * + *

The server provides an implementation for this interface and further processing of transfers + * depends on this method being called when a transfer is eventually Approved or Rejected.

+ * + * @param event Event holding information about the change. + * @throws IllegalArgumentException If transferPublicId or newTransferStatus in the event is null or invalid. + */ + void onTransferStatusUpdate(ITravelRuleTransferStatusUpdateEvent event); + +} \ No newline at end of file diff --git a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleTransferUpdateRequest.java b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleTransferUpdateRequest.java new file mode 100644 index 000000000..69494473a --- /dev/null +++ b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleTransferUpdateRequest.java @@ -0,0 +1,44 @@ +/************************************************************************************* + * Copyright (C) 2014-2025 GENERAL BYTES s.r.o. All rights reserved. + * + * This software may be distributed and modified under the terms of the GNU + * General Public License version 2 (GPL2) as published by the Free Software + * Foundation and appearing in the file GPL2.TXT included in the packaging of + * this file. Please note that GPL2 Section 2[b] requires that all works based + * on this software must also be made publicly available under the terms of + * the GPL2 ("Copyleft"). + * + * Contact information + * ------------------- + * + * GENERAL BYTES s.r.o. + * Web : http://www.generalbytes.com + * + ************************************************************************************/ +package com.generalbytes.batm.server.extensions.travelrule; + +/** + * Request to update a transfer at a travel rule provider. + * + * @see ITravelRuleProvider#updateTransfer(ITravelRuleTransferUpdateRequest) + */ +public interface ITravelRuleTransferUpdateRequest { + + /** + * Get the id of the transfer to update. + * + *

This is the id generated by the provider at transfer creation.

+ * + * @return The id. + * @see ITravelRuleTransferInfo#getId() + */ + String getId(); + + /** + * Get the new transaction hash that the transfer should be updated with. + * + * @return The new transaction hash of the transfer. + */ + String getTransactionHash(); + +} diff --git a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleVasp.java b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleVasp.java new file mode 100644 index 000000000..c84965245 --- /dev/null +++ b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleVasp.java @@ -0,0 +1,35 @@ +/************************************************************************************* + * Copyright (C) 2014-2025 GENERAL BYTES s.r.o. All rights reserved. + * + * This software may be distributed and modified under the terms of the GNU + * General Public License version 2 (GPL2) as published by the Free Software + * Foundation and appearing in the file GPL2.TXT included in the packaging of + * this file. Please note that GPL2 Section 2[b] requires that all works based + * on this software must also be made publicly available under the terms of + * the GPL2 ("Copyleft"). + * + * Contact information + * ------------------- + * + * GENERAL BYTES s.r.o. + * Web : http://www.generalbytes.com + * + ************************************************************************************/ +package com.generalbytes.batm.server.extensions.travelrule; + +/** + * Holds information about a VASP (Virtual Asset Service Provider). + */ +public interface ITravelRuleVasp { + + /** + * Get the identifier of the VASP. + */ + String getDid(); + + /** + * Get the name of the VASP. + */ + String getName(); + +} diff --git a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleWalletInfo.java b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleWalletInfo.java new file mode 100644 index 000000000..8e5bf3b72 --- /dev/null +++ b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/ITravelRuleWalletInfo.java @@ -0,0 +1,33 @@ +/************************************************************************************* + * Copyright (C) 2014-2025 GENERAL BYTES s.r.o. All rights reserved. + * + * This software may be distributed and modified under the terms of the GNU + * General Public License version 2 (GPL2) as published by the Free Software + * Foundation and appearing in the file GPL2.TXT included in the packaging of + * this file. Please note that GPL2 Section 2[b] requires that all works based + * on this software must also be made publicly available under the terms of + * the GPL2 ("Copyleft"). + * + * Contact information + * ------------------- + * + * GENERAL BYTES s.r.o. + * Web : http://www.generalbytes.com + * + ************************************************************************************/ +package com.generalbytes.batm.server.extensions.travelrule; + +/** + * Holds information about a crypto wallet. + */ +public interface ITravelRuleWalletInfo { + /** + * Type of the crypto wallet. + */ + CryptoWalletType getCryptoWalletType(); + + /** + * DID of the VASP that hosts the wallet. Needed when the wallet type is {@link CryptoWalletType#CUSTODIAL}, ignored otherwise. + */ + String getOwnerVaspDid(); +} diff --git a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/IVaspIdentification.java b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/IVaspIdentification.java new file mode 100644 index 000000000..9381dd435 --- /dev/null +++ b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/IVaspIdentification.java @@ -0,0 +1,35 @@ +/************************************************************************************* + * Copyright (C) 2014-2025 GENERAL BYTES s.r.o. All rights reserved. + * + * This software may be distributed and modified under the terms of the GNU + * General Public License version 2 (GPL2) as published by the Free Software + * Foundation and appearing in the file GPL2.TXT included in the packaging of + * this file. Please note that GPL2 Section 2[b] requires that all works based + * on this software must also be made publicly available under the terms of + * the GPL2 ("Copyleft"). + * + * Contact information + * ------------------- + * + * GENERAL BYTES s.r.o. + * Web : http://www.generalbytes.com + * + ************************************************************************************/ +package com.generalbytes.batm.server.extensions.travelrule; + +/** + * An object that identifies the VASP. + */ +public interface IVaspIdentification { + + /** + * @return DID (decentralized identifier) of VASP. + */ + String getDid(); + + /** + * @return Name of VASP. + */ + String getName(); + +} diff --git a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/TravelRuleProviderException.java b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/TravelRuleProviderException.java new file mode 100644 index 000000000..6ed226ede --- /dev/null +++ b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/TravelRuleProviderException.java @@ -0,0 +1,27 @@ +/************************************************************************************* + * Copyright (C) 2014-2025 GENERAL BYTES s.r.o. All rights reserved. + * + * This software may be distributed and modified under the terms of the GNU + * General Public License version 2 (GPL2) as published by the Free Software + * Foundation and appearing in the file GPL2.TXT included in the packaging of + * this file. Please note that GPL2 Section 2[b] requires that all works based + * on this software must also be made publicly available under the terms of + * the GPL2 ("Copyleft"). + * + * Contact information + * ------------------- + * + * GENERAL BYTES s.r.o. + * Web : http://www.generalbytes.com + * + ************************************************************************************/ +package com.generalbytes.batm.server.extensions.travelrule; + +/** + * Represents an exception that can be thrown by a Travel Rule Provider. + */ +public class TravelRuleProviderException extends RuntimeException { + public TravelRuleProviderException(String message) { + super(message); + } +} diff --git a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/TravelRuleProviderTransferStatus.java b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/TravelRuleProviderTransferStatus.java new file mode 100644 index 000000000..066578bbd --- /dev/null +++ b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/TravelRuleProviderTransferStatus.java @@ -0,0 +1,36 @@ +/************************************************************************************* + * Copyright (C) 2014-2025 GENERAL BYTES s.r.o. All rights reserved. + * + * This software may be distributed and modified under the terms of the GNU + * General Public License version 2 (GPL2) as published by the Free Software + * Foundation and appearing in the file GPL2.TXT included in the packaging of + * this file. Please note that GPL2 Section 2[b] requires that all works based + * on this software must also be made publicly available under the terms of + * the GPL2 ("Copyleft"). + * + * Contact information + * ------------------- + * + * GENERAL BYTES s.r.o. + * Web : http://www.generalbytes.com + * + ************************************************************************************/ +package com.generalbytes.batm.server.extensions.travelrule; + +/** + * Represents possible statuses of a transfer. + */ +public enum TravelRuleProviderTransferStatus { + /** + * The transfer has been approved by the VASP. Transfer will be processed. + */ + APPROVED, + /** + * The transfer has been rejected by the VASP, for any reason. Means transfer won't be processed. + */ + REJECTED, + /** + * The transfer is still pending approval by the VASP. Initial state when transfer is created. + */ + IN_PROGRESS +} diff --git a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/WalletTypeEvaluationResult.java b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/WalletTypeEvaluationResult.java index 66fe11029..c72e52379 100644 --- a/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/WalletTypeEvaluationResult.java +++ b/server_extensions_api/src/main/java/com/generalbytes/batm/server/extensions/travelrule/WalletTypeEvaluationResult.java @@ -1,5 +1,5 @@ /************************************************************************************* - * Copyright (C) 2014-2024 GENERAL BYTES s.r.o. All rights reserved. + * Copyright (C) 2014-2025 GENERAL BYTES s.r.o. All rights reserved. * * This software may be distributed and modified under the terms of the GNU * General Public License version 2 (GPL2) as published by the Free Software @@ -27,10 +27,18 @@ public class WalletTypeEvaluationResult { private final CryptoWalletType walletType; private final boolean belongsToIdentity; + private final Long travelRuleProviderId; + private final String vaspDid; - private WalletTypeEvaluationResult(CryptoWalletType walletType, boolean belongsToIdentity) { + private WalletTypeEvaluationResult(CryptoWalletType walletType, + boolean belongsToIdentity, + Long travelRuleProviderId, + String vaspDid + ) { this.walletType = walletType; this.belongsToIdentity = belongsToIdentity; + this.travelRuleProviderId = travelRuleProviderId; + this.vaspDid = vaspDid; } /** @@ -49,20 +57,67 @@ public boolean isBelongsToIdentity() { return belongsToIdentity; } + /** + * @return Get the VASP DID (decentralized identifier) of wallet owner. + */ + public String getVaspDid() { + return vaspDid; + } + + /** + * @return Get ID of the used Travel Rule Provider. + */ + public Long getTravelRuleProviderId() { + return travelRuleProviderId; + } + /** * Create a {@link WalletTypeEvaluationResult} for cases where a wallet type * is successfully evaluated. * - * @param walletType The {@link CryptoWalletType} of the evaluated wallet. + * @param walletType The {@link CryptoWalletType} of the evaluated wallet. * @param belongsToIdentity True if the wallet belongs to the provided identity, false otherwise. * @return The new {@link WalletTypeEvaluationResult}. * @throws IllegalArgumentException If the walletType is null. */ public static WalletTypeEvaluationResult evaluated(CryptoWalletType walletType, boolean belongsToIdentity) { + return evaluated(walletType, belongsToIdentity, null, null); + } + + /** + * Create a {@link WalletTypeEvaluationResult} for cases where a wallet type + * is successfully evaluated. + * + * @param walletType The {@link CryptoWalletType} of the evaluated wallet. + * @param belongsToIdentity True if the wallet belongs to the provided identity, false otherwise. + * @param travelRuleProviderId ID of the used Travel Rule Provider. + * @param vaspDid VASP DID (decentralized identifier) of wallet owner. + * @return The new {@link WalletTypeEvaluationResult}. + * @throws IllegalArgumentException If the walletType is null. + */ + public static WalletTypeEvaluationResult evaluated(CryptoWalletType walletType, + boolean belongsToIdentity, + Long travelRuleProviderId, + String vaspDid + ) { + validateEvaluatedResult(walletType, travelRuleProviderId, vaspDid); + return new WalletTypeEvaluationResult(walletType, belongsToIdentity, travelRuleProviderId, vaspDid); + } + + private static void validateEvaluatedResult(CryptoWalletType walletType, Long travelRuleProviderId, String vaspDid) { if (walletType == null) { throw new IllegalArgumentException("walletType cannot be null"); } - return new WalletTypeEvaluationResult(walletType, belongsToIdentity); + + if (walletType == CryptoWalletType.CUSTODIAL) { + if (travelRuleProviderId == null) { + throw new IllegalArgumentException("travelRuleProviderId cannot be null for custodial wallet"); + } + + if (vaspDid == null || vaspDid.isEmpty()) { + throw new IllegalArgumentException("vaspDid cannot be blank for custodial wallet"); + } + } } /** @@ -72,7 +127,7 @@ public static WalletTypeEvaluationResult evaluated(CryptoWalletType walletType, * @return The new {@link WalletTypeEvaluationResult}. */ public static WalletTypeEvaluationResult unknown() { - return new WalletTypeEvaluationResult(CryptoWalletType.UNKNOWN, false); + return new WalletTypeEvaluationResult(CryptoWalletType.UNKNOWN, false, null, null); } } diff --git a/server_extensions_api/src/test/java/com/generalbytes/batm/server/coinutil/Bech32Test.java b/server_extensions_api/src/test/java/com/generalbytes/batm/server/coinutil/Bech32Test.java index 404b85c8c..138291f9f 100644 --- a/server_extensions_api/src/test/java/com/generalbytes/batm/server/coinutil/Bech32Test.java +++ b/server_extensions_api/src/test/java/com/generalbytes/batm/server/coinutil/Bech32Test.java @@ -1,194 +1,197 @@ package com.generalbytes.batm.server.coinutil; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import java.util.Random; -public class Bech32Test { +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class Bech32Test { ///// BIP-173 test vectors ///// // https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki @Test - public void decodeValidAddresses1() throws AddressFormatException { + void decodeValidAddresses1() throws AddressFormatException { testValidAddress("BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4", "0014751e76e8199196d454941c45d1b3a323f1433bd6"); } @Test - public void decodeValidAddresses2() throws AddressFormatException { + void decodeValidAddresses2() throws AddressFormatException { testValidAddress("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262"); } @Test - public void decodeValidAddresses3() throws AddressFormatException { + void decodeValidAddresses3() throws AddressFormatException { testValidAddress("bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx", "5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6"); } @Test - public void decodeValidAddresses4() throws AddressFormatException { + void decodeValidAddresses4() throws AddressFormatException { testValidAddress("BC1SW50QA3JX3S", "6002751e"); } @Test - public void decodeValidAddresses5() throws AddressFormatException { + void decodeValidAddresses5() throws AddressFormatException { testValidAddress("bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj", "5210751e76e8199196d454941c45d1b3a323"); } @Test - public void decodeValidAddresses6() throws AddressFormatException { + void decodeValidAddresses6() throws AddressFormatException { testValidAddress("tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433"); } private void testValidAddress(String address, String hexData) throws AddressFormatException { Bech32.Bech32Data decoded = Bech32.decodeAddress(address); - Assert.assertEquals("HRP of " + address, address.substring(0, 2).toLowerCase(), decoded.hrp); + assertEquals(address.substring(0, 2).toLowerCase(), decoded.hrp, "HRP of " + address); String expected = hexData.substring(4); String actual = toHex(decoded.data); - Assert.assertEquals("Data of " + address, expected, actual); + assertEquals(expected, actual, "Data of " + address); } ///// The following string are not valid Bech32 ///// // HRP character out of range - @Test(expected = AddressFormatException.class) - public void decodeInvalid01() throws AddressFormatException { - Bech32.decode("\u00201nwldj5"); + @Test + void decodeInvalid01() { + assertThrows(AddressFormatException.class, () -> Bech32.decode("\u00201nwldj5")); } // HRP character out of range - @Test(expected = AddressFormatException.class) - public void decodeInvalid02() throws AddressFormatException { - Bech32.decode("\u007F1axkwrx"); + @Test + void decodeInvalid02() { + assertThrows(AddressFormatException.class, () -> Bech32.decode("\u007F1axkwrx")); } // HRP character out of range - @Test(expected = AddressFormatException.class) - public void decodeInvalid03() throws AddressFormatException { - Bech32.decode("\u00801eym55h"); + @Test + void decodeInvalid03() { + assertThrows(AddressFormatException.class, () -> Bech32.decode("\u00801eym55h")); } // overall max length exceeded - @Test(expected = AddressFormatException.class) - public void decodeInvalid04() throws AddressFormatException { - Bech32.decode("an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx"); + @Test + void decodeInvalid04() { + assertThrows(AddressFormatException.class, + () -> Bech32.decode("an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx")); } // No separator character - @Test(expected = AddressFormatException.class) - public void decodeInvalid05() throws AddressFormatException { - Bech32.decode("pzry9x0s0muk"); + @Test + void decodeInvalid05() { + assertThrows(AddressFormatException.class, () -> Bech32.decode("pzry9x0s0muk")); } // Empty HRP - @Test(expected = AddressFormatException.class) - public void decodeInvalid06() throws AddressFormatException { - Bech32.decode("1pzry9x0s0muk"); + @Test + void decodeInvalid06() { + assertThrows(AddressFormatException.class, () -> Bech32.decode("1pzry9x0s0muk")); } // Invalid data character - @Test(expected = AddressFormatException.class) - public void decodeInvalid07() throws AddressFormatException { - Bech32.decode("x1b4n0q5v"); + @Test + void decodeInvalid07() { + assertThrows(AddressFormatException.class, () -> Bech32.decode("x1b4n0q5v")); } // Too short checksum - @Test(expected = AddressFormatException.class) - public void decodeInvalid08() throws AddressFormatException { - Bech32.decode("li1dgmt3"); + @Test + void decodeInvalid08() { + assertThrows(AddressFormatException.class, () -> Bech32.decode("li1dgmt3")); } // Invalid character in checksum - @Test(expected = AddressFormatException.class) - public void decodeInvalid09() throws AddressFormatException { - Bech32.decode("de1lg7wt\u00FF"); + @Test + void decodeInvalid09() { + assertThrows(AddressFormatException.class, () -> Bech32.decode("de1lg7wt\u00FF")); } // checksum calculated with uppercase form of HRP - @Test(expected = AddressFormatException.class) - public void decodeInvalid10() throws AddressFormatException { - Bech32.decode("A1G7SGD8"); + @Test + void decodeInvalid10() { + assertThrows(AddressFormatException.class, () -> Bech32.decode("A1G7SGD8")); } // empty HRP - @Test(expected = AddressFormatException.class) - public void decodeInvalid11() throws AddressFormatException { - Bech32.decode("10a06t8"); + @Test + void decodeInvalid11() { + assertThrows(AddressFormatException.class, () -> Bech32.decode("10a06t8")); } // empty HRP - @Test(expected = AddressFormatException.class) - public void decodeInvalid12() throws AddressFormatException { - Bech32.decode("1qzzfhee"); + @Test + void decodeInvalid12() { + assertThrows(AddressFormatException.class, () -> Bech32.decode("1qzzfhee")); } ///// The following list gives invalid segwit addresses ///// // Invalid human-readable part - @Ignore("not checked") - @Test(expected = AddressFormatException.class) - public void decodeInvalidAddr01() throws AddressFormatException { - Bech32.decode("tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty"); + @Disabled("not checked") + @Test + void decodeInvalidAddr01() { + assertThrows(AddressFormatException.class, () -> Bech32.decode("tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty")); } // Invalid checksum - @Test(expected = AddressFormatException.class) - public void decodeInvalidAddr02() throws AddressFormatException { - Bech32.decode("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5"); + @Test + void decodeInvalidAddr02() { + assertThrows(AddressFormatException.class, () -> Bech32.decode("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5")); } // Invalid witness version - @Ignore("not checked") - @Test(expected = AddressFormatException.class) - public void decodeInvalidAddr03() throws AddressFormatException { - Bech32.decode("BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2"); + @Disabled("not checked") + @Test + void decodeInvalidAddr03() { + assertThrows(AddressFormatException.class, () -> Bech32.decode("BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2")); } // Invalid program length - @Ignore("not checked") - @Test(expected = AddressFormatException.class) - public void decodeInvalidAddr04() throws AddressFormatException { - Bech32.decode("bc1rw5uspcuh"); + @Disabled("not checked") + @Test + void decodeInvalidAddr04() { + assertThrows(AddressFormatException.class, () -> Bech32.decode("bc1rw5uspcuh")); } // Invalid program length - @Ignore("not checked") - @Test(expected = AddressFormatException.class) - public void decodeInvalidAddr05() throws AddressFormatException { - Bech32.decode("bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90"); + @Disabled("not checked") + @Test + void decodeInvalidAddr05() { + assertThrows(AddressFormatException.class, () -> Bech32.decode("bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90")); } // Invalid program length for witness version 0 (per BIP141) - @Ignore("not checked") - @Test(expected = AddressFormatException.class) - public void decodeInvalidAddr06() throws AddressFormatException { - Bech32.decode("BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P"); + @Disabled("not checked") + @Test + void decodeInvalidAddr06() { + assertThrows(AddressFormatException.class, () -> Bech32.decode("BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P")); } // Mixed case - @Test(expected = AddressFormatException.class) - public void decodeInvalidAddr07() throws AddressFormatException { - Bech32.decode("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7"); + @Test + void decodeInvalidAddr07() { + assertThrows(AddressFormatException.class, () -> Bech32.decode("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7")); } // zero padding of more than 4 bits - @Test(expected = IllegalArgumentException.class) - public void decodeInvalidAddr08() throws AddressFormatException { - Bech32.decodeAddress("bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du"); + @Test + void decodeInvalidAddr08() { + assertThrows(IllegalArgumentException.class, () -> Bech32.decodeAddress("bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du")); } // Non-zero padding in 8-to-5 conversion - @Test(expected = IllegalArgumentException.class) - public void decodeInvalidAddr09() throws AddressFormatException { - Bech32.decodeAddress("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv"); + @Test + void decodeInvalidAddr09() { + assertThrows(IllegalArgumentException.class, () -> Bech32.decodeAddress("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv")); } // Empty data section - @Test(expected = IllegalArgumentException.class) - public void decodeInvalidAddr10() throws AddressFormatException { - Bech32.decodeAddress("bc1gmk9yu"); + @Test + void decodeInvalidAddr10() { + assertThrows(IllegalArgumentException.class, () -> Bech32.decodeAddress("bc1gmk9yu")); } @@ -198,59 +201,59 @@ public void decodeInvalidAddr10() throws AddressFormatException { public static final String LNURL_DECODED = "https://service.com/api?q=3fc3645b439ce8e7f2553a69e5267081d96dcd340693afabe04be7b0ccd178df"; @Test - public void decodeLnurl() throws AddressFormatException { - Assert.assertEquals(LNURL_DECODED, Bech32.decodeString("lnurl", LNURL)); + void decodeLnurl() throws AddressFormatException { + assertEquals(LNURL_DECODED, Bech32.decodeString("lnurl", LNURL)); } - @Test(expected = AddressFormatException.class) - public void decodeLnurlEmpty() throws AddressFormatException { - Bech32.decodeString("lnurl", ""); + @Test + void decodeLnurlEmpty() { + assertThrows(AddressFormatException.class, () -> Bech32.decodeString("lnurl", "")); } - @Test(expected = NullPointerException.class) - public void decodeLnurlNull() throws AddressFormatException { - Bech32.decodeString("lnurl", null); + @Test + void decodeLnurlNull() { + assertThrows(NullPointerException.class, () -> Bech32.decodeString("lnurl", null)); } - @Test(expected = AddressFormatException.class) - public void decodeLnurlWrongHRP() throws AddressFormatException { - Bech32.decodeString("bc", LNURL); + @Test + void decodeLnurlWrongHRP() { + assertThrows(AddressFormatException.class, () -> Bech32.decodeString("bc", LNURL)); } @Test - public void encodeLnurl() { - Assert.assertEquals(LNURL.toLowerCase(), Bech32.encodeString("lnurl", LNURL_DECODED)); - Assert.assertEquals(LNURL.toLowerCase(), Bech32.encodeString("lnURL", LNURL_DECODED)); + void encodeLnurl() { + assertEquals(LNURL.toLowerCase(), Bech32.encodeString("lnurl", LNURL_DECODED)); + assertEquals(LNURL.toLowerCase(), Bech32.encodeString("lnURL", LNURL_DECODED)); } @Test - public void encodeLnurlEmpty() { - Assert.assertEquals("lnurl13myvsu", Bech32.encodeString("lnurl", "")); + void encodeLnurlEmpty() { + assertEquals("lnurl13myvsu", Bech32.encodeString("lnurl", "")); } - @Test(expected = NullPointerException.class) - public void encodeLnurlNull() { - Assert.assertEquals("", Bech32.encodeString("lnurl", null)); + @Test + void encodeLnurlNull() { + assertThrows(NullPointerException.class, () -> Bech32.encodeString("lnurl", null)); } - @Test(expected = NullPointerException.class) - public void encodeLnurlNullHrp() { - Assert.assertEquals("", Bech32.encodeString(null, LNURL_DECODED)); + @Test + void encodeLnurlNullHrp() { + assertThrows(NullPointerException.class, () -> Bech32.encodeString(null, LNURL_DECODED)); } - @Test(expected = IllegalArgumentException.class) - public void encodeLnurlEmptyHrp() { - Assert.assertEquals("", Bech32.encodeString("", LNURL_DECODED)); + @Test + void encodeLnurlEmptyHrp() { + assertThrows(IllegalArgumentException.class, () -> Bech32.encodeString("", LNURL_DECODED)); } @Test - public void encodeDecodeRandom() throws AddressFormatException { + void encodeDecodeRandom() throws AddressFormatException { Random random = new Random(0L); for (int stringLength = 100; stringLength < 350; stringLength += 11) { String generatedString = randomString(random, stringLength); String encoded = Bech32.encodeString("test", generatedString); String decoded = Bech32.decodeString("test", encoded); - Assert.assertEquals(generatedString, decoded); + assertEquals(generatedString, decoded); } } diff --git a/server_extensions_api/src/test/java/com/generalbytes/batm/server/coinutil/CoinUnitTest.java b/server_extensions_api/src/test/java/com/generalbytes/batm/server/coinutil/CoinUnitTest.java index d986219b8..21883c109 100644 --- a/server_extensions_api/src/test/java/com/generalbytes/batm/server/coinutil/CoinUnitTest.java +++ b/server_extensions_api/src/test/java/com/generalbytes/batm/server/coinutil/CoinUnitTest.java @@ -1,121 +1,122 @@ package com.generalbytes.batm.server.coinutil; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.math.BigDecimal; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; -public class CoinUnitTest { +class CoinUnitTest { /// satToBitcoin @Test - public void satToBitcoin() { + void satToBitcoin() { assertThat(CoinUnit.satToBitcoin(10_00000000L)).isEqualByComparingTo(BigDecimal.TEN); } @Test - public void satToBitcoin123() { + void satToBitcoin123() { assertThat(CoinUnit.satToBitcoin(123L)).isEqualByComparingTo("0.00000123"); } @Test - public void satToBitcoinZero() { + void satToBitcoinZero() { assertThat(CoinUnit.satToBitcoin(0L)).isEqualByComparingTo(BigDecimal.ZERO); } /// mSatToBitcoin @Test - public void mSatToBitcoin() { + void mSatToBitcoin() { assertThat(CoinUnit.mSatToBitcoin(10_00000000_000L)).isEqualByComparingTo(BigDecimal.TEN); } @Test - public void mSatToBitcoin123() { + void mSatToBitcoin123() { assertThat(CoinUnit.mSatToBitcoin(123L)).isEqualByComparingTo("0.00000000123"); } @Test - public void mSatToBitcoinZero() { + void mSatToBitcoinZero() { assertThat(CoinUnit.mSatToBitcoin(0L)).isEqualByComparingTo(BigDecimal.ZERO); } /// bitcoinToSat @Test - public void bitcoinToSat() { - Assert.assertEquals((Long) 10_00000000L, CoinUnit.bitcoinToSat(BigDecimal.TEN)); + void bitcoinToSat() { + assertEquals((Long) 10_00000000L, CoinUnit.bitcoinToSat(BigDecimal.TEN)); } @Test - public void bitcoinToSat123() { - Assert.assertEquals((Long) 123L, CoinUnit.bitcoinToSat(new BigDecimal("0.00000123"))); + void bitcoinToSat123() { + assertEquals((Long) 123L, CoinUnit.bitcoinToSat(new BigDecimal("0.00000123"))); } @Test - public void bitcoinToSatRound1() { - Assert.assertEquals((Long) 1L, CoinUnit.bitcoinToSat(new BigDecimal("0.000000011"))); + void bitcoinToSatRound1() { + assertEquals((Long) 1L, CoinUnit.bitcoinToSat(new BigDecimal("0.000000011"))); } @Test - public void bitcoinToSatRound9() { - Assert.assertEquals((Long) 1L, CoinUnit.bitcoinToSat(new BigDecimal("0.000000019"))); + void bitcoinToSatRound9() { + assertEquals((Long) 1L, CoinUnit.bitcoinToSat(new BigDecimal("0.000000019"))); } @Test - public void bitcoinToSatZero() { - Assert.assertEquals((Long) 0L, CoinUnit.bitcoinToSat(BigDecimal.ZERO)); + void bitcoinToSatZero() { + assertEquals((Long) 0L, CoinUnit.bitcoinToSat(BigDecimal.ZERO)); } /// bitcoinToMSat @Test - public void bitcoinToMSat() { - Assert.assertEquals((Long) 10_00000000_000L, CoinUnit.bitcoinToMSat(BigDecimal.TEN)); + void bitcoinToMSat() { + assertEquals((Long) 10_00000000_000L, CoinUnit.bitcoinToMSat(BigDecimal.TEN)); } @Test - public void bitcoinToMSat123() { - Assert.assertEquals((Long) 123L, CoinUnit.bitcoinToMSat(new BigDecimal("0.00000000123"))); + void bitcoinToMSat123() { + assertEquals((Long) 123L, CoinUnit.bitcoinToMSat(new BigDecimal("0.00000000123"))); } @Test - public void bitcoinToMSatRound1() { - Assert.assertEquals((Long) 1L, CoinUnit.bitcoinToMSat(new BigDecimal("0.000000000011"))); + void bitcoinToMSatRound1() { + assertEquals((Long) 1L, CoinUnit.bitcoinToMSat(new BigDecimal("0.000000000011"))); } @Test - public void bitcoinToMSatRound9() { - Assert.assertEquals((Long) 1L, CoinUnit.bitcoinToMSat(new BigDecimal("0.000000000019"))); + void bitcoinToMSatRound9() { + assertEquals((Long) 1L, CoinUnit.bitcoinToMSat(new BigDecimal("0.000000000019"))); } @Test - public void bitcoinToMSatZero() { - Assert.assertEquals((Long) 0L, CoinUnit.bitcoinToMSat(BigDecimal.ZERO)); + void bitcoinToMSatZero() { + assertEquals((Long) 0L, CoinUnit.bitcoinToMSat(BigDecimal.ZERO)); } /// null - @Test(expected = NullPointerException.class) - public void bitcoinToMSatNull() { - CoinUnit.bitcoinToMSat(null); + @Test + void bitcoinToMSatNull() { + assertThrows(NullPointerException.class, () -> CoinUnit.bitcoinToMSat(null)); } - @Test(expected = NullPointerException.class) - public void bitcoinToSatNull() { - CoinUnit.bitcoinToSat(null); + @Test + void bitcoinToSatNull() { + assertThrows(NullPointerException.class, () -> CoinUnit.bitcoinToSat(null)); } - @Test(expected = NullPointerException.class) - public void mSatToBitcoinNull() { - CoinUnit.mSatToBitcoin(null); + @Test + void mSatToBitcoinNull() { + assertThrows(NullPointerException.class, () -> CoinUnit.mSatToBitcoin(null)); } - @Test(expected = NullPointerException.class) - public void satToBitcoinNull() { - CoinUnit.satToBitcoin(null); + @Test + void satToBitcoinNull() { + assertThrows(NullPointerException.class, () -> CoinUnit.satToBitcoin(null)); } } \ No newline at end of file diff --git a/server_extensions_api/src/test/java/com/generalbytes/batm/server/coinutil/LnInvoiceUtilParseTest.java b/server_extensions_api/src/test/java/com/generalbytes/batm/server/coinutil/LnInvoiceUtilParseTest.java index 6ae1a9ed5..4a13eb340 100644 --- a/server_extensions_api/src/test/java/com/generalbytes/batm/server/coinutil/LnInvoiceUtilParseTest.java +++ b/server_extensions_api/src/test/java/com/generalbytes/batm/server/coinutil/LnInvoiceUtilParseTest.java @@ -1,129 +1,130 @@ package com.generalbytes.batm.server.coinutil; import com.generalbytes.batm.server.coinutil.LnInvoiceData.TagType; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -public class LnInvoiceUtilParseTest { +import static org.junit.jupiter.api.Assertions.*; + +class LnInvoiceUtilParseTest { @Test - public void zeroAmount() throws AddressFormatException { + void zeroAmount() throws AddressFormatException { LnInvoiceData d = LnInvoiceData.from("lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w"); - Assert.assertEquals(2, d.getTags().size()); - Assert.assertEquals(1, d.getTags(LnInvoiceData.TagType.payment_hash).size()); - Assert.assertEquals("0001020304050607080900010203040506070809000102030405060708090102", Hex.bytesToHexString(d.getTag(TagType.payment_hash).getData())); - Assert.assertEquals("Please consider supporting this project", getDescription(d)); + assertEquals(2, d.getTags().size()); + assertEquals(1, d.getTags(LnInvoiceData.TagType.payment_hash).size()); + assertEquals("0001020304050607080900010203040506070809000102030405060708090102", Hex.bytesToHexString(d.getTag(TagType.payment_hash).getData())); + assertEquals("Please consider supporting this project", getDescription(d)); } @Test - public void lnbc11() throws AddressFormatException { // 11 BTC, no multiplier + void lnbc11() throws AddressFormatException { // 11 BTC, no multiplier LnInvoiceData d = LnInvoiceData.from("lnbc111ps0aznupp5ueywkfv97xmpauhrneqjh7vuwqemjgsksg0rm89weu38v06r0ttsdqqcqzzgxqrrssrzjqw8c7yfutqqy3kz8662fxutjvef7q2ujsxtt45csu0k688lkzu3ld63fm02a9cknhcqqqqryqqqqthqqpysp5n0nz9acp8lag87yxqs6akqk7hzssmztwze6pjecuu0k8xkxndh9q9qypqsqr5knmj4c75deeut20ye7sv24sut3a39jw0z2sxcjn53marau0a8s5xvp8f07hkdfgeggrf98n067wlrzgnfefhzeztz74mks94uaz0qpzf9azn"); - Assert.assertEquals(7, d.getTags().size()); - Assert.assertEquals(1, d.getRecoveryFlag()); - Assert.assertEquals("1d2d3dcab8f51b9cf16a7933e8315587171ec4b273c4a81b129d23be8fbc7f4f", Hex.bytesToHexString(d.getSignatureR())); - Assert.assertEquals("0a19813a5febd9a9465081a4a79bf5e77c6244d394dc5912c5eaeed02d79d13c", Hex.bytesToHexString(d.getSignatureS())); - Assert.assertTrue(d.getTags().containsKey(TagType.routing)); - Assert.assertEquals("9be622f7013ffa83f8860435db02deb8a10d896e167419671ce3ec7358d36dca", Hex.bytesToHexString(d.getTag(TagType.secret).getData())); - Assert.assertEquals("e648eb2585f1b61ef2e39e412bf99c7033b92216821e3d9caecf22763f437ad7", Hex.bytesToHexString(d.getTag(TagType.payment_hash).getData())); - Assert.assertEquals(Integer.valueOf(72), d.getTag(TagType.min_final_cltv_expiry).getIntValue()); - Assert.assertEquals(Integer.valueOf(3600), d.getTag(TagType.expiry).getIntValue()); - Assert.assertArrayEquals(new byte[]{1, 0, 16, 0}, d.getTag(TagType.features).getData()); - Assert.assertEquals("", getDescription(d)); + assertEquals(7, d.getTags().size()); + assertEquals(1, d.getRecoveryFlag()); + assertEquals("1d2d3dcab8f51b9cf16a7933e8315587171ec4b273c4a81b129d23be8fbc7f4f", Hex.bytesToHexString(d.getSignatureR())); + assertEquals("0a19813a5febd9a9465081a4a79bf5e77c6244d394dc5912c5eaeed02d79d13c", Hex.bytesToHexString(d.getSignatureS())); + assertTrue(d.getTags().containsKey(TagType.routing)); + assertEquals("9be622f7013ffa83f8860435db02deb8a10d896e167419671ce3ec7358d36dca", Hex.bytesToHexString(d.getTag(TagType.secret).getData())); + assertEquals("e648eb2585f1b61ef2e39e412bf99c7033b92216821e3d9caecf22763f437ad7", Hex.bytesToHexString(d.getTag(TagType.payment_hash).getData())); + assertEquals(Integer.valueOf(72), d.getTag(TagType.min_final_cltv_expiry).getIntValue()); + assertEquals(Integer.valueOf(3600), d.getTag(TagType.expiry).getIntValue()); + assertArrayEquals(new byte[]{1, 0, 16, 0}, d.getTag(TagType.features).getData()); + assertEquals("", getDescription(d)); } @Test - public void hashed() throws AddressFormatException, NoSuchAlgorithmException { + void hashed() throws AddressFormatException, NoSuchAlgorithmException { LnInvoiceData d = LnInvoiceData.from("lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs6e4fy93me7wjwdf9sxgrzr8xldm570z02ur92rv6pa7wkhzpfehnecuyhp4mdhsv5t7em4jz4tjtchs8zmx3tr555yl59lk848due0gqvkanpl"); - Assert.assertEquals(1496314658, d.getTimestamp()); - Assert.assertEquals("1111111111111111111111111111111111111111111111111111111111111111", Hex.bytesToHexString(d.getTag(TagType.secret).getData())); - Assert.assertEquals("0001020304050607080900010203040506070809000102030405060708090102", Hex.bytesToHexString(d.getTag(TagType.payment_hash).getData())); + assertEquals(1496314658, d.getTimestamp()); + assertEquals("1111111111111111111111111111111111111111111111111111111111111111", Hex.bytesToHexString(d.getTag(TagType.secret).getData())); + assertEquals("0001020304050607080900010203040506070809000102030405060708090102", Hex.bytesToHexString(d.getTag(TagType.payment_hash).getData())); MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] description = "One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon".getBytes(StandardCharsets.UTF_8); byte[] expected = digest.digest(description); - Assert.assertArrayEquals(expected, d.getTag(TagType.description_hash).getData()); + assertArrayEquals(expected, d.getTag(TagType.description_hash).getData()); } @Test - public void lnbc20m() throws AddressFormatException { + void lnbc20m() throws AddressFormatException { LnInvoiceData d = LnInvoiceData.from("lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqscc6gd6ql3jrc5yzme8v4ntcewwz5cnw92tz0pc8qcuufvq7khhr8wpald05e92xw006sq94mg8v2ndf4sefvf9sygkshp5zfem29trqq2yxxz7"); - Assert.assertEquals(2, d.getTags().size()); - Assert.assertFalse(d.getTags().containsKey(TagType.description)); + assertEquals(2, d.getTags().size()); + assertFalse(d.getTags().containsKey(TagType.description)); } @Test - public void lnbc2500u() throws AddressFormatException { + void lnbc2500u() throws AddressFormatException { LnInvoiceData d = LnInvoiceData.from("lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspfj9srp"); - Assert.assertEquals(3, d.getTags().size()); - Assert.assertEquals("1 cup coffee", getDescription(d)); + assertEquals(3, d.getTags().size()); + assertEquals("1 cup coffee", getDescription(d)); } @Test - public void lnbc5555550n() throws AddressFormatException { + void lnbc5555550n() throws AddressFormatException { LnInvoiceData d = LnInvoiceData.from("lnbc5555550n1ps0azvfpp5jtfmhd3qtxa4txxnvm28fv52a0pr9kalvzjan2e62d4gl7h3m2xsdqu2askcmr9wssx7e3q2dshgmmndp5scqzpgxqyz5vqsp5vqwujjv6cka03aryl4znur6nxu8w2f3qg4s4faz6tuny026uepeq9qyyssq5646c8w8jrnea4wg3nd8yc4zytqz82hnvaywmplk0vlvpvfe0uxr6qgghksjc49nrcwqk9vxc5krlxsee3ngjjg0j0hs4t8458wedjsqh50wrc"); - Assert.assertEquals(6, d.getTags().size()); - Assert.assertEquals("Wallet of Satoshi", getDescription(d)); + assertEquals(6, d.getTags().size()); + assertEquals("Wallet of Satoshi", getDescription(d)); } @Test - public void lnbc9678785340p() throws AddressFormatException { + void lnbc9678785340p() throws AddressFormatException { LnInvoiceData d = LnInvoiceData.from("lnbc9678785340p1pwmna7lpp5gc3xfm08u9qy06djf8dfflhugl6p7lgza6dsjxq454gxhj9t7a0sd8dgfkx7cmtwd68yetpd5s9xar0wfjn5gpc8qhrsdfq24f5ggrxdaezqsnvda3kkum5wfjkzmfqf3jkgem9wgsyuctwdus9xgrcyqcjcgpzgfskx6eqf9hzqnteypzxz7fzypfhg6trddjhygrcyqezcgpzfysywmm5ypxxjemgw3hxjmn8yptk7untd9hxwg3q2d6xjcmtv4ezq7pqxgsxzmnyyqcjqmt0wfjjq6t5v4khxxqyjw5qcqp2rzjq0gxwkzc8w6323m55m4jyxcjwmy7stt9hwkwe2qxmy8zpsgg7jcuwz87fcqqeuqqqyqqqqlgqqqqn3qq9qn07ytgrxxzad9hc4xt3mawjjt8znfv8xzscs7007v9gh9j569lencxa8xeujzkxs0uamak9aln6ez02uunw6rd2ht2sqe4hz8thcdagpleym0j"); - Assert.assertEquals(5, d.getTags().size()); - Assert.assertEquals("Blockstream Store: 88.85 USD for Blockstream Ledger Nano S x 1, \"Back In My Day\" Sticker x 2, \"I Got Lightning Working\" Sticker x 2 and 1 more items", getDescription(d)); + assertEquals(5, d.getTags().size()); + assertEquals("Blockstream Store: 88.85 USD for Blockstream Ledger Nano S x 1, \"Back In My Day\" Sticker x 2, \"I Got Lightning Working\" Sticker x 2 and 1 more items", getDescription(d)); } @Test - public void testnetPrefix() throws AddressFormatException { + void testnetPrefix() throws AddressFormatException { LnInvoiceData d = LnInvoiceData.from("lntb20m1pvjluezhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfpp3x9et2e20v6pu37c5d9vax37wxq72un98kmzzhznpurw9sgl2v0nklu2g4d0keph5t7tj9tcqd8rexnd07ux4uv2cjvcqwaxgj7v4uwn5wmypjd5n69z2xm3xgksg28nwht7f6zspwp3f9t"); - Assert.assertEquals(3, d.getTags().size()); + assertEquals(3, d.getTags().size()); } @Test - public void invalidChecksum() { - AddressFormatException e = Assert.assertThrows(AddressFormatException.class, + void invalidChecksum() { + AddressFormatException e = assertThrows(AddressFormatException.class, () -> LnInvoiceData.from("lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrnt")); - Assert.assertEquals("Invalid Checksum", e.getMessage()); + assertEquals("Invalid Checksum", e.getMessage()); } @Test - public void malformedBech32() { - AddressFormatException e = Assert.assertThrows(AddressFormatException.class, + void malformedBech32() { + AddressFormatException e = assertThrows(AddressFormatException.class, () -> LnInvoiceData.from("pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny")); - Assert.assertEquals("Missing human-readable part", e.getMessage()); + assertEquals("Missing human-readable part", e.getMessage()); } @Test - public void mixedCase() { - AddressFormatException e = Assert.assertThrows(AddressFormatException.class, + void mixedCase() { + AddressFormatException e = assertThrows(AddressFormatException.class, () -> LnInvoiceData.from("LNBC2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny")); - Assert.assertEquals("Invalid character: u, pos: 8", e.getMessage()); + assertEquals("Invalid character: u, pos: 8", e.getMessage()); } @Test - public void invalidMultiplier() throws AddressFormatException { + void invalidMultiplier() throws AddressFormatException { LnInvoiceData d = LnInvoiceData.from("lnbc2500x1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpujr6jxr9gq9pv6g46y7d20jfkegkg4gljz2ea2a3m9lmvvr95tq2s0kvu70u3axgelz3kyvtp2ywwt0y8hkx2869zq5dll9nelr83zzqqpgl2zg"); - Assert.assertEquals(3, d.getTags().size()); - Assert.assertEquals("1 cup coffee", getDescription(d)); + assertEquals(3, d.getTags().size()); + assertEquals("1 cup coffee", getDescription(d)); } @Test - public void invalidPrecision() throws AddressFormatException { + void invalidPrecision() throws AddressFormatException { LnInvoiceData d = LnInvoiceData.from("lnbc2500000001p1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu7hqtk93pkf7sw55rdv4k9z2vj050rxdr6za9ekfs3nlt5lr89jqpdmxsmlj9urqumg0h9wzpqecw7th56tdms40p2ny9q4ddvjsedzcplva53s"); - Assert.assertEquals(3, d.getTags().size()); - Assert.assertEquals("1 cup coffee", getDescription(d)); + assertEquals(3, d.getTags().size()); + assertEquals("1 cup coffee", getDescription(d)); } @Test - public void cupOfNonsense() throws AddressFormatException { + void cupOfNonsense() throws AddressFormatException { LnInvoiceData d = LnInvoiceData.from("lnbc2500u1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuy0x9mrk2a32xsyvz6lzmrs38vzemux64pv6vdtmshnr9fc8eqgt4j74fnf3s60tzgeh6dnwzyv56wftaqwyyl3xu9nccq3py2hnnvsgqu5lqmm"); - Assert.assertEquals("ナンセンス 1杯", getDescription(d)); - Assert.assertEquals(Integer.valueOf(60), d.getTag(TagType.expiry).getIntValue()); + assertEquals("ナンセンス 1杯", getDescription(d)); + assertEquals(Integer.valueOf(60), d.getTag(TagType.expiry).getIntValue()); } private String getDescription(LnInvoiceData d) { diff --git a/server_extensions_api/src/test/java/com/generalbytes/batm/server/coinutil/LnInvoiceUtilTest.java b/server_extensions_api/src/test/java/com/generalbytes/batm/server/coinutil/LnInvoiceUtilTest.java index d7316c2ba..3eab6c26e 100644 --- a/server_extensions_api/src/test/java/com/generalbytes/batm/server/coinutil/LnInvoiceUtilTest.java +++ b/server_extensions_api/src/test/java/com/generalbytes/batm/server/coinutil/LnInvoiceUtilTest.java @@ -1,103 +1,103 @@ package com.generalbytes.batm.server.coinutil; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.math.BigDecimal; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; -public class LnInvoiceUtilTest { +class LnInvoiceUtilTest { private final LnInvoiceUtil util = new LnInvoiceUtil(); @Test - public void zeroAmount() { + void zeroAmount() { assertThat(util.getAmount("lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w")).isEqualByComparingTo(BigDecimal.ZERO); } @Test - public void lnbc11() { // 11 BTC, no multiplier + void lnbc11() { // 11 BTC, no multiplier assertThat(util.getAmount("lnbc111ps0aznupp5ueywkfv97xmpauhrneqjh7vuwqemjgsksg0rm89weu38v06r0ttsdqqcqzzgxqrrssrzjqw8c7yfutqqy3kz8662fxutjvef7q2ujsxtt45csu0k688lkzu3ld63fm02a9cknhcqqqqryqqqqthqqpysp5n0nz9acp8lag87yxqs6akqk7hzssmztwze6pjecuu0k8xkxndh9q9qypqsqr5knmj4c75deeut20ye7sv24sut3a39jw0z2sxcjn53marau0a8s5xvp8f07hkdfgeggrf98n067wlrzgnfefhzeztz74mks94uaz0qpzf9azn")).isEqualByComparingTo("11"); } @Test - public void lnbc20m() { + void lnbc20m() { assertThat(util.getAmount("lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqscc6gd6ql3jrc5yzme8v4ntcewwz5cnw92tz0pc8qcuufvq7khhr8wpald05e92xw006sq94mg8v2ndf4sefvf9sygkshp5zfem29trqq2yxxz7")).isEqualByComparingTo("0.020"); } @Test - public void lnbc2500u() { + void lnbc2500u() { assertThat(util.getAmount("lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspfj9srp")).isEqualByComparingTo("0.002500"); } @Test - public void lnbc5555550n() { + void lnbc5555550n() { assertThat(util.getAmount("lnbc5555550n1ps0azvfpp5jtfmhd3qtxa4txxnvm28fv52a0pr9kalvzjan2e62d4gl7h3m2xsdqu2askcmr9wssx7e3q2dshgmmndp5scqzpgxqyz5vqsp5vqwujjv6cka03aryl4znur6nxu8w2f3qg4s4faz6tuny026uepeq9qyyssq5646c8w8jrnea4wg3nd8yc4zytqz82hnvaywmplk0vlvpvfe0uxr6qgghksjc49nrcwqk9vxc5krlxsee3ngjjg0j0hs4t8458wedjsqh50wrc")).isEqualByComparingTo("0.005555550"); } @Test - public void lnbc9678785340p() { + void lnbc9678785340p() { assertThat(util.getAmount("lnbc9678785340p1pwmna7lpp5gc3xfm08u9qy06djf8dfflhugl6p7lgza6dsjxq454gxhj9t7a0sd8dgfkx7cmtwd68yetpd5s9xar0wfjn5gpc8qhrsdfq24f5ggrxdaezqsnvda3kkum5wfjkzmfqf3jkgem9wgsyuctwdus9xgrcyqcjcgpzgfskx6eqf9hzqnteypzxz7fzypfhg6trddjhygrcyqezcgpzfysywmm5ypxxjemgw3hxjmn8yptk7untd9hxwg3q2d6xjcmtv4ezq7pqxgsxzmnyyqcjqmt0wfjjq6t5v4khxxqyjw5qcqp2rzjq0gxwkzc8w6323m55m4jyxcjwmy7stt9hwkwe2qxmy8zpsgg7jcuwz87fcqqeuqqqyqqqqlgqqqqn3qq9qn07ytgrxxzad9hc4xt3mawjjt8znfv8xzscs7007v9gh9j569lencxa8xeujzkxs0uamak9aln6ez02uunw6rd2ht2sqe4hz8thcdagpleym0j")).isEqualByComparingTo("0.009678785340"); } @Test - public void testnetPrefix() { - IllegalArgumentException e = Assert.assertThrows(IllegalArgumentException.class, + void testnetPrefix() { + IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> util.getAmount("lntb20m1pvjluezhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfpp3x9et2e20v6pu37c5d9vax37wxq72un98kmzzhznpurw9sgl2v0nklu2g4d0keph5t7tj9tcqd8rexnd07ux4uv2cjvcqwaxgj7v4uwn5wmypjd5n69z2xm3xgksg28nwht7f6zspwp3f9t")); - Assert.assertEquals("Failed to match HRP pattern", e.getMessage()); + assertEquals("Failed to match HRP pattern", e.getMessage()); } @Test - public void invalidChecksum() { - IllegalArgumentException e = Assert.assertThrows(IllegalArgumentException.class, + void invalidChecksum() { + IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> util.getAmount("lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrnt")); - Assert.assertEquals("Cannot decode invoice", e.getMessage()); + assertEquals("Cannot decode invoice", e.getMessage()); } @Test - public void malformedBech32() { - IllegalArgumentException e = Assert.assertThrows(IllegalArgumentException.class, + void malformedBech32() { + IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> util.getAmount("pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny")); - Assert.assertEquals("Cannot decode invoice", e.getMessage()); + assertEquals("Cannot decode invoice", e.getMessage()); } @Test - public void mixedCase() { - IllegalArgumentException e = Assert.assertThrows(IllegalArgumentException.class, + void mixedCase() { + IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> util.getAmount("LNBC2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny")); - Assert.assertEquals("Cannot decode invoice", e.getMessage()); + assertEquals("Cannot decode invoice", e.getMessage()); } @Test - public void invalidMultiplier() { - IllegalArgumentException e = Assert.assertThrows(IllegalArgumentException.class, + void invalidMultiplier() { + IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> util.getAmount("lnbc2500x1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpujr6jxr9gq9pv6g46y7d20jfkegkg4gljz2ea2a3m9lmvvr95tq2s0kvu70u3axgelz3kyvtp2ywwt0y8hkx2869zq5dll9nelr83zzqqpgl2zg")); - Assert.assertEquals("Failed to match HRP pattern", e.getMessage()); + assertEquals("Failed to match HRP pattern", e.getMessage()); } @Test - public void invalidPrecision() { - IllegalArgumentException e = Assert.assertThrows(IllegalArgumentException.class, + void invalidPrecision() { + IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> util.getAmount("lnbc2500000001p1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu7hqtk93pkf7sw55rdv4k9z2vj050rxdr6za9ekfs3nlt5lr89jqpdmxsmlj9urqumg0h9wzpqecw7th56tdms40p2ny9q4ddvjsedzcplva53s")); - Assert.assertEquals("sub-millisatoshi amount", e.getMessage()); + assertEquals("sub-millisatoshi amount", e.getMessage()); } @Test - public void find() { - Assert.assertEquals("lnbc2500000001p1pvjluez", util.findInvoice("aaaa bbbb lnbc2500000001p1pvjluez cccc dddd")); - Assert.assertEquals("lnbc2500000001p1pvjluez", util.findInvoice("lnbc2500000001p1pvjluez")); - Assert.assertEquals("lnbc2500000001p1pvjluez", util.findInvoice(" \t lnbc2500000001p1pvjluez \n\t")); - Assert.assertEquals("lnbc1p1aaaa", util.findInvoice(" \t lnbc1p1aaaa \n\t lnbc2p1bbb")); + void find() { + assertEquals("lnbc2500000001p1pvjluez", util.findInvoice("aaaa bbbb lnbc2500000001p1pvjluez cccc dddd")); + assertEquals("lnbc2500000001p1pvjluez", util.findInvoice("lnbc2500000001p1pvjluez")); + assertEquals("lnbc2500000001p1pvjluez", util.findInvoice(" \t lnbc2500000001p1pvjluez \n\t")); + assertEquals("lnbc1p1aaaa", util.findInvoice(" \t lnbc1p1aaaa \n\t lnbc2p1bbb")); } @Test - public void findNull() { - Assert.assertNull(util.findInvoice(null)); - Assert.assertNull(util.findInvoice("")); - Assert.assertNull(util.findInvoice(" \t ")); - Assert.assertNull(util.findInvoice(" aaaaaaa ")); - Assert.assertNull(util.findInvoice("lntb2500000001p1pvjl")); - Assert.assertNull(util.findInvoice("lnbc250000000x1pvjluez")); + void findNull() { + assertNull(util.findInvoice(null)); + assertNull(util.findInvoice("")); + assertNull(util.findInvoice(" \t ")); + assertNull(util.findInvoice(" aaaaaaa ")); + assertNull(util.findInvoice("lntb2500000001p1pvjl")); + assertNull(util.findInvoice("lnbc250000000x1pvjluez")); } } \ No newline at end of file diff --git a/server_extensions_api/src/test/java/com/generalbytes/batm/server/coinutil/LnurlUtilTest.java b/server_extensions_api/src/test/java/com/generalbytes/batm/server/coinutil/LnurlUtilTest.java index a5a27d568..f19424691 100644 --- a/server_extensions_api/src/test/java/com/generalbytes/batm/server/coinutil/LnurlUtilTest.java +++ b/server_extensions_api/src/test/java/com/generalbytes/batm/server/coinutil/LnurlUtilTest.java @@ -1,47 +1,49 @@ package com.generalbytes.batm.server.coinutil; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; -public class LnurlUtilTest { +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class LnurlUtilTest { LnurlUtil lnurlUtil = new LnurlUtil(); @Test - public void decode() throws AddressFormatException { + void decode() throws AddressFormatException { String lnurlPay = "lightning:LNURL1DP68GURN8GHJ7MRWW4EXCTNXD9SHG6NPVCHXXMMD9AKXUATJDSKHQCTE8AEK2UMND9HKU0TPX5EXYVTYXEJX2E3KV43N2DFCXE3KXWTRX3JXXWFNVVMNJC3KXVCNJDRY8QUKZVFNXS6NYD3SVCMXYDF3VGMRJDM9XD3X2VEKVYMN2Q0JTL3"; String expected = "https://lnurl.fiatjaf.com/lnurl-pay?session=a52b1d6def6ec5586cc9c4dc93c79b63194d89a1345260f6b51b697e3be36a75"; - Assert.assertEquals(expected, lnurlUtil.decode(lnurlPay)); + assertEquals(expected, lnurlUtil.decode(lnurlPay)); } @Test - public void decodeWithoutScheme() throws AddressFormatException { + void decodeWithoutScheme() throws AddressFormatException { String lnurlPay = "LNURL1DP68GURN8GHJ7MRWW4EXCTNXD9SHG6NPVCHXXMMD9AKXUATJDSKHQCTE8AEK2UMND9HKU0TPX5EXYVTYXEJX2E3KV43N2DFCXE3KXWTRX3JXXWFNVVMNJC3KXVCNJDRY8QUKZVFNXS6NYD3SVCMXYDF3VGMRJDM9XD3X2VEKVYMN2Q0JTL3"; String expected = "https://lnurl.fiatjaf.com/lnurl-pay?session=a52b1d6def6ec5586cc9c4dc93c79b63194d89a1345260f6b51b697e3be36a75"; - Assert.assertEquals(expected, lnurlUtil.decode(lnurlPay)); + assertEquals(expected, lnurlUtil.decode(lnurlPay)); } @Test - public void schemeOnly() { - AddressFormatException e = Assert.assertThrows(AddressFormatException.class, () -> lnurlUtil.decode("lightning:")); - Assert.assertEquals("Missing human-readable part", e.getMessage()); + void schemeOnly() { + AddressFormatException e = assertThrows(AddressFormatException.class, () -> lnurlUtil.decode("lightning:")); + assertEquals("Missing human-readable part", e.getMessage()); } @Test - public void separatorOnly() { - AddressFormatException e = Assert.assertThrows(AddressFormatException.class, () -> lnurlUtil.decode(":")); - Assert.assertEquals("Missing human-readable part", e.getMessage()); + void separatorOnly() { + AddressFormatException e = assertThrows(AddressFormatException.class, () -> lnurlUtil.decode(":")); + assertEquals("Missing human-readable part", e.getMessage()); } @Test - public void empty() { - AddressFormatException e = Assert.assertThrows(AddressFormatException.class, () -> lnurlUtil.decode("")); - Assert.assertEquals("Missing human-readable part", e.getMessage()); + void empty() { + AddressFormatException e = assertThrows(AddressFormatException.class, () -> lnurlUtil.decode("")); + assertEquals("Missing human-readable part", e.getMessage()); } @Test - public void nullLnurl() { - NullPointerException e = Assert.assertThrows(NullPointerException.class, () -> lnurlUtil.decode(null)); - Assert.assertEquals("lnurl cannot be null", e.getMessage()); + void nullLnurl() { + NullPointerException e = assertThrows(NullPointerException.class, () -> lnurlUtil.decode(null)); + assertEquals("lnurl cannot be null", e.getMessage()); } } \ No newline at end of file diff --git a/server_extensions_api/src/test/java/com/generalbytes/batm/server/extensions/FixPriceRateSourceTest.java b/server_extensions_api/src/test/java/com/generalbytes/batm/server/extensions/FixPriceRateSourceTest.java index e7a44342d..ac55386dc 100644 --- a/server_extensions_api/src/test/java/com/generalbytes/batm/server/extensions/FixPriceRateSourceTest.java +++ b/server_extensions_api/src/test/java/com/generalbytes/batm/server/extensions/FixPriceRateSourceTest.java @@ -1,16 +1,16 @@ package com.generalbytes.batm.server.extensions; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.math.BigDecimal; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertNull; -public class FixPriceRateSourceTest { +class FixPriceRateSourceTest { @Test - public void testRateIsValid() { + void testRateIsValid() { doFixRateTest(new BigDecimal("10000000000"), false); doFixRateTest(new BigDecimal("9999999999.99999999991"), false); doFixRateTest(new BigDecimal("1000000000.123456789"), true); diff --git a/server_extensions_api/src/test/java/com/generalbytes/batm/server/extensions/customfields/CustomFieldDefinitionTypeTest.java b/server_extensions_api/src/test/java/com/generalbytes/batm/server/extensions/customfields/CustomFieldDefinitionTypeTest.java index 2da70f720..cb8a2e8cb 100644 --- a/server_extensions_api/src/test/java/com/generalbytes/batm/server/extensions/customfields/CustomFieldDefinitionTypeTest.java +++ b/server_extensions_api/src/test/java/com/generalbytes/batm/server/extensions/customfields/CustomFieldDefinitionTypeTest.java @@ -1,15 +1,16 @@ package com.generalbytes.batm.server.extensions.customfields; import com.generalbytes.batm.server.extensions.customfields.value.StringCustomFieldValue; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; -public class CustomFieldDefinitionTypeTest { + +class CustomFieldDefinitionTypeTest { @Test - public void isValueTypeAllowed() { + void isValueTypeAllowed() { StringCustomFieldValue str = new StringCustomFieldValue("test"); assertTrue(CustomFieldDefinitionType.PARAGRAPH.isValueTypeAllowed(str.getClass())); assertTrue(CustomFieldDefinitionType.SINGLE_LINE.isValueTypeAllowed(str.getClass())); diff --git a/server_extensions_api/src/test/java/com/generalbytes/batm/server/extensions/payment/PaymentRequestTest.java b/server_extensions_api/src/test/java/com/generalbytes/batm/server/extensions/payment/PaymentRequestTest.java index c931549ce..61421d265 100644 --- a/server_extensions_api/src/test/java/com/generalbytes/batm/server/extensions/payment/PaymentRequestTest.java +++ b/server_extensions_api/src/test/java/com/generalbytes/batm/server/extensions/payment/PaymentRequestTest.java @@ -1,16 +1,16 @@ package com.generalbytes.batm.server.extensions.payment; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.math.BigDecimal; import java.util.Arrays; import static org.assertj.core.api.Assertions.assertThat; -public class PaymentRequestTest { +class PaymentRequestTest { @Test - public void getForwardingTransactionMiningFee() { + void getForwardingTransactionMiningFee() { assertThat(getPaymentRequest(false, getPaymentOutput(new BigDecimal(8)), getPaymentOutput(BigDecimal.ONE)).getForwardingTransactionMiningFee()).isEqualByComparingTo(BigDecimal.ONE); assertThat(getPaymentRequest(null, getPaymentOutput(new BigDecimal(8)), getPaymentOutput(BigDecimal.ONE)).getForwardingTransactionMiningFee()).isEqualByComparingTo(BigDecimal.ONE); assertThat(getPaymentRequest(false, getPaymentOutput(new BigDecimal(9))).getForwardingTransactionMiningFee()).isEqualByComparingTo(BigDecimal.ONE); diff --git a/server_extensions_api/src/test/java/com/generalbytes/batm/server/extensions/util/ExtensionParametersTest.java b/server_extensions_api/src/test/java/com/generalbytes/batm/server/extensions/util/ExtensionParametersTest.java index 8704ad980..a31710f9a 100644 --- a/server_extensions_api/src/test/java/com/generalbytes/batm/server/extensions/util/ExtensionParametersTest.java +++ b/server_extensions_api/src/test/java/com/generalbytes/batm/server/extensions/util/ExtensionParametersTest.java @@ -1,15 +1,16 @@ package com.generalbytes.batm.server.extensions.util; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Arrays; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; -public class ExtensionParametersTest { + +class ExtensionParametersTest { @Test - public void test() { + void test() { testFromDelimited(null, null, 0); testFromDelimited("", "", 0); @@ -51,7 +52,7 @@ private void testFromDelimited(String expected, String input, int paramNumber) { } @Test - public void getPrefix() { + void getPrefix() { assertEquals("A", ExtensionParameters.fromDelimited("A:b:c:").getPrefix()); assertEquals("", ExtensionParameters.fromDelimited(":").getPrefix()); assertEquals("", ExtensionParameters.fromDelimited("").getPrefix()); @@ -59,7 +60,7 @@ public void getPrefix() { } @Test - public void getParameters() { + void getParameters() { assertEquals(1, ExtensionParameters.fromDelimited("P:").getWithoutPrefix().size()); assertEquals(0, ExtensionParameters.fromDelimited("P").getWithoutPrefix().size()); assertEquals(0, ExtensionParameters.fromDelimited("").getWithoutPrefix().size()); @@ -75,7 +76,7 @@ private enum Color { } @Test - public void getEnum() { + void getEnum() { assertEquals(Color.YELLOW, ExtensionParameters.fromDelimited("DOG:YELLOW:MEDIUM").get(1, Color.UNKNOWN)); assertEquals(Color.UNKNOWN, ExtensionParameters.fromDelimited("DOG:GRAY:MEDIUM").get(1, Color.UNKNOWN)); assertEquals(Color.UNKNOWN, ExtensionParameters.fromDelimited("DOG::MEDIUM").get(1, Color.UNKNOWN)); diff --git a/server_extensions_extra/build.gradle b/server_extensions_extra/build.gradle index 0bc283056..9cc788a59 100644 --- a/server_extensions_extra/build.gradle +++ b/server_extensions_extra/build.gradle @@ -96,15 +96,25 @@ dependencies { implementation("com.google.protobuf:protobuf-java:3.13.0") implementation("com.onfido:onfido-api-java:2.3.1") - testImplementation("junit:junit:4.13.1") + // keeping junit 4 for compatibility, will be removed soon, use junit 5 instead + testImplementation("junit:junit:4.13.2") + testImplementation("org.junit.vintage:junit-vintage-engine:5.11.4") + + testImplementation("org.junit.jupiter:junit-jupiter-api:5.11.4") + testImplementation("org.junit.jupiter:junit-jupiter-engine:5.11.4") + testImplementation("org.junit.jupiter:junit-jupiter-params:5.11.4") testImplementation("org.assertj:assertj-core:3.19.0") testImplementation("ch.qos.logback:logback-classic:1.2.9") testImplementation("ch.qos.logback:logback-core:1.2.9") testImplementation("org.assertj:assertj-core:3.19.0") testImplementation('org.glassfish.jersey.core:jersey-common:2.25.1') + testImplementation("org.mockito:mockito-core:4.11.0") + testImplementation("org.mockito:mockito-inline:4.11.0") + testImplementation("org.mockito:mockito-junit-jupiter:4.11.0") } test { + useJUnitPlatform() testLogging { events "failed" showExceptions true diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneApiFactory.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneApiFactory.java new file mode 100644 index 000000000..2bb56b0cf --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneApiFactory.java @@ -0,0 +1,24 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene; + +import com.generalbytes.batm.server.extensions.travelrule.notabene.api.NotabeneApi; +import com.generalbytes.batm.server.extensions.travelrule.notabene.api.NotabeneAuthApi; +import lombok.AllArgsConstructor; +import si.mazi.rescu.RestProxyFactory; + +/** + * Factory for creating Notabene API proxies. + */ +@AllArgsConstructor +public class NotabeneApiFactory { + + private final NotabeneConfiguration configuration; + + public NotabeneApi getNotabeneApi() { + return RestProxyFactory.createProxy(NotabeneApi.class, configuration.getApiUrl()); + } + + public NotabeneAuthApi getNotabeneAuthApi() { + return RestProxyFactory.createProxy(NotabeneAuthApi.class, configuration.getAuthApiUrl()); + } + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneApiService.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneApiService.java new file mode 100644 index 000000000..ce6c19b18 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneApiService.java @@ -0,0 +1,61 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene; + +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleProviderCredentials; +import com.generalbytes.batm.server.extensions.travelrule.notabene.api.NotabeneApiCall; +import com.generalbytes.batm.server.extensions.travelrule.notabene.api.NotabeneApiException; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import javax.servlet.http.HttpServletResponse; + +/** + * This service is responsible for making calls to Notabene API. + */ +@Slf4j +@AllArgsConstructor +public class NotabeneApiService { + + private static final String UNAUTHORIZED_ERROR_NAME = "UnauthorizedError"; + private static final int MAX_REFRESH_ACCESS_TOKEN_CALLS = 5; + + private final NotabeneAuthService authService; + + /** + * Make a call to Notabene API. + * + *

This method will take care of authorization.

+ * + * @param providerCredentials The used {@link ITravelRuleProviderCredentials}. + * @param apiCall The call to make. + * @param Type of the expected response. + * @return Response from the call. + */ + public T callApi(ITravelRuleProviderCredentials providerCredentials, NotabeneApiCall apiCall) { + return callApiInternal(providerCredentials, apiCall, MAX_REFRESH_ACCESS_TOKEN_CALLS); + } + + private T callApiInternal(ITravelRuleProviderCredentials providerCredentials, NotabeneApiCall apiCall, int remainingRepetitions) { + try { + String accessToken = authService.getAccessToken(providerCredentials); + String authorization = getAuthorizationHeaderContent(accessToken); + return apiCall.execute(authorization); + } catch (NotabeneApiException e) { + log.trace("Notabene API call failed", e); + if (e.getCode() == HttpServletResponse.SC_UNAUTHORIZED && UNAUTHORIZED_ERROR_NAME.equalsIgnoreCase(e.getName())) { + if (remainingRepetitions <= 0) { + log.warn("Failed to refresh access token after {} attempts", MAX_REFRESH_ACCESS_TOKEN_CALLS); + throw e; + } + authService.refreshAccessToken(providerCredentials); + + return callApiInternal(providerCredentials, apiCall, --remainingRepetitions); + } + throw e; + } + } + + private String getAuthorizationHeaderContent(String accessToken) { + return "Bearer " + accessToken; + } + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneApiWrapper.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneApiWrapper.java new file mode 100644 index 000000000..585555fc1 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneApiWrapper.java @@ -0,0 +1,115 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene; + +import com.fasterxml.jackson.core.JsonParseException; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleProviderCredentials; +import com.generalbytes.batm.server.extensions.travelrule.notabene.api.NotabeneApi; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneAddressOwnershipInfoRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneAddressOwnershipInfoResponse; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneFullyValidateTransferResponse; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneListVaspsQueryParams; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneListVaspsResponse; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneRegisterWebhookRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferCreateRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferInfo; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferUpdateRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneUnregisterWebhookRequest; +import lombok.extern.slf4j.Slf4j; + +/** + * Wrapper for {@link NotabeneApi} providing easy access to Notabene API. + * + * @see NotabeneApi + * @see NotabeneApiService + */ +@Slf4j +public class NotabeneApiWrapper { + + private final NotabeneApi api; + private final NotabeneApiService apiService; + + public NotabeneApiWrapper(NotabeneApiFactory apiFactory, NotabeneApiService apiService) { + this.api = apiFactory.getNotabeneApi(); + this.apiService = apiService; + } + + /** + * @see NotabeneApi#listVasps(String, String, Boolean) + */ + public NotabeneListVaspsResponse listVasps(ITravelRuleProviderCredentials providerCredentials, NotabeneListVaspsQueryParams queryParams) { + return apiService.callApi(providerCredentials, authorization -> api.listVasps(authorization, queryParams.getQuery(), + queryParams.getAll())); + } + + /** + * @see NotabeneApi#validateFull(String, NotabeneTransferCreateRequest) + */ + public NotabeneFullyValidateTransferResponse validateFull(ITravelRuleProviderCredentials providerCredentials, + NotabeneTransferCreateRequest request) { + return apiService.callApi(providerCredentials, authorization -> api.validateFull(authorization, request)); + } + + /** + * @see NotabeneApi#createTransfer(String, NotabeneTransferCreateRequest) + */ + public NotabeneTransferInfo createTransfer(ITravelRuleProviderCredentials providerCredentials, + NotabeneTransferCreateRequest request) { + return apiService.callApi(providerCredentials, authorization -> api.createTransfer(authorization, request)); + } + + /** + * @see NotabeneApi#updateTransfer(String, NotabeneTransferUpdateRequest) + */ + public NotabeneTransferInfo updateTransfer(ITravelRuleProviderCredentials providerCredentials, + NotabeneTransferUpdateRequest request) { + return apiService.callApi(providerCredentials, authorization -> api.updateTransfer(authorization, request)); + } + + /** + * @see NotabeneApi#getAddressOwnershipInformation(String, String, String, String) + */ + public NotabeneAddressOwnershipInfoResponse getAddressOwnershipInformation(ITravelRuleProviderCredentials providerCredentials, + NotabeneAddressOwnershipInfoRequest request) { + return apiService.callApi(providerCredentials, authorization -> api.getAddressOwnershipInformation(authorization, + request.getAddress(), request.getVaspDid(), request.getAsset())); + } + + /** + * @see NotabeneApi#approveTransfer(String, String) + */ + public NotabeneTransferInfo approveTransfer(ITravelRuleProviderCredentials providerCredentials, String transferId) { + return apiService.callApi(providerCredentials, authorization -> api.approveTransfer(authorization, transferId)); + } + + /** + * @see NotabeneApi#registerWebhook(String, NotabeneRegisterWebhookRequest); + */ + public void registerWebhook(ITravelRuleProviderCredentials providerCredentials, NotabeneRegisterWebhookRequest request) { + apiService.callApi(providerCredentials, authorization -> { + try { + api.registerWebhook(authorization, request); + } catch (JsonParseException e) { + // On success, the endpoint returns plain text which cannot be parsed as json. + // This is a problem because mazi.rescu only supports one media type in @Produces, + // and we need json for parsing NotabeneApiException. + } + return null; + }); + } + + /** + * @see NotabeneApi#unregisterWebhook(String, NotabeneUnregisterWebhookRequest); + */ + public void unregisterWebhook(ITravelRuleProviderCredentials providerCredentials, NotabeneUnregisterWebhookRequest request) { + apiService.callApi(providerCredentials, authorization -> { + try { + api.unregisterWebhook(authorization, request); + } catch (JsonParseException e) { + // On success, the endpoint returns plain text which cannot be parsed as json. + // This is a problem because mazi.rescu only supports one media type in @Produces, + // and we need json for parsing NotabeneApiException. + } + return null; + }); + } + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneAuthService.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneAuthService.java new file mode 100644 index 000000000..10d60d05a --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneAuthService.java @@ -0,0 +1,143 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene; + +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleProviderCredentials; +import com.generalbytes.batm.server.extensions.travelrule.notabene.api.NotabeneAuthApi; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneGenerateAccessTokenRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneGenerateAccessTokenResponse; +import lombok.extern.slf4j.Slf4j; +import si.mazi.rescu.HttpStatusIOException; + +import javax.servlet.http.HttpServletResponse; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +/** + * This service is responsible for managing the access token for Notabene API. + */ +@Slf4j +public class NotabeneAuthService { + + private final Map accessTokens = new ConcurrentHashMap<>(); + private final Map> tokenRequests = new ConcurrentHashMap<>(); + private final NotabeneAuthApi notabeneAuthApi; + private final NotabeneConfiguration configuration; + + public NotabeneAuthService(NotabeneApiFactory apiFactory, NotabeneConfiguration configuration) { + this.notabeneAuthApi = apiFactory.getNotabeneAuthApi(); + this.configuration = configuration; + } + + public void removeAccessToken(ITravelRuleProviderCredentials providerCredentials) { + String clientId = providerCredentials.getClientId(); + synchronized (tokenRequests) { + log.debug("Clearing access token for clientId: {}", clientId); + CompletableFuture future = tokenRequests.get(clientId); + if (future != null && !future.isDone()) { + try { + future.join(); + } catch (Exception e) { + log.trace("An error occurred while waiting for the token request to complete. Does not affect the token removal.", e); + } + } + String accessToken = accessTokens.remove(clientId); + tokenRequests.remove(clientId); + + if (accessToken != null) { + log.debug("Removed access token for clientId: {}", clientId); + } else { + log.debug("No access token found for clientId: {}", clientId); + } + } + } + + /** + * Get the current access token. + * + *

There is no guarantee that this token is valid. If it is not, + * use {@link #refreshAccessToken(ITravelRuleProviderCredentials)} to refresh it.

+ * + * @param providerCredentials The {@link ITravelRuleProviderCredentials} to get the access token for. + * @return The access token. + * @throws IllegalArgumentException If providerCredentials is null. + */ + public String getAccessToken(ITravelRuleProviderCredentials providerCredentials) { + if (providerCredentials == null) { + throw new IllegalArgumentException("providerCredentials cannot be null"); + } + + if (tokenRequests.containsKey(providerCredentials.getClientId())) { + tokenRequests.get(providerCredentials.getClientId()).join(); + } + + return accessTokens.get(providerCredentials.getClientId()); + } + + /** + * Refresh the access token for the given {@link ITravelRuleProviderCredentials}. + * + *

Use {@link #getAccessToken(ITravelRuleProviderCredentials)} to get the current token.

+ * + * @param providerCredentials The {@link ITravelRuleProviderCredentials} to refresh the token for. + */ + public void refreshAccessToken(ITravelRuleProviderCredentials providerCredentials) { + if (providerCredentials == null) { + throw new IllegalArgumentException("providerCredentials cannot be null"); + } + + tokenRequests.computeIfAbsent(providerCredentials.getClientId(), getAccessTokenAsync(providerCredentials)) + .whenComplete((result, throwable) -> handleCompletedCall(providerCredentials, throwable)); + } + + private Function> getAccessTokenAsync(ITravelRuleProviderCredentials providerCredentials) { + return clientId -> CompletableFuture.supplyAsync(() -> getAccessToken(providerCredentials, clientId)); + } + + private String getAccessToken(ITravelRuleProviderCredentials providerCredentials, String clientId) { + NotabeneGenerateAccessTokenRequest request = createNotabeneGenerateAccessTokenRequest(providerCredentials); + NotabeneGenerateAccessTokenResponse response = callGetAccessTokenApi(request); + if (response == null) { + return null; + } + accessTokens.put(clientId, response.getAccessToken()); + return response.getAccessToken(); + } + + private void handleCompletedCall(ITravelRuleProviderCredentials providerCredentials, Throwable throwable) { + if (throwable != null) { + log.error("Failed to refresh access token: {}", throwable.getMessage()); + } else { + tokenRequests.remove(providerCredentials.getClientId()); + } + } + + private NotabeneGenerateAccessTokenResponse callGetAccessTokenApi(NotabeneGenerateAccessTokenRequest request) { + try { + return notabeneAuthApi.generateAccessToken(request); + } catch (HttpStatusIOException e) { + if (e.getHttpStatusCode() == HttpServletResponse.SC_UNAUTHORIZED) { + throw new CompletionException("unauthorized request", e); + } + if (e.getHttpStatusCode() == HttpServletResponse.SC_FORBIDDEN) { + throw new CompletionException("forbidden request", e); + } + log.error("Failed to get access token, unexpected error, status: {}, message: {}, response: {}", + e.getHttpStatusCode(), e.getMessage(), e.getHttpBody()); + } catch (Exception e) { + log.error("Failed to refresh access token, unexpected error", e); + } + return null; + } + + private NotabeneGenerateAccessTokenRequest createNotabeneGenerateAccessTokenRequest(ITravelRuleProviderCredentials travelRuleProvider) { + NotabeneGenerateAccessTokenRequest request = new NotabeneGenerateAccessTokenRequest(); + request.setClientId(travelRuleProvider.getClientId()); + request.setClientSecret(travelRuleProvider.getClientSecret()); + request.setGrantType("client_credentials"); + request.setAudience(configuration.getApiUrl()); + return request; + } + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneConfiguration.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneConfiguration.java new file mode 100644 index 000000000..39f2297e3 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneConfiguration.java @@ -0,0 +1,11 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene; + +import lombok.Data; + +@Data +public class NotabeneConfiguration { + private String apiUrl; + private String authApiUrl; + private boolean automaticApprovalOfOutgoingTransfersEnabled; + private String masterExtensionsUrl; +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneExtension.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneExtension.java new file mode 100644 index 000000000..acb6d02d3 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneExtension.java @@ -0,0 +1,103 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene; + +import com.generalbytes.batm.server.extensions.AbstractExtension; +import com.generalbytes.batm.server.extensions.IExtensionContext; +import com.generalbytes.batm.server.extensions.IRestService; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleProviderFactory; +import lombok.extern.slf4j.Slf4j; + +import java.util.HashSet; +import java.util.Set; + +/** + * Extension for Notabene travel rule provider implementation. + */ +@Slf4j +public class NotabeneExtension extends AbstractExtension { + + /** + * Configuration file for Notabene travel rule provider implementation, in '/batm/config' directory. + */ + private static final String NOTABENE_CONFIG_FILE = "notabene"; + + private Set restServices = null; + private NotabeneConfiguration configuration = null; + + @Override + public String getName() { + return "Travel rule extension for Notabene"; + } + + @Override + public void init(IExtensionContext ctx) { + log.info("Initializing Notabene extension"); + super.init(ctx); + configuration = getConfiguration(); + restServices = getServices(); + log.info("Notabene extension initialized"); + } + + @Override + public Set getTravelRuleProviderFactories() { + if (configuration == null) { + throw new IllegalStateException("Extension not initialized yet"); + } + return Set.of(new NotabeneProviderFactory(configuration)); + } + + @Override + public Set getRestServices() { + if (restServices == null) { + throw new IllegalStateException("Extension not initialized yet"); + } + return restServices; + } + + private Set getServices() { + Set services = new HashSet<>(); + if (isNotabeneWebhooksEnabled()) { + services.add(new NotabeneWebhookRestService()); + log.info("Notabene webhook initialized"); + } else { + log.info("Notabene webhook disabled, to enable set 'webhooksEnabled' to 'true' in the Notabene configuration"); + } + return services; + } + + private NotabeneConfiguration getConfiguration() { + NotabeneConfiguration notabeneConfiguration = new NotabeneConfiguration(); + notabeneConfiguration.setApiUrl(getNotabeneApiUrl()); + notabeneConfiguration.setAuthApiUrl(getNotabeneAuthApiUrl()); + notabeneConfiguration.setAutomaticApprovalOfOutgoingTransfersEnabled(isNotabeneAutomaticApprovalOfOutgoingTransfersEnabled()); + notabeneConfiguration.setMasterExtensionsUrl(getServerUrl()); + log.info("Using Notabene configuration: {}", notabeneConfiguration); + return notabeneConfiguration; + } + + private String getNotabeneApiUrl() { + return getConfiguredProperty("apiUrl", "https://api.notabene.id"); + } + + private String getNotabeneAuthApiUrl() { + return getConfiguredProperty("authApiUrl", "https://auth.notabene.id"); + } + + private boolean isNotabeneAutomaticApprovalOfOutgoingTransfersEnabled() { + String automaticApprovalEnabled = getConfiguredProperty("automaticallyApproveOutgoingTransfers", null); + return "true".equalsIgnoreCase(automaticApprovalEnabled); + } + + private boolean isNotabeneWebhooksEnabled() { + String webhooksEnabled = getConfiguredProperty("webhooksEnabled", null); + return "true".equalsIgnoreCase(webhooksEnabled); + } + + private String getConfiguredProperty(String key, String defaultValue) { + return ctx.getConfigProperty(NOTABENE_CONFIG_FILE, key, defaultValue); + } + + private String getServerUrl() { + String masterHost = ctx.getConfigProperty("network", "master_bind_ip", null); + return String.format("https://%s:7743/extensions", masterHost); + } +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneProviderFactory.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneProviderFactory.java new file mode 100644 index 000000000..3f43d932c --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneProviderFactory.java @@ -0,0 +1,56 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene; + +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleProvider; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleProviderCredentials; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleProviderFactory; + +import java.util.HashMap; +import java.util.Map; + +public class NotabeneProviderFactory implements ITravelRuleProviderFactory { + + private final NotabeneService notabeneService; + private final NotabeneTransferPublisher notabeneTransferPublisher; + private final NotabeneAuthService notabeneAuthService; + private final NotabeneConfiguration configuration; + + /** + * Already initialized travel rule providers. VASP DID is used as a key to avoid multiple initializations based on same settings. + */ + private final Map travelRuleProviders = new HashMap<>(); + + public NotabeneProviderFactory(NotabeneConfiguration configuration) { + this.configuration = configuration; + notabeneTransferPublisher = NotabeneTransferPublisher.getInstance(); + + NotabeneApiFactory notabeneApiFactory = new NotabeneApiFactory(configuration); + notabeneAuthService = new NotabeneAuthService(notabeneApiFactory, configuration); + NotabeneApiService notabeneApiService = new NotabeneApiService(notabeneAuthService); + NotabeneApiWrapper notabeneApiWrapper = new NotabeneApiWrapper(notabeneApiFactory, notabeneApiService); + notabeneService = new NotabeneService(notabeneApiWrapper, configuration); + } + + @Override + public String getProviderName() { + return NotabeneTravelRuleProvider.NAME; + } + + @Override + public synchronized ITravelRuleProvider getProvider(ITravelRuleProviderCredentials credentials) { + String vaspDid = credentials.getVaspDid(); + if (vaspDid == null) { + return initializeProvider(credentials); + } + if (travelRuleProviders.containsKey(vaspDid)) { + return travelRuleProviders.get(vaspDid); + } + NotabeneTravelRuleProvider notabeneTravelRuleProvider = initializeProvider(credentials); + travelRuleProviders.put(vaspDid, notabeneTravelRuleProvider); + + return notabeneTravelRuleProvider; + } + + private NotabeneTravelRuleProvider initializeProvider(ITravelRuleProviderCredentials credentials) { + return new NotabeneTravelRuleProvider(credentials, configuration, notabeneAuthService, notabeneService, notabeneTransferPublisher); + } +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneService.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneService.java new file mode 100644 index 000000000..719070804 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneService.java @@ -0,0 +1,205 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene; + +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleProviderCredentials; +import com.generalbytes.batm.server.extensions.travelrule.TravelRuleProviderException; +import com.generalbytes.batm.server.extensions.travelrule.notabene.api.NotabeneApiException; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneAddressOwnershipInfoRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneAddressOwnershipInfoResponse; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneCryptoAddressType; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneListVaspsQueryParams; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneListVaspsResponse; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneRegisterWebhookRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferCreateRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferInfo; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferUpdateRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneUnregisterWebhookRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneVaspInfoSimple; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +@Slf4j +@AllArgsConstructor +public class NotabeneService { + + private final NotabeneApiWrapper api; + private final NotabeneConfiguration configuration; + + /** + * Get all available VASPs from Notabene. + * + * @param providerCredentials The {@link ITravelRuleProviderCredentials} to get the VASPs for. + * @return List of available VASPs. Empty if the retrieval fails. + */ + public List getAllVasps(ITravelRuleProviderCredentials providerCredentials) { + NotabeneListVaspsQueryParams queryParams = new NotabeneListVaspsQueryParams(); + return getVasps(providerCredentials, queryParams); + } + + private List getVasps(ITravelRuleProviderCredentials providerCredentials, NotabeneListVaspsQueryParams queryParams) { + try { + NotabeneListVaspsResponse response = api.listVasps(providerCredentials, queryParams); + return response.getVasps(); + } catch (Exception e) { + log.warn("Failed to get VASPs from Notabene: {}", getExceptionMessage(e)); + throw new TravelRuleProviderException("Failed to fetch VASPs from Notabene."); + } + } + + /** + * Create a new transfer. + * + * @param providerCredentials The {@link ITravelRuleProviderCredentials} to create the transfer for. + * @param request The request. + * @return A {@link NotabeneTransferInfo} or null if the creation fails. + */ + public NotabeneTransferInfo createTransfer(ITravelRuleProviderCredentials providerCredentials, NotabeneTransferCreateRequest request) { + try { + return api.createTransfer(providerCredentials, request); + } catch (Exception e) { + log.warn("Failed to create transfer at Notabene: {}", getExceptionMessage(e)); + return null; + } + } + + /** + * Approve an existing transfer. + * + * @param providerCredentials The {@link ITravelRuleProviderCredentials} to approve the transfer for. + * @param transferId Identifier of the transfer to approve. + * @return The approved {@link NotabeneTransferInfo} or null if the approval fails. + */ + public NotabeneTransferInfo approveTransfer(ITravelRuleProviderCredentials providerCredentials, String transferId) { + try { + return api.approveTransfer(providerCredentials, transferId); + } catch (Exception e) { + log.warn("Failed to approve transfer at Notabene: {}", getExceptionMessage(e)); + return null; + } + } + + /** + * Update an existing transfer. + * + * @param providerCredentials The {@link ITravelRuleProviderCredentials} to update the transfer for. + * @param request The request. + * @return The updated {@link NotabeneTransferInfo} or null if the update fails. + */ + public NotabeneTransferInfo updateTransfer(ITravelRuleProviderCredentials providerCredentials, NotabeneTransferUpdateRequest request) { + try { + return api.updateTransfer(providerCredentials, request); + } catch (Exception e) { + log.warn("Failed to update transfer at Notabene: {}", getExceptionMessage(e)); + return null; + } + } + + /** + * Get the ownership information about a customer blockchain address. + * + * @param providerCredentials The {@link ITravelRuleProviderCredentials} to create the transfer for. + * @param request The request. + * @return A {@link NotabeneAddressOwnershipInfoResponse}. + */ + public NotabeneAddressOwnershipInfoResponse getAddressOwnershipInformation(ITravelRuleProviderCredentials providerCredentials, + NotabeneAddressOwnershipInfoRequest request) { + try { + return api.getAddressOwnershipInformation(providerCredentials, request); + } catch (Exception e) { + if (isCausedByHttpNotFound(e)) { + // 404 Not Found -> Notabene does not have the information, which is not a failure. + return createNotabeneAddressOwnershipInfoResponseUnknown(); + } + log.warn("Failed to get address ownership information from Notabene: {}", getExceptionMessage(e)); + return null; + } + } + + private NotabeneAddressOwnershipInfoResponse createNotabeneAddressOwnershipInfoResponseUnknown() { + NotabeneAddressOwnershipInfoResponse response = new NotabeneAddressOwnershipInfoResponse(); + response.setAddressType(NotabeneCryptoAddressType.UNKNOWN); + return response; + } + + /** + * Register a webhook. This operation is idempotent. + * + * @param providerCredentials The {@link ITravelRuleProviderCredentials} to register the webhook for. + * @return True if the webhook was registered, false otherwise. + */ + public boolean registerWebhook(ITravelRuleProviderCredentials providerCredentials) { + NotabeneRegisterWebhookRequest request = new NotabeneRegisterWebhookRequest(); + request.setVaspDid(providerCredentials.getVaspDid()); + String webhookUrl = getWebhookUrl(); + log.info("Registering webhook at Notabene: {}", webhookUrl); + request.setUrl(webhookUrl); + + try { + api.registerWebhook(providerCredentials, request); + return true; + } catch (Exception e) { + log.error("Failed to register webhook at Notabene: {}", getExceptionMessage(e)); + } + + return false; + } + + private String getWebhookUrl() { + String masterExtensionsUrl = configuration.getMasterExtensionsUrl(); + return String.format("%s/notabene/webhooks", masterExtensionsUrl); + } + + /** + * Unregister a webhook. + * + * @param providerCredentials The {@link ITravelRuleProviderCredentials} to unregister the webhook for. + * @return True if the webhook was unregistered, false otherwise. + */ + public boolean unregisterWebhook(ITravelRuleProviderCredentials providerCredentials) { + NotabeneUnregisterWebhookRequest request = new NotabeneUnregisterWebhookRequest(); + request.setVaspDid(providerCredentials.getVaspDid()); + + try { + api.unregisterWebhook(providerCredentials, request); + return true; + } catch (Exception e) { + if (isCausedByHttpNotFound(e)) { + // There was no webhook to unregister, which is ok. + return true; + } + + log.warn("Failed to unregister webhook at Notabene: {}", getExceptionMessage(e)); + } + + return false; + } + + /** + * Tests whether the credentials are valid. The endpoint for obtaining VASPs is used for testing. + * + * @param providerCredentials {@link ITravelRuleProviderCredentials} + * @return {@code True} if credentials are valid, otherwise {@code false}. + */ + public boolean testProviderCredentials(ITravelRuleProviderCredentials providerCredentials) { + try { + api.listVasps(providerCredentials, new NotabeneListVaspsQueryParams()); + return true; + } catch (Exception e) { + log.warn("Notabene credentials test failed: {}", getExceptionMessage(e)); + return false; + } + } + + private String getExceptionMessage(Exception e) { + log.trace("Notabene service failure: ", e); + return e.getCause() == null ? e.getMessage() : e.getCause().getMessage(); + } + + private boolean isCausedByHttpNotFound(Exception e) { + log.trace("Checking if exception is caused by HTTP 404 Not Found: ", e); + return e instanceof NotabeneApiException notabeneApiException && notabeneApiException.getCode() == HttpServletResponse.SC_NOT_FOUND; + } + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneTransferPublisher.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneTransferPublisher.java new file mode 100644 index 000000000..5c6cd6775 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneTransferPublisher.java @@ -0,0 +1,83 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene; + +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferInfo; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +/** + * A publisher for propagating updates to Notabene transfers to registered listeners. + * + *

This class implements a publish-subscribe mechanism, where services interested in receiving + * updates about Notabene transfers can register listeners. When an update occurs, + * all registered listeners are notified.

+ * + * @see NotabeneTransferUpdateListener + */ +@Slf4j +public class NotabeneTransferPublisher { + + // Singleton eager initialization + private static final NotabeneTransferPublisher INSTANCE = new NotabeneTransferPublisher(); + + private NotabeneTransferPublisher() { + // Private constructor to prevent instantiation. + } + + public static NotabeneTransferPublisher getInstance() { + return INSTANCE; + } + + private final Map listeners = new ConcurrentHashMap<>(); + + /** + * Registers a new listener to receive transfer update events. + * + * @param vaspDid DID of the VASP to register the listener for. + * @param listener the {@link NotabeneTransferUpdateListener} to be registered. + */ + public void registerListener(String vaspDid, NotabeneTransferUpdateListener listener) { + listeners.put(vaspDid, listener); + } + + /** + * Unregisters a previously registered listener, stopping it from receiving transfer update events. + * + * @param vaspDid DID of the VASP to unregister a listener for. + */ + public void unregisterListener(String vaspDid) { + listeners.remove(vaspDid); + } + + /** + * Publishes a transfer update event to all registered listeners. + * + * @param transferInfo The updated {@link NotabeneTransferInfo} containing the transfer details. + */ + void publishEvent(NotabeneTransferInfo transferInfo) { + boolean isSingleVasp = Objects.equals(transferInfo.getOriginatorVaspDid(), transferInfo.getBeneficiaryVaspDid()); + if (isSingleVasp) { + publishEventForVasp(transferInfo.getOriginatorVaspDid(), transferInfo, "originator & beneficiary"); + } else { + publishEventForVasp(transferInfo.getOriginatorVaspDid(), transferInfo, "originator"); + publishEventForVasp(transferInfo.getBeneficiaryVaspDid(), transferInfo, "beneficiary"); + } + } + + private void publishEventForVasp(String vaspDid, NotabeneTransferInfo transferInfo, String vaspRole) { + if (vaspDid == null) { + return; + } + + NotabeneTransferUpdateListener listener = listeners.get(vaspDid); + if (listener == null) { + return; + } + + log.debug("Publishing update event of transfer {} for VASP {} ({}).", transferInfo.getTransactionRef(), vaspDid, vaspRole); + listener.onTransferUpdate(transferInfo); + } + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneTransferStatusUpdateListener.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneTransferStatusUpdateListener.java new file mode 100644 index 000000000..145beb607 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneTransferStatusUpdateListener.java @@ -0,0 +1,52 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene; + +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleTransferStatusUpdateEvent; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleTransferUpdateListener; +import com.generalbytes.batm.server.extensions.travelrule.TravelRuleProviderTransferStatus; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferInfo; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferStatus; +import lombok.EqualsAndHashCode; + +/** + * This listener is responsible for mapping transfer status updates received from Notabene and sending them to a designated + * {@link ITravelRuleTransferUpdateListener} for further processing on server side. + */ +@EqualsAndHashCode +public class NotabeneTransferStatusUpdateListener implements NotabeneTransferUpdateListener { + + private final ITravelRuleTransferUpdateListener transferUpdateHandler; + + public NotabeneTransferStatusUpdateListener(ITravelRuleTransferUpdateListener transferUpdateHandler) { + this.transferUpdateHandler = transferUpdateHandler; + } + + @Override + public void onTransferUpdate(NotabeneTransferInfo updatedTransferInfo) { + ITravelRuleTransferStatusUpdateEvent event = mapToTransferStatusUpdateEvent(updatedTransferInfo); + transferUpdateHandler.onTransferStatusUpdate(event); + } + + private ITravelRuleTransferStatusUpdateEvent mapToTransferStatusUpdateEvent(NotabeneTransferInfo updatedTransferInfo) { + TravelRuleProviderTransferStatus status = mapToTravelRuleTransferStatus(updatedTransferInfo.getStatus()); + return new ITravelRuleTransferStatusUpdateEvent() { + @Override + public String getTransferPublicId() { + return updatedTransferInfo.getTransactionRef(); + } + + @Override + public TravelRuleProviderTransferStatus getNewTransferStatus() { + return status; + } + }; + } + + private TravelRuleProviderTransferStatus mapToTravelRuleTransferStatus(NotabeneTransferStatus notabeneStatus) { + return switch (notabeneStatus) { + case ACCEPTED, SAVED -> TravelRuleProviderTransferStatus.APPROVED; + case REJECTED, DECLINED, NOT_READY, CANCELLED -> TravelRuleProviderTransferStatus.REJECTED; + default -> TravelRuleProviderTransferStatus.IN_PROGRESS; + }; + } + +} \ No newline at end of file diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneTransferUpdateListener.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneTransferUpdateListener.java new file mode 100644 index 000000000..edde19ad5 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneTransferUpdateListener.java @@ -0,0 +1,25 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene; + + +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferInfo; + +/** + * Functional interface for listening to updates about Notabene transfers. + * + *

Implementations of this interface can be registered with a {@link NotabeneTransferPublisher} to + * receive notifications whenever a transfer update event occurs.

+ * + * @see NotabeneTransferPublisher + * @see NotabeneTransferInfo + */ +@FunctionalInterface +public interface NotabeneTransferUpdateListener { + + /** + * Invoked when a Notabene transfer update event occurs. + * + * @param updatedTransferInfo A {@link NotabeneTransferInfo} containing details about the updated transfer. + */ + void onTransferUpdate(NotabeneTransferInfo updatedTransferInfo); + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneTravelRuleProvider.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneTravelRuleProvider.java new file mode 100644 index 000000000..403456724 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneTravelRuleProvider.java @@ -0,0 +1,288 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene; + +import com.generalbytes.batm.server.extensions.travelrule.CryptoWalletType; +import com.generalbytes.batm.server.extensions.travelrule.IIdentityWalletEvaluationRequest; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleNaturalPerson; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleProvider; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleProviderCredentials; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleTransferData; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleTransferInfo; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleTransferUpdateListener; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleTransferUpdateRequest; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleVasp; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleWalletInfo; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneAddressOwnershipInfoRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneAddressOwnershipInfoResponse; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneBeneficiary; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneCryptoAddressType; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneNameIdentifier; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneNameIdentifierType; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneNaturalPerson; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneOriginator; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabenePerson; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabenePersonName; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransactionBlockchainInfo; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferCreateRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferInfo; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferStatus; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferUpdateRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneVaspInfoSimple; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +import java.util.ArrayList; +import java.util.List; + +/** + * Travel Rule provider for Notabene. + */ +@Slf4j +@AllArgsConstructor +public class NotabeneTravelRuleProvider implements ITravelRuleProvider { + + public static final String NAME = "Notabene Travel Rule Provider"; + + private final ITravelRuleProviderCredentials credentials; + private final NotabeneConfiguration configuration; + private final NotabeneAuthService notabeneAuthService; + private final NotabeneService notabeneService; + private final NotabeneTransferPublisher notabeneTransferPublisher; + + @Override + public String getName() { + return NAME; + } + + @Override + public ITravelRuleWalletInfo getWalletInfo(IIdentityWalletEvaluationRequest walletEvaluationRequest) { + NotabeneAddressOwnershipInfoRequest request = createNotabeneAddressOwnershipInfoRequest(walletEvaluationRequest); + NotabeneAddressOwnershipInfoResponse ownershipInformation = notabeneService.getAddressOwnershipInformation(credentials, request); + return mapToTravelRuleWalletInfo(ownershipInformation); + } + + @Override + public List getAllVasps() { + List vaspInfoList = notabeneService.getAllVasps(credentials); + return mapToTravelRuleVasps(vaspInfoList); + } + + @Override + public ITravelRuleTransferInfo createTransfer(ITravelRuleTransferData outgoingTransferData) { + NotabeneTransferCreateRequest request = createNotabeneTransferCreateRequest(outgoingTransferData); + NotabeneTransferInfo notabeneTransferInfo = notabeneService.createTransfer(credentials, request); + NotabeneTransferInfo latestTransferInfo = attemptApproveTransfer(outgoingTransferData, notabeneTransferInfo); + if (latestTransferInfo != null) { + return mapToTravelRuleTransferInfo(latestTransferInfo); + } + + return null; + } + + private NotabeneTransferInfo attemptApproveTransfer(ITravelRuleTransferData outgoingTransferData, + NotabeneTransferInfo notabeneTransferInfo) { + if (notabeneTransferInfo == null) { + return null; + } + + if (transferNeedsApproval(notabeneTransferInfo)) { + NotabeneTransferInfo approvedTransferInfo = notabeneService.approveTransfer(credentials, notabeneTransferInfo.getId()); + if (approvedTransferInfo != null) { + log.info("Auto-approved new transfer at Notabene. publicId: {}, externalId: {}", + outgoingTransferData.getPublicId(), notabeneTransferInfo.getId()); + return approvedTransferInfo; + } else { + log.error("Failed to approve new transfer at Notabene. publicId: {}, externalId: {}", + outgoingTransferData.getPublicId(), notabeneTransferInfo.getId()); + } + } + + return notabeneTransferInfo; + } + + private boolean transferNeedsApproval(NotabeneTransferInfo notabeneTransferInfo) { + return configuration.isAutomaticApprovalOfOutgoingTransfersEnabled() + && notabeneTransferInfo.getStatus() == NotabeneTransferStatus.NEW; + } + + @Override + public boolean registerStatusUpdateListener(ITravelRuleTransferUpdateListener listener) { + if (notabeneService.registerWebhook(credentials)) { + NotabeneTransferUpdateListener notabeneListener = new NotabeneTransferStatusUpdateListener(listener); + notabeneTransferPublisher.registerListener(credentials.getVaspDid(), notabeneListener); + return true; + } + return false; + } + + @Override + public boolean unregisterStatusUpdateListener() { + notabeneTransferPublisher.unregisterListener(credentials.getVaspDid()); + return true; + } + + @Override + public ITravelRuleTransferInfo updateTransfer(ITravelRuleTransferUpdateRequest request) { + NotabeneTransferUpdateRequest notabeneRequest = mapToNotabeneUpdateRequest(request); + NotabeneTransferInfo notabeneTransferInfo = notabeneService.updateTransfer(credentials, notabeneRequest); + if (notabeneTransferInfo == null) { + return null; + } + + return mapToTravelRuleTransferInfo(notabeneTransferInfo); + } + + @Override + public void notifyProviderConfigurationChanged() { + notabeneAuthService.removeAccessToken(credentials); + } + + @Override + public boolean testProviderConfiguration() { + log.info("A configuration test was requested for {}, clientId: {}", NAME, credentials.getClientId()); + + return notabeneService.testProviderCredentials(credentials); + } + + private NotabeneTransferUpdateRequest mapToNotabeneUpdateRequest(ITravelRuleTransferUpdateRequest request) { + NotabeneTransferUpdateRequest notabeneRequest = new NotabeneTransferUpdateRequest(); + notabeneRequest.setId(request.getId()); + notabeneRequest.setTxHash(request.getTransactionHash()); + return notabeneRequest; + } + + private ITravelRuleTransferInfo mapToTravelRuleTransferInfo(NotabeneTransferInfo notabeneTransferInfo) { + return notabeneTransferInfo::getId; + } + + private NotabeneTransferCreateRequest createNotabeneTransferCreateRequest(ITravelRuleTransferData outgoingTransferData) { + NotabeneTransferCreateRequest request = new NotabeneTransferCreateRequest(); + request.setTransactionRef(outgoingTransferData.getPublicId()); + request.setTransactionAsset(outgoingTransferData.getTransactionAsset()); + request.setTransactionAmount(String.valueOf(outgoingTransferData.getTransactionAmount())); + request.setTransactionBlockchainInfo(createBlockchainInfo(outgoingTransferData)); + request.setOriginatorVaspDid(outgoingTransferData.getOriginatorVasp().getDid()); + request.setBeneficiaryVaspDid(outgoingTransferData.getBeneficiaryVasp().getDid()); + request.setOriginator(createOriginator(outgoingTransferData.getOriginator())); + request.setBeneficiary(createBeneficiary(outgoingTransferData.getBeneficiary())); + return request; + } + + private NotabeneBeneficiary createBeneficiary(ITravelRuleNaturalPerson beneficiaryNaturalPerson) { + NotabenePerson person = createPerson(beneficiaryNaturalPerson); + NotabeneBeneficiary beneficiary = new NotabeneBeneficiary(); + beneficiary.setAccountNumber(List.of(beneficiaryNaturalPerson.getIdentityPublicId())); + beneficiary.setBeneficiaryPersons(List.of(person)); + return beneficiary; + } + + private NotabeneOriginator createOriginator(ITravelRuleNaturalPerson originatorNaturalPerson) { + NotabenePerson person = createPerson(originatorNaturalPerson); + NotabeneOriginator originator = new NotabeneOriginator(); + originator.setAccountNumber(List.of(originatorNaturalPerson.getIdentityPublicId())); + originator.setOriginatorPersons(List.of(person)); + return originator; + } + + private NotabenePerson createPerson(ITravelRuleNaturalPerson travelRuleNaturalPerson) { + NotabeneNameIdentifier notabenePersonNameIdentifier = new NotabeneNameIdentifier(); + notabenePersonNameIdentifier.setPrimaryIdentifier(travelRuleNaturalPerson.getName().getPrimaryName()); + notabenePersonNameIdentifier.setSecondaryIdentifier(travelRuleNaturalPerson.getName().getSecondaryName()); + notabenePersonNameIdentifier.setNameIdentifierType(getNameIdentifierType(travelRuleNaturalPerson)); + + NotabenePersonName personName = new NotabenePersonName(); + personName.setNameIdentifier(List.of(notabenePersonNameIdentifier)); + + NotabeneNaturalPerson naturalPerson = new NotabeneNaturalPerson(); + naturalPerson.setName(List.of(personName)); + naturalPerson.setCustomerIdentification(travelRuleNaturalPerson.getIdentityPublicId()); + + NotabenePerson person = new NotabenePerson(); + person.setNaturalPerson(naturalPerson); + return person; + } + + private NotabeneNameIdentifierType getNameIdentifierType(ITravelRuleNaturalPerson travelRuleNaturalPerson) { + try { + if (travelRuleNaturalPerson.getName().getNameType() != null) { + return NotabeneNameIdentifierType.valueOf(travelRuleNaturalPerson.getName().getNameType()); + } + } catch (IllegalArgumentException e) { + // ignore + } + return null; + } + + private NotabeneTransactionBlockchainInfo createBlockchainInfo(ITravelRuleTransferData outgoingTransferData) { + NotabeneTransactionBlockchainInfo blockchainInfo = new NotabeneTransactionBlockchainInfo(); + blockchainInfo.setDestination(outgoingTransferData.getDestinationAddress()); + blockchainInfo.setTxHash(outgoingTransferData.getTransactionHash()); + return blockchainInfo; + } + + private List mapToTravelRuleVasps(List vaspInfoList) { + List vasps = new ArrayList<>(vaspInfoList.size()); + for (NotabeneVaspInfoSimple vaspInfo : vaspInfoList) { + vasps.add(initializeITravelRuleVasp(vaspInfo)); + } + return vasps; + } + + private ITravelRuleVasp initializeITravelRuleVasp(NotabeneVaspInfoSimple vaspInfo) { + return new ITravelRuleVasp() { + @Override + public String getDid() { + return vaspInfo.getDid(); + } + + @Override + public String getName() { + return vaspInfo.getName(); + } + }; + } + + private NotabeneAddressOwnershipInfoRequest createNotabeneAddressOwnershipInfoRequest(IIdentityWalletEvaluationRequest walletEvaluationRequest) { + NotabeneAddressOwnershipInfoRequest request = new NotabeneAddressOwnershipInfoRequest(); + request.setVaspDid(credentials.getVaspDid()); + request.setAddress(walletEvaluationRequest.getCryptoAddress()); + request.setAsset(walletEvaluationRequest.getCryptocurrency()); + return request; + } + + private ITravelRuleWalletInfo mapToTravelRuleWalletInfo(NotabeneAddressOwnershipInfoResponse ownershipInformation) { + if (ownershipInformation == null) { + return new TravelRuleWalletInfo(CryptoWalletType.UNKNOWN); + } + CryptoWalletType walletType = mapToCryptoWalletType(ownershipInformation.getAddressType()); + return new TravelRuleWalletInfo(walletType, ownershipInformation.getOwnerVaspDid()); + } + + private CryptoWalletType mapToCryptoWalletType(NotabeneCryptoAddressType cryptoAddressType) { + if (cryptoAddressType == null) { + return CryptoWalletType.UNKNOWN; + } + return switch (cryptoAddressType) { + case HOSTED -> CryptoWalletType.CUSTODIAL; + case UNHOSTED -> CryptoWalletType.UNHOSTED; + case UNKNOWN -> CryptoWalletType.UNKNOWN; + }; + } + + @Getter + private static class TravelRuleWalletInfo implements ITravelRuleWalletInfo { + private final CryptoWalletType cryptoWalletType; + private final String ownerVaspDid; + + public TravelRuleWalletInfo(CryptoWalletType cryptoWalletType) { + this.cryptoWalletType = cryptoWalletType; + this.ownerVaspDid = null; + } + + public TravelRuleWalletInfo(CryptoWalletType cryptoWalletType, String ownerVaspDid) { + this.cryptoWalletType = cryptoWalletType; + this.ownerVaspDid = ownerVaspDid; + } + } + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneWebhookRestService.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneWebhookRestService.java new file mode 100644 index 000000000..cc6a4da65 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneWebhookRestService.java @@ -0,0 +1,46 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene; + +import com.generalbytes.batm.server.extensions.IRestService; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneWebhookMessage; +import lombok.extern.slf4j.Slf4j; + +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.core.MediaType; + +/** + * Servlet responsible for handling incoming webhook messages from Notabene. + * + * @see Notabene Documentation + * @see NotabeneWebhookMessage + */ +@Path("/") +@Consumes(MediaType.APPLICATION_JSON) +@Slf4j +public class NotabeneWebhookRestService implements IRestService { + + private final NotabeneTransferPublisher transferPublisher; + + public NotabeneWebhookRestService() { + this.transferPublisher = NotabeneTransferPublisher.getInstance(); + } + + @POST + @Path("/webhooks") + public void handleWebhookMessage(NotabeneWebhookMessage message) { + if (NotabeneWebhookMessage.TYPE_TRANSACTION_UPDATED.equals(message.getMessage())) { + transferPublisher.publishEvent(message.getPayload().getTransaction()); + } + } + + @Override + public String getPrefixPath() { + return "notabene"; + } + + @Override + public Class getImplementation() { + return getClass(); + } +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/api/NotabeneApi.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/api/NotabeneApi.java new file mode 100644 index 000000000..e88fc17ed --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/api/NotabeneApi.java @@ -0,0 +1,158 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.api; + +import com.fasterxml.jackson.core.JsonParseException; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneAddressOwnershipInfoResponse; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneFullyValidateTransferResponse; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneListVaspsQueryParams; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneListVaspsResponse; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneRegisterWebhookRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferCreateRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferInfo; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferStatus; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferUpdateRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneUnregisterWebhookRequest; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; + +/** + * Provides access to relevant Notabene endpoints. + * + * @see Notabene Documentation + */ +@Path("/") +@Produces(MediaType.APPLICATION_JSON) +public interface NotabeneApi { + + String AUTHORIZATION_HEADER_NAME = "Authorization"; + + /** + * Returns a list of VASPs. VASPs can be searched and sorted and results are paginated. + * + * @param authorization The Authorization header content. + * @param query The query filter for VASPs. + * @param returnAllRecords True to return all records at once, false to use pagination. + * @return The response. + * @see Notabene Documentation + * @see NotabeneListVaspsQueryParams + * @see NotabeneListVaspsResponse + */ + @GET + @Path("/tf/simple/vasps") + NotabeneListVaspsResponse listVasps(@HeaderParam(AUTHORIZATION_HEADER_NAME) String authorization, + @QueryParam("q") String query, + @QueryParam("all") Boolean returnAllRecords) throws NotabeneApiException; + + /** + * Fully validate a transfer. + * + * @param request The request. + * @return The response. + * @see Notabene Documentation + * @see NotabeneTransferCreateRequest + * @see NotabeneFullyValidateTransferResponse + */ + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Path("/tx/validate/full") + NotabeneFullyValidateTransferResponse validateFull(@HeaderParam(AUTHORIZATION_HEADER_NAME) String authorization, + NotabeneTransferCreateRequest request) throws NotabeneApiException; + + /** + * Creates a new transfer. The fields required in a transfer differ depending on the jurisdiction of the originating VASP. + * Additional data may be provided to the beneficiary VASP depending on their jurisdiction. + * + * @param request The request. + * @return The response. + * @see Notabene Documentation + * @see NotabeneTransferCreateRequest + * @see NotabeneTransferInfo + */ + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Path("/tx/create") + NotabeneTransferInfo createTransfer(@HeaderParam(AUTHORIZATION_HEADER_NAME) String authorization, + NotabeneTransferCreateRequest request) throws NotabeneApiException; + + /** + * Update a transfer with the passed parameters. + * + * @param request The request. + * @return The response. + * @see Notabene Documentation + * @see NotabeneTransferUpdateRequest + * @see NotabeneTransferInfo + */ + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Path("/tx/update") + NotabeneTransferInfo updateTransfer(@HeaderParam(AUTHORIZATION_HEADER_NAME) String authorization, + NotabeneTransferUpdateRequest request) throws NotabeneApiException; + + /** + * Approves an outgoing transfer. If the VASP is present in the Notabene Directory, + * approving a transfer will send the transfer to them and set the transfer status + * to {@link NotabeneTransferStatus#SENT}. If the VASP is not in the Notabene Directory, + * approving the transfer will set the status to {@link NotabeneTransferStatus#WAITING_FOR_INFORMATION}. + * + * @param transferId Identifier of the transfer to approve. + * @return The response. + * @see Notabene Documentation + */ + @POST + @Path("/tx/approve") + NotabeneTransferInfo approveTransfer(@HeaderParam(AUTHORIZATION_HEADER_NAME) String authorization, + @QueryParam("id") String transferId) throws NotabeneApiException; + + /** + * Get the ownership information about a customer blockchain address. + * + * @param address The blockchain address. + * @param vaspDid VASP did. + * @param asset Cryptocurrency. + * @return The response. + * @see Notabene Documentation + * @see NotabeneAddressOwnershipInfoResponse + */ + @GET + @Path("/v1/addresses/address-ownerships/{address}") + NotabeneAddressOwnershipInfoResponse getAddressOwnershipInformation(@HeaderParam(AUTHORIZATION_HEADER_NAME) String authorization, + @PathParam("address") String address, + @QueryParam("vasp_did") String vaspDid, + @QueryParam("asset") String asset) throws NotabeneApiException; + + /** + * Register the multi-message Webhook URL for a given VASP. + * + * @param request The request. + * @see Notabene Documentation + * @see NotabeneRegisterWebhookRequest + */ + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Path("/webhook") + void registerWebhook(@HeaderParam(AUTHORIZATION_HEADER_NAME) String authorization, + NotabeneRegisterWebhookRequest request) throws NotabeneApiException, JsonParseException; + + /** + * Unregister the multi-message Webhook URL for a given VASP. + * + * @param request The request. + * @see Notabene Documentation + * @see NotabeneUnregisterWebhookRequest + */ + @DELETE + @Consumes(MediaType.APPLICATION_JSON) + @Path("/webhook") + void unregisterWebhook(@HeaderParam(AUTHORIZATION_HEADER_NAME) String authorization, + NotabeneUnregisterWebhookRequest request) throws NotabeneApiException, JsonParseException; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/api/NotabeneApiCall.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/api/NotabeneApiCall.java new file mode 100644 index 000000000..56242c53f --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/api/NotabeneApiCall.java @@ -0,0 +1,19 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.api; + +/** + * Represents a call to Notabene API. + * + * @param Type of the expected response. + */ +@FunctionalInterface +public interface NotabeneApiCall { + + /** + * Make a call to Notabene API. + * + * @param authorization Authorization header content. + * @return The response from the call. + */ + T execute(String authorization); + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/api/NotabeneApiException.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/api/NotabeneApiException.java new file mode 100644 index 000000000..1faed0502 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/api/NotabeneApiException.java @@ -0,0 +1,38 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.api; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneApiError; +import lombok.Getter; + +/** + * Custom exception thrown when a Notabene API call fails + * and the API provides a detailed error description. + */ +@Getter +@JsonIgnoreProperties(ignoreUnknown = true) +public class NotabeneApiException extends RuntimeException { + + /** + * The name of the error. + */ + private final String name; + /** + * The HTTP status code. + */ + private final int code; + /** + * The error stack message. + */ + private final String stack; + + @JsonCreator + public NotabeneApiException(@JsonProperty("err") NotabeneApiError notabeneError) { + super(notabeneError.getMessage()); + this.name = notabeneError.getName(); + this.code = notabeneError.getCode(); + this.stack = notabeneError.getStack(); + } + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/api/NotabeneAuthApi.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/api/NotabeneAuthApi.java new file mode 100644 index 000000000..bbfa07b04 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/api/NotabeneAuthApi.java @@ -0,0 +1,37 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.api; + +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneGenerateAccessTokenRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneGenerateAccessTokenResponse; +import si.mazi.rescu.HttpStatusIOException; + +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +/** + * Provides access to Notabene Authentication endpoints. + * + * @see Notabene Documentation + */ +@Path("/") +@Produces(MediaType.APPLICATION_JSON) +public interface NotabeneAuthApi { + + /** + * Generate a new access token. + * + * @param request The request. + * @return The response. + * @throws HttpStatusIOException If the request fails. + * @see Notabene Documentation + * @see NotabeneGenerateAccessTokenRequest + * @see NotabeneGenerateAccessTokenResponse + */ + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Path("/oauth/token") + NotabeneGenerateAccessTokenResponse generateAccessToken(NotabeneGenerateAccessTokenRequest request) throws HttpStatusIOException; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneAddressOwnershipInfoRequest.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneAddressOwnershipInfoRequest.java new file mode 100644 index 000000000..5ddc28333 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneAddressOwnershipInfoRequest.java @@ -0,0 +1,21 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Request to get ownership information about a customer blockchain address. + * + * @see Notabene Documentation + */ +@Getter +@Setter +@NoArgsConstructor +public class NotabeneAddressOwnershipInfoRequest { + + private String address; + private String vaspDid; + private String asset; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneAddressOwnershipInfoResponse.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneAddressOwnershipInfoResponse.java new file mode 100644 index 000000000..84052cb7c --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneAddressOwnershipInfoResponse.java @@ -0,0 +1,25 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Response with ownership information about a customer blockchain address. + * + * @see Notabene Documentation + */ +@Getter +@Setter +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class NotabeneAddressOwnershipInfoResponse { + + @JsonProperty("owner_vasp_did") + private String ownerVaspDid; + @JsonProperty("address_type") + private NotabeneCryptoAddressType addressType; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneApiError.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneApiError.java new file mode 100644 index 000000000..91a4eabcd --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneApiError.java @@ -0,0 +1,34 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * General Notabene Api error response. + */ +@Getter +@Setter +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class NotabeneApiError { + + /** + * The name of the error. + */ + private String name; + /** + * The HTTP status code. + */ + private int code; + /** + * The error message. + */ + private String message; + /** + * The error stack message. + */ + private String stack; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneApiPagination.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneApiPagination.java new file mode 100644 index 000000000..690ccec61 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneApiPagination.java @@ -0,0 +1,45 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Holds information about Notabene Api pagination. + * + * @see NotabeneListVaspsResponse + */ +@Getter +@Setter +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class NotabeneApiPagination { + + /** + * Current page number. Starts at zero. + */ + private int page; + /** + * Records per page. + */ + @JsonProperty("per_page") + private int pageSize; + /** + * Field to order by. + * + *

Order direction is specified by leading 'ASC' or 'DESC'. Examples:

+ *
    + *
  • {@code "name:ASC"} -> Order by name in ascending direction.
  • + *
  • {@code "name:DESC"} -> Order by name in descending direction.
  • + *
+ */ + @JsonProperty("order") + private String orderBy; + /** + * Total records. + */ + private int total; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneBeneficiary.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneBeneficiary.java new file mode 100644 index 000000000..61a4a564b --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneBeneficiary.java @@ -0,0 +1,33 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +/** + * The beneficiary is defined as the natural or legal person or legal arrangement + * who is identified by the originator as the receiver of the requested VA transfer. + */ +@Getter +@Setter +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_DEFAULT) +public class NotabeneBeneficiary { + + /** + * The natural or legal person or legal arrangement who is identified by the originator + * as the receiver of the requested VA transfer. + * + * @see NotabenePerson + */ + private List beneficiaryPersons; + /** + * Identifier of an account that is used to process the transfer. + * The value for this element is case-sensitive. + */ + private List accountNumber; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneCryptoAddressType.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneCryptoAddressType.java new file mode 100644 index 000000000..871ee57ea --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneCryptoAddressType.java @@ -0,0 +1,19 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +/** + * Represents the type of crypto address. + */ +public enum NotabeneCryptoAddressType { + /** + * Address is not known. + */ + UNKNOWN, + /** + * Address is hosted by a custodial service. + */ + HOSTED, + /** + * Address is unhosted. + */ + UNHOSTED +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneDateAndPlaceOfBirth.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneDateAndPlaceOfBirth.java new file mode 100644 index 000000000..b6b3dd38d --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneDateAndPlaceOfBirth.java @@ -0,0 +1,22 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Holds the date and place of birth. + */ +@Getter +@Setter +@NoArgsConstructor +public class NotabeneDateAndPlaceOfBirth { + + /** + * A point in time, represented as a day within the calendar year. + * Compliant with ISO 8601. (YYYY-MM-DD) + */ + private String dateOfBirth; + private String placeOfBirth; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneFullyValidateTransferResponse.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneFullyValidateTransferResponse.java new file mode 100644 index 000000000..ef73af8be --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneFullyValidateTransferResponse.java @@ -0,0 +1,44 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +import java.util.List; + +/** + * Response from fully validating a transfer. + * + * @see Notabene Documentation + */ +@Getter +@Setter +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +@ToString +public class NotabeneFullyValidateTransferResponse { + + /** + * Is the transfer valid? + */ + private boolean isValid; + /** + * Type of the transfer. + */ + private NotabeneTransferType type; + /** + * Type of the Beneficiary's Blockchain Address. + */ + private NotabeneCryptoAddressType beneficiaryAddressType; + /** + * Name of the Beneficiary's VASP. + */ + @JsonProperty("beneficiaryVASPname") + private String beneficiaryVaspName; + private List errors; + private List warnings; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneGenerateAccessTokenRequest.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneGenerateAccessTokenRequest.java new file mode 100644 index 000000000..a98fe657f --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneGenerateAccessTokenRequest.java @@ -0,0 +1,31 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Request to generate a new access token. + * + * @see Notabene Documentation + * @see NotabeneGenerateAccessTokenResponse + */ +@Getter +@Setter +@NoArgsConstructor +public class NotabeneGenerateAccessTokenRequest { + + @JsonProperty("client_id") + private String clientId; + @JsonProperty("client_secret") + private String clientSecret; + @JsonProperty("grant_type") + private String grantType; + /** + * {@code https://api.notabene.id} (Production) or {@code https://api.notabene.dev} (Testing) + */ + @JsonProperty("audience") + private String audience; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneGenerateAccessTokenResponse.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneGenerateAccessTokenResponse.java new file mode 100644 index 000000000..020b5f71e --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneGenerateAccessTokenResponse.java @@ -0,0 +1,26 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Response to generating a new access token. + * + * @see Notabene Documentation + * @see NotabeneGenerateAccessTokenRequest + */ +@Getter +@Setter +@NoArgsConstructor +public class NotabeneGenerateAccessTokenResponse { + + @JsonProperty("access_token") + private String accessToken; + @JsonProperty("expires_in") + private int expiresInMillis; + @JsonProperty("token_type") + private String tokenType; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneGeographicAddress.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneGeographicAddress.java new file mode 100644 index 000000000..69f5b2dac --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneGeographicAddress.java @@ -0,0 +1,48 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +/** + * The particulars of a location at which a person may be communicated with. + */ +@Getter +@Setter +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_DEFAULT) +public class NotabeneGeographicAddress { + + /** + * Identifies the nature of the address. + */ + private NotabeneGeographicAddressType addressType; + private String department; + private String subDepartment; + private String streetName; + private String buildingNumber; + private String buildingName; + private String floor; + private String postBox; + private String room; + private String postCode; + private String townName; + private String townLocationName; + private String districtName; + private String countrySubDivision; + /** + * Information that locates and identifies a specific address, + * as defined by postal services, presented in free format text. + */ + private List addressLine; + /** + * Two alphabetic characters representing an ISO-3166 Alpha-2 country, + * including the code ‘XX’ to represent an indicator for unknown States, + * other entities or organisations + */ + private String country; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneGeographicAddressType.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneGeographicAddressType.java new file mode 100644 index 000000000..04d070a94 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneGeographicAddressType.java @@ -0,0 +1,20 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +/** + * Identifies the nature of the address. + */ +public enum NotabeneGeographicAddressType { + /** + * Residential - Address is the home address. + */ + HOME, + /** + * Business - Address is the business address. + */ + BIZZ, + /** + * Geographic - Address is the unspecified physical (geographical) address + * suitable for identification of the natural or legal person. + */ + GEOG +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneListVaspsQueryParams.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneListVaspsQueryParams.java new file mode 100644 index 000000000..44a7deb96 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneListVaspsQueryParams.java @@ -0,0 +1,32 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import lombok.Getter; + +/** + * Holds Query parameters for the List VASPs endpoint. + * + * @see Notabene Documentation + */ +@Getter +public class NotabeneListVaspsQueryParams { + + /** + * String to query. + */ + private final String query; + /** + * True to return all found records at once. False to page the response. + */ + private final Boolean all; + + public NotabeneListVaspsQueryParams() { + this.query = null; + this.all = true; + } + + public NotabeneListVaspsQueryParams(String query) { + this.query = query; + this.all = true; + } + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneListVaspsResponse.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneListVaspsResponse.java new file mode 100644 index 000000000..a6650a067 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneListVaspsResponse.java @@ -0,0 +1,35 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +/** + * Response of listing VASPs. + * + * @see Notabene Documentation + * @see NotabeneVaspInfoSimple + */ +@Getter +@Setter +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class NotabeneListVaspsResponse { + + /** + * The retrieved VASPs. + * + * @see NotabeneVaspInfoSimple + */ + private List vasps; + /** + * Information about endpoint pagination. + * + * @see NotabeneApiPagination + */ + private NotabeneApiPagination pagination; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneNameIdentifier.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneNameIdentifier.java new file mode 100644 index 000000000..b5397317e --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneNameIdentifier.java @@ -0,0 +1,24 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Full name separated into primary and secondary identifier. + */ +@Getter +@Setter +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_DEFAULT) +public class NotabeneNameIdentifier { + + private String primaryIdentifier; + private String secondaryIdentifier; + /** + * A single value corresponding to the nature of name being adopted. + */ + private NotabeneNameIdentifierType nameIdentifierType; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneNameIdentifierType.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneNameIdentifierType.java new file mode 100644 index 000000000..15ff034d6 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneNameIdentifierType.java @@ -0,0 +1,28 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +/** + * Represents the nature of name being adopted. + */ +public enum NotabeneNameIdentifierType { + /** + * Alias name - A name other than the legal name by which a natural person is also known. + */ + ALIA, + /** + * Name at birth - The name given to a natural person at birth. + */ + BIRT, + /** + * Maiden name - The original name of a natural person who has changed their name after marriage. + */ + MAID, + /** + * Legal name - The name that identifies a natural person for legal, official or administrative purposes. + */ + LEGL, + /** + * Unspecified - A name by which a natural person may be known but which cannot otherwise be categorized + * or the category of which the sender is unable to determine. + */ + MISC +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneNationalIdentification.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneNationalIdentification.java new file mode 100644 index 000000000..f802d8daf --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneNationalIdentification.java @@ -0,0 +1,24 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_DEFAULT) +public class NotabeneNationalIdentification { + + private String nationalIdentifier; + private NotabeneNationalIdentifierType nationalIdentifierType; + /** + * Two alphabetic characters representing an ISO-3166 Alpha-2 country, + * including the code ‘XX’ to represent an indicator for unknown States, + * other entities or organisations + */ + private String countryOfIssue; + private String registrationAuthority; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneNationalIdentifierType.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneNationalIdentifierType.java new file mode 100644 index 000000000..d24ceb497 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneNationalIdentifierType.java @@ -0,0 +1,48 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +/** + * Identifies the national identification type. + */ +public enum NotabeneNationalIdentifierType { + /** + * Alien registration number - Number assigned by a government agency to identify foreign nationals. + */ + ARNU, + /** + * Passport number - Number assigned by a passport authority. + */ + CCPT, + /** + * Registration authority identifier - Identifier of a legal entity as maintained by a registration authority. + */ + RAID, + /** + * Driver license number - Number assigned to a driver's license. + */ + DRLC, + /** + * Foreign investment identity number - Number assigned to a foreign investor (other than the alien number). + */ + FIIN, + /** + * Tax identification number - Number assigned by a tax authority to an entity. + */ + TXID, + /** + * Social security number - Number assigned by a social security agency. + */ + SOCS, + /** + * Identity card number - Number assigned by a national authority to an identity card. + */ + IDCD, + /** + * Legal Entity Identifier - Legal Entity Identifier (LEI) assigned in accordance with ISO 17442 11 . + */ + LEIX, + /** + * Unspecified - A national identifier which may be known but which cannot otherwise be categorized + * or the category of which the sender is unable to determine. + */ + MISC +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneNaturalPerson.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneNaturalPerson.java new file mode 100644 index 000000000..1ba680c7a --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneNaturalPerson.java @@ -0,0 +1,31 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +/** + * Represents a natural (non-legal) person. + */ +@Getter +@Setter +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_DEFAULT) +public class NotabeneNaturalPerson { + + private List name; + private List geographicAddress; + private NotabeneNationalIdentification nationalIdentification; + private String customerIdentification; + private NotabeneDateAndPlaceOfBirth dateAndPlaceOfBirth; + /** + * Two alphabetic characters representing an ISO-3166 Alpha-2 country, + * including the code ‘XX’ to represent an indicator for unknown States, + * other entities or organisations + */ + private String countryOfResidence; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneOriginator.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneOriginator.java new file mode 100644 index 000000000..3768d4cda --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneOriginator.java @@ -0,0 +1,34 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +/** + * The originator is defined as the account holder who allows the VA transfer from that account or, + * where there is no account, the natural or legal person that places the order with the originating VASP + * to perform the VA transfer. + */ +@Getter +@Setter +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_DEFAULT) +public class NotabeneOriginator { + + /** + * The account holder who allows the VA transfer from that account or, where there is no account, + * the natural or legal person that places the order with the originating VASP to perform the VA transfer. + * + * @see NotabenePerson + */ + private List originatorPersons; + /** + * Identifier of an account that is used to process the transfer. + * The value for this element is case-sensitive. + */ + private List accountNumber; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabenePerson.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabenePerson.java new file mode 100644 index 000000000..588b7b809 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabenePerson.java @@ -0,0 +1,19 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Represents the definition of an individual person. + */ +@Getter +@Setter +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_DEFAULT) +public class NotabenePerson { + + private NotabeneNaturalPerson naturalPerson; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabenePersonName.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabenePersonName.java new file mode 100644 index 000000000..ddc301fda --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabenePersonName.java @@ -0,0 +1,23 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +/** + * Holds name identifiers for a person. + * + * @see NotabenePerson + */ +@Getter +@Setter +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_DEFAULT) +public class NotabenePersonName { + + private List nameIdentifier; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneProof.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneProof.java new file mode 100644 index 000000000..bc6bc9cba --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneProof.java @@ -0,0 +1,26 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Wallet Ownership Proof data. + */ +@Getter +@Setter +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_DEFAULT) +public class NotabeneProof { + + /** + * The proof. + */ + private String proof; + /** + * Type of the proof. + */ + private String type; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneRegisterWebhookRequest.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneRegisterWebhookRequest.java new file mode 100644 index 000000000..21713db2e --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneRegisterWebhookRequest.java @@ -0,0 +1,24 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Request to register the multi-message Webhook URL for a given VASP. + * + * @see Notabene Documentation + */ +@Getter +@Setter +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_DEFAULT) +public class NotabeneRegisterWebhookRequest { + + @JsonProperty("vaspDID") + private String vaspDid; + private String url; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneTransactionBlockchainInfo.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneTransactionBlockchainInfo.java new file mode 100644 index 000000000..d9590a6fe --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneTransactionBlockchainInfo.java @@ -0,0 +1,30 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Information about a transaction on the blockchain + */ +@Getter +@Setter +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_DEFAULT) +public class NotabeneTransactionBlockchainInfo { + + /** + * Transaction txHash. + */ + private String txHash; + /** + * Originator crypto address. + */ + private String origin; + /** + * Destination crypto address. + */ + private String destination; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneTransferCreateRequest.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneTransferCreateRequest.java new file mode 100644 index 000000000..b7052a605 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneTransferCreateRequest.java @@ -0,0 +1,62 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Request to create a new transfer. + * + *

This is also used to validate a transfer before creating it.

+ * + * @see Notabene Create Documentation + * @see Notabene Validate Documentation + * @see NotabeneTransferInfo + * @see NotabeneFullyValidateTransferResponse + */ +@Getter +@Setter +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_DEFAULT) +public class NotabeneTransferCreateRequest { + + /** + * Idempotency key: to prevent duplicate transactions. + * It is highly recommended to populate this field with a unique identifier for the transfer. + */ + private String transactionRef; + /** + * The type of the asset as Asset Symbol (BTC, ETH) + */ + private String transactionAsset; + /** + * Amount in base unit of the asset (satoshi, wei, etc.) + */ + private String transactionAmount; + /** + * Originator VASP Decentralized Identifier + * + * @see Notabene Documentation + */ + @JsonProperty("originatorVASPdid") + private String originatorVaspDid; + /** + * Beneficiary VASP Decentralized Identifier + * + * @see Notabene Documentation + */ + @JsonProperty("beneficiaryVASPdid") + private String beneficiaryVaspDid; + /** + * Information about the transaction on the blockchain + * + * @see NotabeneTransactionBlockchainInfo + */ + private NotabeneTransactionBlockchainInfo transactionBlockchainInfo; + private NotabeneOriginator originator; + private NotabeneBeneficiary beneficiary; + private NotabeneProof beneficiaryProof; + +} \ No newline at end of file diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneTransferInfo.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneTransferInfo.java new file mode 100644 index 000000000..25300d167 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneTransferInfo.java @@ -0,0 +1,83 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Holds information about a transfer. + * + *

This is a part of the response by several endpoints + * and webhook payloads.

+ */ +@Getter +@Setter +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class NotabeneTransferInfo { + + /** + * Identifier of the Transfer, generated by Notabene. + */ + private String id; + /** + * Identifier of the Transfer, provided by server at create. + */ + private String transactionRef; + /** + * Status of the Transfer. + * + * @see NotabeneTransferStatus + */ + private NotabeneTransferStatus status; + /** + * Type of the Transfer. + * + * @see NotabeneTransferType + */ + private NotabeneTransferType transactionType; + /** + * The type of the asset as Asset Symbol (BTC, ETH) + */ + private String transactionAsset; + /** + * Amount in base unit of the asset (satoshi, wei, etc.) + */ + private String transactionAmount; + private int chargedQuantity; + /** + * Originator Decentralized Identifier. + * + * @see Notabene Documentation + */ + private String originatorDid; + /** + * Beneficiary Decentralized Identifier. + * + * @see Notabene Documentation + */ + private String beneficiaryDid; + /** + * Originator VASP Decentralized Identifier + * + * @see Notabene Documentation + */ + @JsonProperty("originatorVASPdid") + private String originatorVaspDid; + /** + * Beneficiary VASP Decentralized Identifier + * + * @see Notabene Documentation + */ + @JsonProperty("beneficiaryVASPdid") + private String beneficiaryVaspDid; + /** + * Information about the transaction on the blockchain. + * + * @see NotabeneTransactionBlockchainInfo + */ + private NotabeneTransactionBlockchainInfo transactionBlockchainInfo; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneTransferStatus.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneTransferStatus.java new file mode 100644 index 000000000..aed4c3538 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneTransferStatus.java @@ -0,0 +1,61 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +/** + * Status of a Transfer. + * + * @see Notabene Documentation + */ +public enum NotabeneTransferStatus { + /** + * Message has been created and is ready to be sent. + */ + NEW, + /** + * Message is missing information about the beneficiary (only if txCreate is called + * with the option skipBeneficiaryDataValidation=true). + */ + MISSING_BENEFICIARY_DATA, + /** + * Missing contact details to reach the beneficiary VASP. Notabene to action. + */ + WAITING_FOR_INFORMATION, + /** + * Message has been automatically or manually cancelled. + */ + CANCELLED, + /** + * Message has been created with txNotify and is missing both originator and beneficiary information. + * + *

Transfer created only with Blockchain Data (called by Beneficiary).

+ */ + INCOMPLETE, + /** + * Message has been sent to the beneficiary VASP. + */ + SENT, + /** + * Beneficiary VASP has confirmed they control the destination address. + */ + ACK, + /** + * Beneficiary VASP accepted your value transfer request. + */ + ACCEPTED, + /** + * Beneficiary VASP declined your value transfer request. + */ + DECLINED, + /** + * Beneficiary VASP does not control the destination address. + */ + REJECTED, + /** + * Beneficiary VASP is not ready to respond to travel rule messages. + */ + NOT_READY, + /** + * The transaction is "BELOW_THRESHOLD," "NON_CUSTODIAL" or an internal transfer. + * Therefore, after being created, it is not transmitted, just saved. + */ + SAVED +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneTransferType.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneTransferType.java new file mode 100644 index 000000000..bf4a02144 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneTransferType.java @@ -0,0 +1,23 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +/** + * Holds all possible types of a transfer. + */ +public enum NotabeneTransferType { + /** + * Transfer is below all threshold. + */ + BELOW_THRESHOLD, + /** + * Transfer is on a customer owned wallet. Proof required. + */ + NON_CUSTODIAL, + /** + * Transfer is between VASPs. TravelRule transfer created. + */ + TRAVELRULE, + /** + * Transfer with any missing information. + */ + UNKNOWN +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneTransferUpdateRequest.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneTransferUpdateRequest.java new file mode 100644 index 000000000..ec582a9cb --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneTransferUpdateRequest.java @@ -0,0 +1,29 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Request to update an existing transfer. + * + * @see Notabene Documentation + * @see NotabeneTransferInfo + */ +@Getter +@Setter +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_DEFAULT) +public class NotabeneTransferUpdateRequest { + + /** + * Identifier of the transfer. Returned by Notabene on create. + */ + private String id; + /** + * The blockchain transaction hash. + */ + private String txHash; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneUnregisterWebhookRequest.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneUnregisterWebhookRequest.java new file mode 100644 index 000000000..589c1adb6 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneUnregisterWebhookRequest.java @@ -0,0 +1,23 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Request to unregister the multi-message Webhook URL for a given VASP. + * + * @see Notabene Documentation + */ +@Getter +@Setter +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_DEFAULT) +public class NotabeneUnregisterWebhookRequest { + + @JsonProperty("vaspDID") + private String vaspDid; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneVaspInfoSimple.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneVaspInfoSimple.java new file mode 100644 index 000000000..22861bbf6 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneVaspInfoSimple.java @@ -0,0 +1,34 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Holds information about a single VASP. + */ +@Getter +@Setter +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class NotabeneVaspInfoSimple { + + /** + * Decentralized Identifier + * + * @see Notabene Documentation + */ + private String did; + private String name; + private String website; + private String logo; + private String incorporationCountry; + private String documents; + private boolean hasAdmin; + private boolean isNotifiable; + private boolean isActiveSender; + private boolean isActiveReceiver; + private String issuers; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneWebhookMessage.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneWebhookMessage.java new file mode 100644 index 000000000..54be5c8f4 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneWebhookMessage.java @@ -0,0 +1,44 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Represents a message sent to a webhook by Notabene. + * + * @see Notabene Documentation + * @see NotabeneWebhookMessagePayload + */ +@Getter +@Setter +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class NotabeneWebhookMessage { + + /** + * Indicates that a blockchain transaction must be halted. + */ + public static final String TYPE_HALT_BLOCKCHAIN_TRANSACTION = "notification.haltBlockchainTransaction"; + /** + * Indicates that a blockchain transaction can be completed. + */ + public static final String TYPE_PROCESS_BLOCKCHAIN_TRANSACTION = "notification.processBlockchainTransaction"; + /** + * Notifies us about any changes to transfers. + */ + public static final String TYPE_TRANSACTION_UPDATED = "notification.transactionUpdated"; + + /** + * A message specifying the type of the payload. + * + * @see NotabeneWebhookMessage#TYPE_HALT_BLOCKCHAIN_TRANSACTION + * @see NotabeneWebhookMessage#TYPE_PROCESS_BLOCKCHAIN_TRANSACTION + * @see NotabeneWebhookMessage#TYPE_TRANSACTION_UPDATED + */ + private String message; + private NotabeneWebhookMessagePayload payload; + private String version; + +} diff --git a/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneWebhookMessagePayload.java b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneWebhookMessagePayload.java new file mode 100644 index 000000000..82085dcd0 --- /dev/null +++ b/server_extensions_extra/src/main/java/com/generalbytes/batm/server/extensions/travelrule/notabene/dto/NotabeneWebhookMessagePayload.java @@ -0,0 +1,26 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Represents the payload of a webhook message. + * + * @see NotabeneWebhookMessage + */ +@Getter +@Setter +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class NotabeneWebhookMessagePayload { + + /** + * Information about the affected transaction. + * + * @see NotabeneTransferInfo + */ + private NotabeneTransferInfo transaction; + +} diff --git a/server_extensions_extra/src/main/resources/batm-extensions.xml b/server_extensions_extra/src/main/resources/batm-extensions.xml index a63cea567..49c99e6e8 100644 --- a/server_extensions_extra/src/main/resources/batm-extensions.xml +++ b/server_extensions_extra/src/main/resources/batm-extensions.xml @@ -2375,4 +2375,5 @@ + diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/BatmExtensionsXmlTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/BatmExtensionsXmlTest.java index af3e648a7..a334149bb 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/BatmExtensionsXmlTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/BatmExtensionsXmlTest.java @@ -2,8 +2,7 @@ import com.generalbytes.batm.common.currencies.CryptoCurrency; import com.google.common.collect.ImmutableSet; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.w3c.dom.Document; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; @@ -23,25 +22,28 @@ import java.util.List; import java.util.Set; -public class BatmExtensionsXmlTest { +import static org.assertj.core.api.AssertionsForClassTypes.fail; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class BatmExtensionsXmlTest { private static final String XML_FILENAME = "src/main/resources/batm-extensions.xml"; @Test - public void testCryptoCurrencies() throws Exception { + void testCryptoCurrencies() throws Exception { for (String cryptocurrency : getXmlElementValues(XML_FILENAME, "//cryptocurrency/text()|//cryptologo/@cryptocurrency")) { try { CryptoCurrency.valueOfCode(cryptocurrency); } catch (IllegalArgumentException e) { - Assert.fail(cryptocurrency + " not in " + CryptoCurrency.class.getSimpleName() + " enum"); + fail(cryptocurrency + " not in " + CryptoCurrency.class.getSimpleName() + " enum"); } } } @Test - public void testCryptoLogos() throws Exception { + void testCryptoLogos() throws Exception { for (String file : getXmlElementValues(XML_FILENAME, "//cryptologo/@file")) { - Assert.assertTrue(file + " cryptologo file does not exist", new File("src/main/resources", file).exists()); + assertTrue(new File("src/main/resources", file).exists(), file + " cryptologo file does not exist"); } } @@ -49,7 +51,7 @@ public void testCryptoLogos() throws Exception { * tests that all extension class names mentioned in the XML exist */ @Test - public void testExtensionClassesExist() throws Exception { + void testExtensionClassesExist() throws Exception { getExtensionInstances(); } @@ -57,7 +59,7 @@ public void testExtensionClassesExist() throws Exception { * tests that CryptoCurrencyValidator exist for all crypto currencies used in the XML */ @Test - public void testCryptoCurrencyValidators() throws Exception { + void testCryptoCurrencyValidators() throws Exception { // currencies mentioned in batm_extensions.xml but having CryptoCurrencyValidators implemented in the non-opensourced part of the codebase Set supportedInBuiltin = ImmutableSet.of("TRTL", "BTC", "LBTC", "XMR", "ETC"); @@ -65,7 +67,7 @@ public void testCryptoCurrencyValidators() throws Exception { getCryptoCurrencies().stream() .filter(cryptoCurrency -> !supportedInBuiltin.contains(cryptoCurrency)) .filter(cryptoCurrency -> !anyExtensionProvidesCryptoCurrencyValidator(extensions, cryptoCurrency)) - .forEach(cryptoCurrency -> Assert.fail("No ICryptoAddressValidator found for " + cryptoCurrency)); + .forEach(cryptoCurrency -> fail("No ICryptoAddressValidator found for " + cryptoCurrency)); } private boolean anyExtensionProvidesCryptoCurrencyValidator(List extensions, String cryptoCurrency) { diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/coinutil/BCHUtilTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/coinutil/BCHUtilTest.java index 7042f295e..38b1edab2 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/coinutil/BCHUtilTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/coinutil/BCHUtilTest.java @@ -1,13 +1,14 @@ package com.generalbytes.batm.server.extensions.coinutil; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; -public class BCHUtilTest { +import static org.junit.jupiter.api.Assertions.assertEquals; + +class BCHUtilTest { @Test - public void convertBech32To3() { - Assert.assertEquals("11F89RtxhJ1TwjtpEZx7NLXwmEH8iz6RN", BCHUtil.convertBech32To3("qqqqhjsevx2979t4rlrtkqqqmkmza6rcyuytaa05sg")); + void convertBech32To3() { + assertEquals("11F89RtxhJ1TwjtpEZx7NLXwmEH8iz6RN", BCHUtil.convertBech32To3("qqqqhjsevx2979t4rlrtkqqqmkmza6rcyuytaa05sg")); } } diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/ExchangeTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/ExchangeTest.java index d4fc82965..2e584e370 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/ExchangeTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/ExchangeTest.java @@ -20,25 +20,22 @@ import com.generalbytes.batm.server.extensions.IExchange; import com.generalbytes.batm.server.extensions.IExchangeAdvanced; import com.generalbytes.batm.server.extensions.ITask; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import java.math.BigDecimal; import java.util.Arrays; import java.util.Collection; +import static org.junit.jupiter.api.Assertions.assertNotNull; + // All tests @Ignore'd here because they depend on external resources // To be run manually, not as a part of the build -@Ignore -@RunWith(Parameterized.class) -public class ExchangeTest { +@Disabled +class ExchangeTest { private final String cryptoCurrency; private final IExchange exchange; - @Parameterized.Parameters public static Collection getTestData() { return Arrays.asList(new Object[][]{ // {"LTC", new BinanceComExchange("", "", "EUR")}, @@ -53,14 +50,14 @@ public ExchangeTest(String cryptoCurrency, IExchange exchange) { } @Test - public void testPurchase() { + void testPurchase() { String purchaseId = exchange.purchaseCoins(BigDecimal.TEN, cryptoCurrency,"USD", "test"); System.out.println(purchaseId); - Assert.assertNotNull(purchaseId); + assertNotNull(purchaseId); } @Test - public void testPurchaseAdvanced() { + void testPurchaseAdvanced() { if (exchange instanceof IExchangeAdvanced) { ITask tt = ((IExchangeAdvanced) exchange).createPurchaseCoinsTask(new BigDecimal("0.00000001"), cryptoCurrency, exchange.getPreferredFiatCurrency(), "test"); tt.onCreate(); @@ -69,11 +66,11 @@ public void testPurchaseAdvanced() { } String purchaseId = tt.getResult() == null ? null : tt.getResult().toString(); System.out.println(purchaseId); - Assert.assertNotNull(purchaseId); + assertNotNull(purchaseId); } } @Test - public void testSellAdvanced() { + void testSellAdvanced() { if (exchange instanceof IExchangeAdvanced) { ITask tt = ((IExchangeAdvanced) exchange).createSellCoinsTask(new BigDecimal("0.00000001"), cryptoCurrency, exchange.getPreferredFiatCurrency(), "test"); tt.onCreate(); @@ -82,34 +79,34 @@ public void testSellAdvanced() { } String purchaseId = tt.getResult() == null ? null : tt.getResult().toString(); System.out.println(purchaseId); - Assert.assertNotNull(purchaseId); + assertNotNull(purchaseId); } } @Test - public void testGetCryptoBalance() { + void testGetCryptoBalance() { BigDecimal cryptoBalance = exchange.getCryptoBalance(cryptoCurrency); System.out.println(cryptoBalance); - Assert.assertNotNull(cryptoBalance); + assertNotNull(cryptoBalance); } @Test - public void testGetFiatBalance() { + void testGetFiatBalance() { BigDecimal fiatBalance = exchange.getFiatBalance(exchange.getPreferredFiatCurrency()); System.out.println(fiatBalance); - Assert.assertNotNull(fiatBalance); + assertNotNull(fiatBalance); } @Test - public void testGetDepositAddress() { + void testGetDepositAddress() { String depositAddress = exchange.getDepositAddress(cryptoCurrency); System.out.println(depositAddress); - Assert.assertNotNull(depositAddress); + assertNotNull(depositAddress); } @Test - public void testSendCoins() { + void testSendCoins() { String res = exchange.sendCoins(exchange.getDepositAddress(cryptoCurrency), exchange.getCryptoBalance(cryptoCurrency), cryptoCurrency, "test"); System.out.println(res); - Assert.assertNotNull(res); + assertNotNull(res); } } \ No newline at end of file diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/RateSourceTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/RateSourceTest.java index 97f603564..9b237c260 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/RateSourceTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/RateSourceTest.java @@ -18,26 +18,23 @@ package com.generalbytes.batm.server.extensions.extra; import com.generalbytes.batm.server.extensions.IRateSourceAdvanced; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.Arrays; import java.util.Collection; +import static org.junit.jupiter.api.Assertions.assertNotNull; + // All tests @Ignore'd here because they depend on external resources // To be run manually, not as a part of the build -@Ignore -@RunWith(Parameterized.class) -public class RateSourceTest { +@Disabled +class RateSourceTest { private final String cryptoCurrency; private final IRateSourceAdvanced rateSource; - @Parameterized.Parameters public static Collection getTestData() { return Arrays.asList(new Object[][]{ // {"LTC", new BinanceComExchange("USD")}, @@ -51,40 +48,40 @@ public RateSourceTest(String cryptoCurrency, IRateSourceAdvanced rateSource) { } @Test - public void testExchangeRateLast() { + void testExchangeRateLast() { BigDecimal rate = rateSource.getExchangeRateLast(cryptoCurrency, rateSource.getPreferredFiatCurrency()); System.out.println(rate); - Assert.assertNotNull(rate); + assertNotNull(rate); } @Test - public void testExchangeRateForSell() { + void testExchangeRateForSell() { BigDecimal rate = rateSource.getExchangeRateForSell(cryptoCurrency, rateSource.getPreferredFiatCurrency()); System.out.println(rate); - Assert.assertNotNull(rate); + assertNotNull(rate); } @Test - public void testExchangeRateForBuy() { + void testExchangeRateForBuy() { BigDecimal rate = rateSource.getExchangeRateForBuy(cryptoCurrency, rateSource.getPreferredFiatCurrency()); System.out.println(rate); - Assert.assertNotNull(rate); + assertNotNull(rate); } @Test - public void testcalculateBuyPrice() { + void testcalculateBuyPrice() { BigDecimal cryptoAmount = new BigDecimal("0.5"); BigDecimal rate = rateSource.calculateBuyPrice(cryptoCurrency, rateSource.getPreferredFiatCurrency(), cryptoAmount).divide(cryptoAmount, RoundingMode.FLOOR); System.out.println(rate); - Assert.assertNotNull(rate); + assertNotNull(rate); } @Test - public void testcalculateSellPrice() { + void testcalculateSellPrice() { BigDecimal cryptoAmount = new BigDecimal("0.5"); BigDecimal rate = rateSource.calculateSellPrice(cryptoCurrency, rateSource.getPreferredFiatCurrency(), cryptoAmount).divide(cryptoAmount, RoundingMode.FLOOR); System.out.println(rate); - Assert.assertNotNull(rate); + assertNotNull(rate); } } \ No newline at end of file diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/WalletTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/WalletTest.java index 188f083a3..9d98abfbb 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/WalletTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/WalletTest.java @@ -11,27 +11,21 @@ import com.generalbytes.batm.server.extensions.extra.litecoin.wallets.litecoind.LitecoindRPCWallet; import com.generalbytes.batm.server.extensions.extra.litecoin.wallets.litecoind.LitecoindUniqueAddressRPCWallet; import com.generalbytes.batm.server.extensions.payment.ReceivedAmount; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import java.math.BigDecimal; -import java.util.Arrays; -import java.util.Collection; + +import static org.junit.jupiter.api.Assertions.*; // All tests @Ignore'd here because they depend on external resources (locally running wallets). // To be run manually, not as a part of the build -@Ignore -@RunWith(Parameterized.class) -public class WalletTest { - private final String cryptoCurrency; - private final IWallet wallet; +@Disabled +class WalletTest { - @Parameterized.Parameters - public static Collection getTestData() { - return Arrays.asList(new Object[][]{ + public static Object[][] getTestData() { + return new Object[][]{ {CryptoCurrency.BCH, new BitcoinCashRPCWallet("http://user:password@localhost:9332", "")}, {CryptoCurrency.BCH, new BitcoinCashRPCWallet("http://user:password@localhost:9332", "BATMTEST")}, {CryptoCurrency.BCH, new BitcoinCashUniqueAddressRPCWallet("http://user:password@localhost:9332")}, @@ -43,55 +37,53 @@ public static Collection getTestData() { {CryptoCurrency.BTC, new BATMBitcoindRPCWallet("http://user:b999524f11318c0c86d5b51b3beffbc02b@localhost:8332", "")}, {CryptoCurrency.BTC, new BATMBitcoindRPCWallet("http://user:b999524f11318c0c86d5b51b3beffbc02b@localhost:8332", "BATMTEST")}, {CryptoCurrency.BTC, new BATMBitcoindRPCWalletWithUniqueAddresses("http://user:b999524f11318c0c86d5b51b3beffbc02b@localhost:8332")}, - }); - } - - // @Parameterized.Parameters annotated method results are passed here - public WalletTest(CryptoCurrency cryptoCurrency, IWallet wallet) { - this.cryptoCurrency = cryptoCurrency.getCode(); - this.wallet = wallet; + }; } - - @Test - public void testSendCoins() { + @ParameterizedTest + @MethodSource("getTestData") + void testSendCoins(String cryptoCurrency, IWallet wallet) { String tx = wallet.sendCoins(wallet.getCryptoAddress(cryptoCurrency), new BigDecimal("999"), cryptoCurrency, "test send to self"); System.out.println(tx); } - @Test - public void testGetCryptoAddress() { + @ParameterizedTest + @MethodSource("getTestData") + void testGetCryptoAddress(String cryptoCurrency, IWallet wallet) { String a = wallet.getCryptoAddress(cryptoCurrency); - Assert.assertNotNull(a); + assertNotNull(a); System.out.println(a); String b = wallet.getCryptoAddress(cryptoCurrency); - Assert.assertNotNull(b); + assertNotNull(b); System.out.println(b); - Assert.assertEquals("getCryptoAddress must return the same address every time", a, b); + assertEquals(a, b, "getCryptoAddress must return the same address every time"); } - @Test - public void testGenerateNewDepositCryptoAddress() { + @ParameterizedTest + @MethodSource("getTestData") + void testGenerateNewDepositCryptoAddress(String cryptoCurrency, IWallet wallet) { if (wallet instanceof IGeneratesNewDepositCryptoAddress) { String a = ((IGeneratesNewDepositCryptoAddress) wallet).generateNewDepositCryptoAddress(cryptoCurrency, "testtxid1"); - Assert.assertNotNull(a); + assertNotNull(a); System.out.println(a); String b = ((IGeneratesNewDepositCryptoAddress) wallet).generateNewDepositCryptoAddress(cryptoCurrency, "testtxid1"); - Assert.assertNotNull(b); + assertNotNull(b); System.out.println(b); - Assert.assertFalse("generateNewDepositCryptoAddress must return new address every time", a.equals(b)); + assertNotEquals(a, b, "generateNewDepositCryptoAddress must return new address every time"); } } - @Test - public void testGetCryptoBalance() { + @ParameterizedTest + @MethodSource("getTestData") + void testGetCryptoBalance(String cryptoCurrency, IWallet wallet) { BigDecimal cryptoBalance = wallet.getCryptoBalance(cryptoCurrency); System.out.println(cryptoBalance); - Assert.assertNotNull(cryptoBalance); + assertNotNull(cryptoBalance); } - @Test - public void testReceivedAmount() { + @ParameterizedTest + @MethodSource("getTestData") + void testReceivedAmount(String cryptoCurrency, IWallet wallet) { if (wallet instanceof IQueryableWallet) { ReceivedAmount amount = ((IQueryableWallet) wallet).getReceivedAmount("MAtZDadQRhRaD2uAZ8gUMHq8Say4TSVshw", cryptoCurrency); System.out.println(amount.getConfirmations()); diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/BitcoinExtensionTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/BitcoinExtensionTest.java index 1a6fb7dd4..65a762ae6 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/BitcoinExtensionTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/BitcoinExtensionTest.java @@ -3,19 +3,20 @@ import com.generalbytes.batm.server.extensions.IWallet; import com.generalbytes.batm.server.extensions.TestExtensionContext; import com.generalbytes.batm.server.extensions.extra.bitcoin.wallets.bitgo.v2.BitgoWallet; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.StringTokenizer; -public class BitcoinExtensionTest { +import static org.junit.jupiter.api.Assertions.*; + +class BitcoinExtensionTest { private static final Logger log = LoggerFactory.getLogger(BitcoinExtensionTest.class); @Test - public void testCreateFromExtension() { + void testCreateFromExtension() { testUrl("http://localhost:3080/", "bitgo:http://localhost:3080:tokentoken:wallet_address:wallet_passphrase"); testUrl("http://localhost:3080/", "bitgo:localhost:3080:tokentoken:wallet_address:wallet_passphrase"); testUrl("http://localhost/", "bitgo:http://localhost:tokentoken:wallet_address:wallet_passphrase"); @@ -28,12 +29,12 @@ private void testUrl(String expected, String walletLogin) { final BitcoinExtension bitcoinExtension = new BitcoinExtension(); bitcoinExtension.init(new TestExtensionContext()); final IWallet bitgowallet = bitcoinExtension.createWallet(walletLogin, null); - Assert.assertTrue(bitgowallet instanceof BitgoWallet); - Assert.assertEquals(expected, ((BitgoWallet)bitgowallet).getUrl()); + assertTrue(bitgowallet instanceof BitgoWallet); + assertEquals(expected, ((BitgoWallet)bitgowallet).getUrl()); } @Test - public void testCreateWalletBitGoFees() { + void testCreateWalletBitGoFees() { // Both fee rate and max fee rate are set -> parse fee rate and max fee rate individually doTestCreateWalletBitGoFees(createBitGoWalletUrl(1000, 2000), 1000, 2000); // Max fee rate is lower than fee rate -> use fee rate as max fee rate @@ -59,15 +60,15 @@ private void doTestCreateWalletBitGoFees(String url, Integer expectedFeeRate, In final BitcoinExtension bitcoinExtension = new BitcoinExtension(); bitcoinExtension.init(new TestExtensionContext()); final IWallet wallet = bitcoinExtension.createWallet(url, null); - Assert.assertTrue(wallet instanceof BitgoWallet); + assertTrue(wallet instanceof BitgoWallet); final BitgoWallet bitgoWallet = (BitgoWallet) wallet; - Assert.assertNotNull(bitgoWallet); - Assert.assertEquals(expectedFeeRate, bitgoWallet.getFeeRate()); - Assert.assertEquals(expectedMaxFeeRate, bitgoWallet.getMaxFeeRate()); + assertNotNull(bitgoWallet); + assertEquals(expectedFeeRate, bitgoWallet.getFeeRate()); + assertEquals(expectedMaxFeeRate, bitgoWallet.getMaxFeeRate()); } @Test - public void bitgoFullTokenTest() { + void bitgoFullTokenTest() { String wallet = "bitgo:http://localhost:3080:v2x8d5e9e46379dc328b2039a400a12b04ea986689b38107fd84cd339bc89e3fb21:5b20e3a9266bbe80095757489d84a6bb:Vranec8586"; StringTokenizer st = new StringTokenizer(wallet,":"); String walletType = st.nextToken(); @@ -98,24 +99,24 @@ public void bitgoFullTokenTest() { log.info("wallet = {}, protocol = {}, host = {}, port = {}, fullHost = {}, token = {}, address = {}, passphrase = {}, next = {}", walletType, protocol, host, port, fullHost, token, walletAddress, walletPassphrase, next); - Assert.assertEquals("http", protocol); - Assert.assertEquals("//localhost", host); - Assert.assertEquals("http://localhost", fullHost); - Assert.assertEquals("3080", port); + assertEquals("http", protocol); + assertEquals("//localhost", host); + assertEquals("http://localhost", fullHost); + assertEquals("3080", port); final BitcoinExtension bitcoinExtension = new BitcoinExtension(); bitcoinExtension.init(new TestExtensionContext()); final IWallet bitgowallet = bitcoinExtension.createWallet(wallet, null); - Assert.assertTrue(bitgowallet instanceof BitgoWallet); + assertTrue(bitgowallet instanceof BitgoWallet); final BitgoWallet bitgoWallet = (BitgoWallet)bitgowallet; - Assert.assertNotNull(bitgoWallet); - Assert.assertEquals("http://localhost:3080/", bitgoWallet.getUrl()); - Assert.assertEquals("5b20e3a9266bbe80095757489d84a6bb", bitgoWallet.getWalletId()); + assertNotNull(bitgoWallet); + assertEquals("http://localhost:3080/", bitgoWallet.getUrl()); + assertEquals("5b20e3a9266bbe80095757489d84a6bb", bitgoWallet.getWalletId()); log.info("wallet = " + bitgoWallet); } @Test - public void bitgoNoPortTokenTest() { + void bitgoNoPortTokenTest() { String wallet = "bitgo:http://localhost:v2x8d5e9e46379dc328b2039a400a12b04ea986689b38107fd84cd339bc89e3fb21:5b20e3a9266bbe80095757489d84a6bb:Vranec8586"; StringTokenizer st = new StringTokenizer(wallet,":"); String walletType = st.nextToken(); @@ -146,24 +147,24 @@ public void bitgoNoPortTokenTest() { log.info("wallet = {}, protocol = {}, host = {}, port = {}, fullHost = {}, token = {}, address = {}, passphrase = {}, next = {}", walletType, protocol, host, port, fullHost, token, walletAddress, walletPassphrase, next); - Assert.assertEquals("http", protocol); - Assert.assertEquals("//localhost", host); - Assert.assertEquals("http://localhost", fullHost); - Assert.assertEquals("", port); + assertEquals("http", protocol); + assertEquals("//localhost", host); + assertEquals("http://localhost", fullHost); + assertEquals("", port); final BitcoinExtension bitcoinExtension = new BitcoinExtension(); bitcoinExtension.init(new TestExtensionContext()); final IWallet bitgowallet = bitcoinExtension.createWallet(wallet, null); - Assert.assertTrue(bitgowallet instanceof BitgoWallet); + assertTrue(bitgowallet instanceof BitgoWallet); final BitgoWallet bitgoWallet = (BitgoWallet)bitgowallet; - Assert.assertNotNull(bitgoWallet); - Assert.assertEquals("http://localhost/", bitgoWallet.getUrl()); - Assert.assertEquals("5b20e3a9266bbe80095757489d84a6bb", bitgoWallet.getWalletId()); + assertNotNull(bitgoWallet); + assertEquals("http://localhost/", bitgoWallet.getUrl()); + assertEquals("5b20e3a9266bbe80095757489d84a6bb", bitgoWallet.getWalletId()); log.info("wallet = " + bitgoWallet); } @Test - public void bitgoHttpsTokenTest() { + void bitgoHttpsTokenTest() { String wallet = "bitgo:https://localhost:3080:v2x8d5e9e46379dc328b2039a400a12b04ea986689b38107fd84cd339bc89e3fb21:5b20e3a9266bbe80095757489d84a6bb:Vranec8586"; StringTokenizer st = new StringTokenizer(wallet,":"); String walletType = st.nextToken(); @@ -194,24 +195,24 @@ public void bitgoHttpsTokenTest() { log.info("wallet = {}, protocol = {}, host = {}, port = {}, fullHost = {}, token = {}, address = {}, passphrase = {}, next = {}", walletType, protocol, host, port, fullHost, token, walletAddress, walletPassphrase, next); - Assert.assertEquals("https", protocol); - Assert.assertEquals("//localhost", host); - Assert.assertEquals("https://localhost", fullHost); - Assert.assertEquals("3080", port); + assertEquals("https", protocol); + assertEquals("//localhost", host); + assertEquals("https://localhost", fullHost); + assertEquals("3080", port); final BitcoinExtension bitcoinExtension = new BitcoinExtension(); bitcoinExtension.init(new TestExtensionContext()); final IWallet bitgowallet = bitcoinExtension.createWallet(wallet, null); - Assert.assertTrue(bitgowallet instanceof BitgoWallet); + assertTrue(bitgowallet instanceof BitgoWallet); final BitgoWallet bitgoWallet = (BitgoWallet)bitgowallet; - Assert.assertNotNull(bitgoWallet); - Assert.assertEquals("https://localhost:3080/", bitgoWallet.getUrl()); - Assert.assertEquals("5b20e3a9266bbe80095757489d84a6bb", bitgoWallet.getWalletId()); + assertNotNull(bitgoWallet); + assertEquals("https://localhost:3080/", bitgoWallet.getUrl()); + assertEquals("5b20e3a9266bbe80095757489d84a6bb", bitgoWallet.getWalletId()); log.info("wallet = " + bitgoWallet); } @Test - public void bitgoNoProtocolTokenTest() { + void bitgoNoProtocolTokenTest() { String wallet = "bitgo:localhost:3080:v2x8d5e9e46379dc328b2039a400a12b04ea986689b38107fd84cd339bc89e3fb21:5b20e3a9266bbe80095757489d84a6bb:Vranec8586"; StringTokenizer st = new StringTokenizer(wallet,":"); String walletType = st.nextToken(); @@ -242,59 +243,59 @@ public void bitgoNoProtocolTokenTest() { log.info("wallet = {}, protocol = {}, host = {}, port = {}, fullHost = {}, token = {}, address = {}, passphrase = {}, next = {}", walletType, protocol, host, port, fullHost, token, walletAddress, walletPassphrase, next); - Assert.assertEquals("", protocol); - Assert.assertEquals("localhost", host); - Assert.assertEquals("localhost", fullHost); - Assert.assertEquals("3080", port); + assertEquals("", protocol); + assertEquals("localhost", host); + assertEquals("localhost", fullHost); + assertEquals("3080", port); final BitcoinExtension bitcoinExtension = new BitcoinExtension(); bitcoinExtension.init(new TestExtensionContext()); final IWallet bitgowallet = bitcoinExtension.createWallet(wallet, null); - Assert.assertTrue(bitgowallet instanceof BitgoWallet); + assertTrue(bitgowallet instanceof BitgoWallet); final BitgoWallet bitgoWallet = (BitgoWallet)bitgowallet; - Assert.assertNotNull(bitgoWallet); - Assert.assertEquals("http://localhost:3080/", bitgoWallet.getUrl()); - Assert.assertEquals("5b20e3a9266bbe80095757489d84a6bb", bitgoWallet.getWalletId()); + assertNotNull(bitgoWallet); + assertEquals("http://localhost:3080/", bitgoWallet.getUrl()); + assertEquals("5b20e3a9266bbe80095757489d84a6bb", bitgoWallet.getWalletId()); log.info("wallet = " + bitgoWallet); } @Test - public void bitgoWalletTest() { + void bitgoWalletTest() { String address = "bitgo:http://localhost:3080:v2x8d5e9e46379dc328b2039a400a12b04ea986689b38107fd84cd339bc89e3fb21:5b20e3a9266bbe80095757489d84a6bb:Vranec8586"; final BitcoinExtension bitcoinExtension = new BitcoinExtension(); bitcoinExtension.init(new TestExtensionContext()); final IWallet wallet = bitcoinExtension.createWallet(address, null); - Assert.assertTrue(wallet instanceof BitgoWallet); + assertTrue(wallet instanceof BitgoWallet); final BitgoWallet bitgoWallet = (BitgoWallet)wallet; - Assert.assertNotNull(bitgoWallet); - Assert.assertNotNull(bitgoWallet); - Assert.assertEquals("http://localhost:3080/", bitgoWallet.getUrl()); - Assert.assertEquals("5b20e3a9266bbe80095757489d84a6bb", bitgoWallet.getWalletId()); + assertNotNull(bitgoWallet); + assertNotNull(bitgoWallet); + assertEquals("http://localhost:3080/", bitgoWallet.getUrl()); + assertEquals("5b20e3a9266bbe80095757489d84a6bb", bitgoWallet.getWalletId()); log.info("wallet = " + bitgoWallet); } @Test - public void bitgoErrorTest() { + void bitgoErrorTest() { String body = "{\"error\":\"invalid address for network\",\"name\":\"Invalid\",\"requestId\":\"cjjectv6y2zlgill815ppvp4g\",\"message\":\"invalid address for network\"}"; int index1 = body.indexOf("error") + 8; int index2 = body.indexOf(",") - 1; String value = body.substring(index1, index2); - Assert.assertNotNull(value); - Assert.assertEquals("invalid address for network", value); + assertNotNull(value); + assertEquals("invalid address for network", value); body = "{\"error\":\"sub-dust-threshold amount for LWcx7VHK3hKDV5KaiE2EQLPpGt9GEVwzdM: 10000\",\"name\":\"Invalid\",\"requestId\":\"cjjebbtly2ew1gmldyr9h2dnk\",\"message\":\"sub-dust-threshold amount for LWcx7VHK3hKDV5KaiE2EQLPpGt9GEVwzdM: 10000\"}"; index1 = body.indexOf("error") + 8; index2 = body.indexOf(",") - 1; value = body.substring(index1, index2); - Assert.assertNotNull(value); - Assert.assertEquals("sub-dust-threshold amount for LWcx7VHK3hKDV5KaiE2EQLPpGt9GEVwzdM: 10000", value); + assertNotNull(value); + assertEquals("sub-dust-threshold amount for LWcx7VHK3hKDV5KaiE2EQLPpGt9GEVwzdM: 10000", value); body = "{\"error\":\"needs unlock\",\"needsOTP\":true,\"needsUnlock\":true,\"name\":\"Response\",\"requestId\":\"cjjebd0lq2ke0g7l73czlu75j\",\"message\":\"needs unlock\"}"; index1 = body.indexOf("error") + 8; index2 = body.indexOf(",") - 1; value = body.substring(index1, index2); - Assert.assertNotNull(value); - Assert.assertEquals("needs unlock", value); + assertNotNull(value); + assertEquals("needs unlock", value); } } diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/exchanges/binance/BinanceExchangeRoundingTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/exchanges/binance/BinanceExchangeRoundingTest.java index cc14d2547..c07c2d778 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/exchanges/binance/BinanceExchangeRoundingTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/exchanges/binance/BinanceExchangeRoundingTest.java @@ -1,16 +1,16 @@ package com.generalbytes.batm.server.extensions.extra.bitcoin.exchanges.binance; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.math.BigDecimal; import static org.assertj.core.api.Assertions.assertThat; -public class BinanceExchangeRoundingTest { +class BinanceExchangeRoundingTest { BinanceUsExchange e = new BinanceUsExchange("USD"); @Test - public void testGetAmountRoundedToMinStep() { + void testGetAmountRoundedToMinStep() { assertEquals("1.1", "0.00001", "1.1"); assertEquals("1.12345789", "0.0001", "1.1234"); assertEquals("1.12345789", "0.002", "1.122"); diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/exchanges/binance/BinanceExchangeTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/exchanges/binance/BinanceExchangeTest.java index 2ba77748c..5e6d565bc 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/exchanges/binance/BinanceExchangeTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/exchanges/binance/BinanceExchangeTest.java @@ -1,21 +1,18 @@ package com.generalbytes.batm.server.extensions.extra.bitcoin.exchanges.binance; import com.generalbytes.batm.server.extensions.IExchangeAdvanced; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import java.math.BigDecimal; import java.util.Arrays; import java.util.Collection; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.assertj.core.api.Fail.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; -@Ignore // requires online resources - for manual run only -@RunWith(Parameterized.class) -public class BinanceExchangeTest { +@Disabled // requires online resources - for manual run only +class BinanceExchangeTest { private final IExchangeAdvanced exchange; @@ -24,7 +21,6 @@ public BinanceExchangeTest(IExchangeAdvanced exchange) { this.exchange = exchange; } - @Parameterized.Parameters public static Collection params() { return Arrays.asList(new Object[][]{ // {new BinanceUsExchange("", "", "USD")}, @@ -35,7 +31,7 @@ public static Collection params() { // this tests getTradableAmount(). If amount sent to exchange had too many decimal places, it throwed "Filter failure: LOT_SIZE" message @Test - public void testLotSizeSell() { + void testLotSizeSell() { try { String result = exchange.sellCoins(new BigDecimal("9.14155797"), "BTC", "USD", ""); fail("Expected exception not thrown"); @@ -45,7 +41,7 @@ public void testLotSizeSell() { } @Test - public void testLotSizeBuy() { + void testLotSizeBuy() { try { String result = exchange.purchaseCoins(new BigDecimal("9.14155797"), "BTC", "USD", ""); fail("Expected exception not thrown"); @@ -55,7 +51,7 @@ public void testLotSizeBuy() { } @Test - public void testInsufficientBalance() { + void testInsufficientBalance() { try { String result = exchange.sellCoins(new BigDecimal("10"), "BTC", "USD", ""); fail("Expected exception not thrown"); @@ -66,7 +62,7 @@ public void testInsufficientBalance() { @Test - public void test() { + void test() { // BinanceUsExchange rs = new BinanceUsExchange("USD"); // System.out.println(rs.getExchangeRateLast("BTC", "USD")); // System.out.println(rs.getExchangeRateLast("LTC", "USD")); diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/exchanges/bitpandapro/BitpandaProExchangeTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/exchanges/bitpandapro/BitpandaProExchangeTest.java index b57722c6c..719f5be0d 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/exchanges/bitpandapro/BitpandaProExchangeTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/exchanges/bitpandapro/BitpandaProExchangeTest.java @@ -12,22 +12,19 @@ import static com.generalbytes.batm.common.currencies.FiatCurrency.CHF; import static com.generalbytes.batm.common.currencies.FiatCurrency.EUR; import static com.generalbytes.batm.common.currencies.FiatCurrency.GBP; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import java.math.BigDecimal; import java.net.URI; import java.util.stream.Stream; -import org.junit.Ignore; -import org.junit.Test; - import com.generalbytes.batm.server.extensions.IExchangeAdvanced; import com.generalbytes.batm.server.extensions.ITask; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; -@Ignore // requires online resources - for manual run only -public class BitpandaProExchangeTest { +@Disabled // requires online resources - for manual run only +class BitpandaProExchangeTest { /** exchange sandbox */ private static final URI API = URI.create("https://api.exchange.waskurzes.com"); private static final String API_KEY = "YOUR.API.KEY"; @@ -35,58 +32,58 @@ public class BitpandaProExchangeTest { private final IExchangeAdvanced subject = new BitpandaProExchange(API, API_KEY, EUR.getCode()); @Test - public void shouldFetchCryptoCurrencies() { + void shouldFetchCryptoCurrencies() { assertNotNull(subject.getCryptoCurrencies()); } @Test - public void shouldFetchFiatCurrencies() { + void shouldFetchFiatCurrencies() { assertNotNull(subject.getFiatCurrencies()); } @Test - public void shouldYieldConfiguredPreferredFiatCurrency() { + void shouldYieldConfiguredPreferredFiatCurrency() { assertEquals("EUR", subject.getPreferredFiatCurrency()); } @Test - public void shouldFetchFiatBalances() { + void shouldFetchFiatBalances() { Stream.of(EUR, CHF, GBP).forEach(currency -> { final BigDecimal balance = subject.getFiatBalance(EUR.getCode()); - assertNotNull("getFiatBalance(" + currency + ")", balance); - assertTrue("negative balance", balance.compareTo(BigDecimal.ZERO) >= 0); + assertNotNull(balance, "getFiatBalance(" + currency + ")"); + assertTrue(balance.compareTo(BigDecimal.ZERO) >= 0, "negative balance"); }); } @Test - public void shouldFetchCryptoBalances() { + void shouldFetchCryptoBalances() { Stream.of(BTC, ETH, XRP, ADA, USDT, TRX, BCH, DOGE, LTC ).forEach(currency -> { final BigDecimal balance = subject.getCryptoBalance(currency.getCode()); - assertNotNull("getCryptoBalance(" + currency + ")", balance); - assertTrue("negative balance", balance.compareTo(BigDecimal.ZERO) >= 0); + assertNotNull(balance, "getCryptoBalance(" + currency + ")"); + assertTrue(balance.compareTo(BigDecimal.ZERO) >= 0, "negative balance"); }); } @Test - public void shouldGetBitcoinDepositAddress() { + void shouldGetBitcoinDepositAddress() { final String address = subject.getDepositAddress(BTC.getCode()); assertNotNull(address); } @Test - public void shouldWithdrawBitcoin() { + void shouldWithdrawBitcoin() { final String transaction = subject.sendCoins("3FXtMi8gC2TUgzBoFnBxeRKWz1Gw8bSrbJ", BigDecimal.ONE, BTC.getCode(), "batm-test"); assertNotNull(transaction); } @Test - public void shouldSellCoins() { + void shouldSellCoins() { final String orderId = subject.sellCoins(new BigDecimal("0.01"), BTC.getCode(), EUR.getCode(), "batm-sell-test"); assertNotNull(orderId); } @Test - public void shouldSellCoinsAdvanced() throws InterruptedException { + void shouldSellCoinsAdvanced() throws InterruptedException { final ITask task = subject.createSellCoinsTask(new BigDecimal("0.02"), BTC.getCode(), EUR.getCode(), "batm-sell-advanced-test"); task.onCreate(); for (int i = 0; i < 10 && !task.isFinished(); i++) { @@ -97,13 +94,13 @@ public void shouldSellCoinsAdvanced() throws InterruptedException { } @Test - public void shouldPurchaseCoins() { + void shouldPurchaseCoins() { final String orderId = subject.purchaseCoins(new BigDecimal("0.01"), BTC.getCode(), EUR.getCode(), "batm-purchase-test"); assertNotNull(orderId); } @Test - public void shouldPurchaseCoinsAdvanced() throws InterruptedException { + void shouldPurchaseCoinsAdvanced() throws InterruptedException { final ITask task = subject.createPurchaseCoinsTask(new BigDecimal("0.02"), BTC.getCode(), EUR.getCode(), "batm-purchase-advanced-test"); task.onCreate(); for (int i = 0; i < 10 && !task.isFinished(); i++) { diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/exchanges/bitpandapro/BitpandaProRateSourceTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/exchanges/bitpandapro/BitpandaProRateSourceTest.java index 273309ad4..42df6950a 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/exchanges/bitpandapro/BitpandaProRateSourceTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/exchanges/bitpandapro/BitpandaProRateSourceTest.java @@ -1,8 +1,8 @@ package com.generalbytes.batm.server.extensions.extra.bitcoin.exchanges.bitpandapro; import com.generalbytes.batm.server.extensions.IRateSourceAdvanced; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import java.math.BigDecimal; @@ -14,33 +14,30 @@ import static com.generalbytes.batm.common.currencies.FiatCurrency.EUR; import static com.generalbytes.batm.common.currencies.FiatCurrency.GBP; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.*; -@Ignore // requires online resources - for manual run only -public class BitpandaProRateSourceTest { +@Disabled // requires online resources - for manual run only +class BitpandaProRateSourceTest { private final IRateSourceAdvanced subject = BitpandaProExchange.asRateSource(EUR.getCode()); @Test - public void shouldFetchCryptoCurrencies() { + void shouldFetchCryptoCurrencies() { assertNotNull(subject.getCryptoCurrencies()); } @Test - public void shouldFetchFiatCurrencies() { + void shouldFetchFiatCurrencies() { assertNotNull(subject.getFiatCurrencies()); } @Test - public void shouldYieldConfiguredPreferredFiatCurrency() { + void shouldYieldConfiguredPreferredFiatCurrency() { assertEquals("EUR", subject.getPreferredFiatCurrency()); } @Test - public void shouldFailOnInvalidInstrument() { + void shouldFailOnInvalidInstrument() { // bogus crypto assertNull(subject.getExchangeRateLast("GAGA", "EUR")); // bogus fiat @@ -56,7 +53,7 @@ public void shouldFailOnInvalidInstrument() { } @Test - public void shouldFetchLastExchangeRate() { + void shouldFetchLastExchangeRate() { // fetch fresh final BigDecimal fresh = subject.getExchangeRateLast(BTC.getCode(), EUR.getCode()); assertNotNull(fresh); @@ -67,40 +64,40 @@ public void shouldFetchLastExchangeRate() { } @Test - public void shouldCalculateReasonableBuyAndSellPricesForOneBitcoin() { + void shouldCalculateReasonableBuyAndSellPricesForOneBitcoin() { final BigDecimal buy = subject.calculateBuyPrice(BTC.getCode(), EUR.getCode(), BigDecimal.ONE); assertNotNull(buy); - assertTrue("calculated non-positive buy price", buy.compareTo(BigDecimal.ZERO) > 0); + assertTrue(buy.compareTo(BigDecimal.ZERO) > 0, "calculated non-positive buy price"); final BigDecimal sell = subject.calculateSellPrice(BTC.getCode(), EUR.getCode(), BigDecimal.ONE); assertNotNull(sell); - assertTrue("calculated non-positive sell price", sell.compareTo(BigDecimal.ZERO) > 0); + assertTrue(sell.compareTo(BigDecimal.ZERO) > 0, "calculated non-positive sell price"); - assertTrue("buy price is smaller than sell", buy.compareTo(sell) >= 0); + assertTrue(buy.compareTo(sell) >= 0, "buy price is smaller than sell"); } @Test - public void shouldCalculateReasonableBuyAndSellRatesForBitcoin() { + void shouldCalculateReasonableBuyAndSellRatesForBitcoin() { final BigDecimal buy = subject.getExchangeRateForBuy(BTC.getCode(), EUR.getCode()); assertNotNull(buy); - assertTrue("calculated non-positive buy price", buy.compareTo(BigDecimal.ZERO) > 0); + assertTrue(buy.compareTo(BigDecimal.ZERO) > 0, "calculated non-positive buy price"); final BigDecimal sell = subject.getExchangeRateForSell(BTC.getCode(), EUR.getCode()); assertNotNull(sell); - assertTrue("calculated non-positive sell price", sell.compareTo(BigDecimal.ZERO) > 0); + assertTrue(sell.compareTo(BigDecimal.ZERO) > 0, "calculated non-positive sell price"); - assertTrue("buy price is smaller than sell", buy.compareTo(sell) >= 0); + assertTrue(buy.compareTo(sell) >= 0, "buy price is smaller than sell"); } @Test - public void shouldSupportAllConfiguredPairs() { - assertNotNull("BTC/EUR", subject.getExchangeRateLast(BTC.getCode(), EUR.getCode())); - assertNotNull("ETH/EUR", subject.getExchangeRateLast(ETH.getCode(), EUR.getCode())); - assertNotNull("XRP/EUR", subject.getExchangeRateLast(XRP.getCode(), EUR.getCode())); - assertNotNull("BTC/CHF", subject.getExchangeRateLast(BTC.getCode(), CHF.getCode())); - assertNotNull("ETH/CHF", subject.getExchangeRateLast(ETH.getCode(), CHF.getCode())); - assertNotNull("XRP/CHF", subject.getExchangeRateLast(XRP.getCode(), CHF.getCode())); - assertNotNull("BTC/GBP", subject.getExchangeRateLast(BTC.getCode(), GBP.getCode())); - assertNotNull("DOGE/EUR", subject.getExchangeRateLast(DOGE.getCode(), EUR.getCode())); + void shouldSupportAllConfiguredPairs() { + assertNotNull(subject.getExchangeRateLast(BTC.getCode(), EUR.getCode()), "BTC/EUR"); + assertNotNull(subject.getExchangeRateLast(ETH.getCode(), EUR.getCode()), "ETH/EUR"); + assertNotNull(subject.getExchangeRateLast(XRP.getCode(), EUR.getCode()), "XRP/EUR"); + assertNotNull(subject.getExchangeRateLast(BTC.getCode(), CHF.getCode()), "BTC/CHF"); + assertNotNull(subject.getExchangeRateLast(ETH.getCode(), CHF.getCode()), "ETH/CHF"); + assertNotNull(subject.getExchangeRateLast(XRP.getCode(), CHF.getCode()), "XRP/CHF"); + assertNotNull(subject.getExchangeRateLast(BTC.getCode(), GBP.getCode()), "BTC/GBP"); + assertNotNull(subject.getExchangeRateLast(DOGE.getCode(), EUR.getCode()), "DOGE/EUR"); } } diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/exchanges/stillmandigital/StillmanDigitalExchangeTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/exchanges/stillmandigital/StillmanDigitalExchangeTest.java index 17fe7511a..0c7a0db9a 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/exchanges/stillmandigital/StillmanDigitalExchangeTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/exchanges/stillmandigital/StillmanDigitalExchangeTest.java @@ -1,40 +1,40 @@ package com.generalbytes.batm.server.extensions.extra.bitcoin.exchanges.stillmandigital; import com.generalbytes.batm.server.extensions.ITask; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import java.math.BigDecimal; import java.security.GeneralSecurityException; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotNull; -@Ignore // requires online resources - for manual run only -public class StillmanDigitalExchangeTest { +@Disabled // requires online resources - for manual run only +class StillmanDigitalExchangeTest { private static final String PUBLIC_KEY = "9QU20A1IO3QN2L6ND90JBG80"; private static final String PRIVATE_KEY = "A2CBOQMJKJ69O59ZC9SKVY58M9NCNQQXEOF9W1Y0DSF56GBI"; private static final String BASE_URL = "https://sandbox-api.stillmandigital.com"; private static StillmanDigitalExchange exchange; - @BeforeClass - public static void createExchange() throws GeneralSecurityException { + @BeforeEach + void setUp() throws GeneralSecurityException { exchange = new StillmanDigitalExchange(PUBLIC_KEY, PRIVATE_KEY, BASE_URL); } @Test - public void getFiatBalanceTest() { + void getFiatBalanceTest() { System.out.println("USD balance: " + exchange.getFiatBalance("USD")); } @Test - public void getCryptoBalanceTest() { + void getCryptoBalanceTest() { System.out.println("Crypto balance: " + exchange.getFiatBalance("BTC")); } @Test - public void getRatesTest() { + void getRatesTest() { System.out.println("Buy rate: " + exchange.getExchangeRateForBuy("BTC", "USD")); System.out.println("Sell rate: " + exchange.getExchangeRateForSell("BTC", "USD")); System.out.println("Buy rate for 1 BTC: " + exchange.calculateBuyPrice("BTC", "USD", BigDecimal.ONE)); @@ -43,7 +43,7 @@ public void getRatesTest() { @Test - public void createOrderTest() throws InterruptedException { + void createOrderTest() throws InterruptedException { ITask task = exchange.createPurchaseCoinsTask(BigDecimal.valueOf(0.01), "BTC", "USD", null); task.onCreate(); for (int i = 0; i < 10 && !task.isFinished(); i++) { diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/wallets/bitgo/v2/BitgoAPITest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/wallets/bitgo/v2/BitgoAPITest.java index 4f2a6bf4f..a8f8a87bf 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/wallets/bitgo/v2/BitgoAPITest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/wallets/bitgo/v2/BitgoAPITest.java @@ -4,10 +4,9 @@ import ch.qos.logback.classic.LoggerContext; import com.generalbytes.batm.server.extensions.extra.bitcoin.wallets.bitgo.v2.dto.BitGoCoinRequest; import com.generalbytes.batm.server.extensions.util.net.CompatSSLSocketFactory; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import si.mazi.rescu.ClientConfig; @@ -23,8 +22,10 @@ import java.util.List; import java.util.Map; +import static org.junit.jupiter.api.Assertions.*; -public class BitgoAPITest { + +class BitgoAPITest { private static final Logger log = LoggerFactory.getLogger(BitgoAPITest.class); @@ -48,8 +49,8 @@ private static void setLoggerLevel(String name, String level) { } } - @BeforeClass - public static void setup() { + @BeforeEach + void setUp() { setLoggerLevel("batm", "trace"); setLoggerLevel("si.mazi.rescu","trace"); @@ -72,97 +73,97 @@ public static void setup() { } @Test - @Ignore - public void getWalletsTest() throws IOException { + @Disabled + void getWalletsTest() throws IOException { final Map result = api.getWallets("tbtc"); - Assert.assertNotNull(result); + assertNotNull(result); final Object coin = result.get("coin"); - Assert.assertNotNull(coin); - Assert.assertTrue(coin instanceof String); + assertNotNull(coin); + assertTrue(coin instanceof String); final String stringCoin = (String) coin; - Assert.assertEquals("tbtc", stringCoin); + assertEquals("tbtc", stringCoin); final Object wallets = result.get("wallets"); - Assert.assertNotNull(wallets); - Assert.assertTrue(wallets instanceof ArrayList); + assertNotNull(wallets); + assertTrue(wallets instanceof ArrayList); final ArrayList list = (ArrayList)wallets; - Assert.assertFalse(list.isEmpty()); + assertFalse(list.isEmpty()); final Object element = list.get(0); - Assert.assertNotNull(element); - Assert.assertTrue(element instanceof LinkedHashMap); + assertNotNull(element); + assertTrue(element instanceof LinkedHashMap); final LinkedHashMap map = (LinkedHashMap)element; - Assert.assertNotNull(map); + assertNotNull(map); final String id = (String) map.get("id"); - Assert.assertNotNull(id); - Assert.assertEquals("5b20e3a9266bbe80095757489d84a6bb", id); + assertNotNull(id); + assertEquals("5b20e3a9266bbe80095757489d84a6bb", id); final Object users = map.get("users"); - Assert.assertNotNull(users); - Assert.assertTrue(users instanceof ArrayList); + assertNotNull(users); + assertTrue(users instanceof ArrayList); final ArrayList userList = (ArrayList)users; - Assert.assertFalse(userList.isEmpty()); + assertFalse(userList.isEmpty()); final Object user = userList.get(0); - Assert.assertNotNull(user); - Assert.assertTrue(user instanceof LinkedHashMap); + assertNotNull(user); + assertTrue(user instanceof LinkedHashMap); LinkedHashMap userMap = (LinkedHashMap)user; String username = (String) userMap.get("user"); - Assert.assertNotNull(username); - Assert.assertEquals("5b20e31ebd6c12af0983fcb383e19a21", username); + assertNotNull(username); + assertEquals("5b20e31ebd6c12af0983fcb383e19a21", username); final String cryptoCoin = (String) map.get("coin"); - Assert.assertNotNull(cryptoCoin); - Assert.assertEquals("tbtc", cryptoCoin); + assertNotNull(cryptoCoin); + assertEquals("tbtc", cryptoCoin); String label = (String) map.get("label"); - Assert.assertEquals("TBTCWallet", label); + assertEquals("TBTCWallet", label); log.debug("Wallet label = {}", label); } @Test - @Ignore - public void getBalancesTest() throws IOException { + @Disabled + void getBalancesTest() throws IOException { final String accessToken = "Bearer v2x8d5e9e46379dc328b2039a400a12b04ea986689b38107fd84cd339bc89e3fb21"; final Map result = api.getTotalBalances("tbtc"); - Assert.assertNotNull(result); - Assert.assertTrue(result instanceof LinkedHashMap); + assertNotNull(result); + assertTrue(result instanceof LinkedHashMap); final LinkedHashMap map = (LinkedHashMap)result; - Assert.assertFalse(map.isEmpty()); + assertFalse(map.isEmpty()); final Integer balance = (Integer) map.get("balance"); - Assert.assertNotNull(balance); - Assert.assertTrue(balance instanceof Integer); + assertNotNull(balance); + assertTrue(balance instanceof Integer); final String balanceString = (String) map.get("balanceString"); - Assert.assertNotNull(balanceString); - Assert.assertTrue(balanceString instanceof String); + assertNotNull(balanceString); + assertTrue(balanceString instanceof String); final Integer confirmedBalance = (Integer) map.get("confirmedBalance"); - Assert.assertNotNull(confirmedBalance); + assertNotNull(confirmedBalance); final String confirmedBalanceString = (String) map.get("confirmedBalanceString"); - Assert.assertNotNull(confirmedBalanceString); + assertNotNull(confirmedBalanceString); final Integer spendableBalance = (Integer) map.get("spendableBalance"); - Assert.assertNotNull(spendableBalance); + assertNotNull(spendableBalance); final String spendableBalanceString = (String) map.get("spendableBalanceString"); - Assert.assertNotNull(spendableBalanceString); + assertNotNull(spendableBalanceString); } @Test - @Ignore - public void getWalletByLabelTest() throws IOException { + @Disabled + void getWalletByLabelTest() throws IOException { final String accessToken = "Bearer v2x8d5e9e46379dc328b2039a400a12b04ea986689b38107fd84cd339bc89e3fb21"; final String walletLabel = "TBTCWallet"; final Map result = api.getWallets("tbtc"); - Assert.assertNotNull(result); + assertNotNull(result); List wallets = (List) result.get("wallets"); - Assert.assertNotNull(wallets); + assertNotNull(wallets); boolean walletFound = false; for(Object o : wallets) { if(!(o instanceof Map)) { @@ -175,73 +176,73 @@ public void getWalletByLabelTest() throws IOException { } } - Assert.assertTrue(walletFound); + assertTrue(walletFound); } - @Ignore + @Disabled @Test - public void getWalletTest() throws IOException { + void getWalletTest() throws IOException { final String accessToken = "Bearer v2x8d5e9e46379dc328b2039a400a12b04ea986689b38107fd84cd339bc89e3fb21"; final String walletId = "5b20e3a9266bbe80095757489d84a6bb"; final Map result = api.getWalletById("tbtc", walletId); - Assert.assertNotNull(result); - Assert.assertTrue(result instanceof LinkedHashMap); + assertNotNull(result); + assertTrue(result instanceof LinkedHashMap); final String id = (String) result.get("id"); - Assert.assertEquals("5b20e3a9266bbe80095757489d84a6bb", id); + assertEquals("5b20e3a9266bbe80095757489d84a6bb", id); final String coin = (String) result.get("coin"); - Assert.assertNotNull(coin); - Assert.assertEquals("tbtc", coin); + assertNotNull(coin); + assertEquals("tbtc", coin); final String label = (String) result.get("label"); - Assert.assertEquals("TBTCWallet", label); + assertEquals("TBTCWallet", label); } - @Ignore + @Disabled @Test - public void getWalletByAIdTest() throws IOException { + void getWalletByAIdTest() throws IOException { final String accessToken = "Bearer v2x8d5e9e46379dc328b2039a400a12b04ea986689b38107fd84cd339bc89e3fb21"; String address = "5b20e3a9266bbe80095757489d84a6bb"; final Map result = api.getWalletById("tbtc", address); - Assert.assertNotNull(result); - Assert.assertTrue(result instanceof LinkedHashMap); + assertNotNull(result); + assertTrue(result instanceof LinkedHashMap); String id = (String) result.get("id"); - Assert.assertEquals("5b20e3a9266bbe80095757489d84a6bb", id); + assertEquals("5b20e3a9266bbe80095757489d84a6bb", id); String coin = (String) result.get("coin"); - Assert.assertNotNull(coin); - Assert.assertEquals("tbtc", coin); + assertNotNull(coin); + assertEquals("tbtc", coin); final String label = (String) result.get("label"); - Assert.assertEquals("TBTCWallet", label); + assertEquals("TBTCWallet", label); final Map receiveAddressMap = (Map) result.get("receiveAddress"); - Assert.assertNotNull(receiveAddressMap); - Assert.assertFalse(receiveAddressMap.isEmpty()); + assertNotNull(receiveAddressMap); + assertFalse(receiveAddressMap.isEmpty()); id = (String) receiveAddressMap.get("id"); - Assert.assertNotNull(id); - Assert.assertTrue(id.startsWith("5b")); + assertNotNull(id); + assertTrue(id.startsWith("5b")); String walletId = (String) receiveAddressMap.get("wallet"); - Assert.assertNotNull(walletId); - Assert.assertEquals("5b20e3a9266bbe80095757489d84a6bb", walletId); + assertNotNull(walletId); + assertEquals("5b20e3a9266bbe80095757489d84a6bb", walletId); coin = (String)receiveAddressMap.get("coin"); - Assert.assertNotNull(coin); - Assert.assertEquals("tbtc", coin); + assertNotNull(coin); + assertEquals("tbtc", coin); address = (String)receiveAddressMap.get("address"); - Assert.assertNotNull(address); + assertNotNull(address); log.info("Address = {}", address); } @Test - @Ignore("Local instance of bitgo-express test environment is required to run") - public void sendCoinsTest() { + @Disabled("Local instance of bitgo-express test environment is required to run") + void sendCoinsTest() { try { IBitgoAPI localapi = RestProxyFactory.createProxy(IBitgoAPI.class, "http://localhost:3080/"); final String coin = "tbtc"; @@ -254,19 +255,19 @@ public void sendCoinsTest() { String accessToken = "Bearer v2x8d5e9e46379dc328b2039a400a12b04ea986689b38107fd84cd339bc89e3fb21"; String contentType = "application/json"; Map result = localapi.sendCoins(coin, id, request); - Assert.assertNotNull(result); + assertNotNull(result); Object statusO = result.get("status"); - Assert.assertTrue(statusO instanceof String); + assertTrue(statusO instanceof String); String status = (String) statusO; - Assert.assertNotNull(status); - Assert.assertEquals("signed", status); + assertNotNull(status); + assertEquals("signed", status); Object tx = result.get("tx"); - Assert.assertNotNull(tx); + assertNotNull(tx); Object txid = result.get("txid"); - Assert.assertNotNull(txid); + assertNotNull(txid); } catch (Exception e) { log.error("Error", e); } diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/wallets/bitgo/v2/BitgoWalletTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/wallets/bitgo/v2/BitgoWalletTest.java index 6f4107e39..ba1753a1b 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/wallets/bitgo/v2/BitgoWalletTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoin/wallets/bitgo/v2/BitgoWalletTest.java @@ -6,10 +6,9 @@ import com.generalbytes.batm.common.currencies.CryptoCurrency; import com.generalbytes.batm.server.extensions.ICanSendMany; import org.assertj.core.api.Assertions; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import si.mazi.rescu.RestProxyFactory; @@ -18,8 +17,10 @@ import java.util.Arrays; import java.util.List; +import static org.junit.jupiter.api.Assertions.*; -public class BitgoWalletTest { + +class BitgoWalletTest { private static final Logger log = LoggerFactory.getLogger(BitgoWalletTest.class); @@ -41,8 +42,8 @@ private static void setLoggerLevel(String name, String level) { } } - @BeforeClass - public static void setup() { + @BeforeEach + void setUp() { setLoggerLevel("batm", "trace"); setLoggerLevel("si.mazi.rescu","trace"); @@ -60,8 +61,8 @@ public static void setup() { } @Test - @Ignore - public void getCryptAddressTest() { + @Disabled + void getCryptAddressTest() { String coin = CryptoCurrency.TBTC.getCode(); String scheme = "https"; String host = "test.bitgo.com"; @@ -72,13 +73,13 @@ public void getCryptAddressTest() { final BitgoWallet remotewallet = new BitgoWallet(scheme, host, port, token, walletId, walletPassphrase, 2); final String address = remotewallet.getCryptoAddress(coin); - Assert.assertNotNull(address); - Assert.assertEquals("2N2WR6aVSEgq5ZLTED9vHvCWFdAMf6yhebd", address); + assertNotNull(address); + assertEquals("2N2WR6aVSEgq5ZLTED9vHvCWFdAMf6yhebd", address); } @Test - @Ignore - public void getCryptBalanceTest() { + @Disabled + void getCryptBalanceTest() { String coin = CryptoCurrency.TBTC.getCode(); String scheme = "https"; String host = "test.bitgo.com"; @@ -89,13 +90,13 @@ public void getCryptBalanceTest() { final BitgoWallet remotewallet = new BitgoWallet(scheme, host, port, token, walletId, walletPassphrase, 2); BigDecimal balance = remotewallet.getCryptoBalance(coin); - Assert.assertNotNull(balance); + assertNotNull(balance); log.info("balance = {}", balance); } @Test - @Ignore("Local instance of bitgo-express is required to run") - public void sendCoinsTest() { + @Disabled("Local instance of bitgo-express is required to run") + void sendCoinsTest() { String destinationAddress = "2N5q4MwNSUxbAtaidhRgkiDrbwVR4yCZDhi"; String coin = CryptoCurrency.TBTC.getCode(); Integer amountInt = 10000; @@ -107,8 +108,8 @@ public void sendCoinsTest() { } @Test - @Ignore("Local instance of bitgo-express is required to run") - public void sendManyTest() { + @Disabled("Local instance of bitgo-express is required to run") + void sendManyTest() { String coin = CryptoCurrency.TBTC.getCode(); Integer amountInt = 10000; BigDecimal amount = BigDecimal.valueOf(amountInt).divide(Converters.TBTC); @@ -122,8 +123,8 @@ public void sendManyTest() { } @Test - @Ignore("Local instance of bitgo-express is required to run") - public void sendCoinsNumBlocksTest() { + @Disabled("Local instance of bitgo-express is required to run") + void sendCoinsNumBlocksTest() { String destinationAddress = "2N5q4MwNSUxbAtaidhRgkiDrbwVR4yCZDhi"; String coin = CryptoCurrency.TBTC.getCode(); Integer amountInt = 10000; @@ -135,7 +136,7 @@ public void sendCoinsNumBlocksTest() { } @Test - public void convertBalance() { + void convertBalance() { Assertions.assertThat(wallet.toSatoshis(new BigDecimal("15.50581145"), CryptoCurrency.USDT.getCode())).isEqualTo("15505811"); Assertions.assertThat(wallet.toSatoshis(new BigDecimal("15.50581145"), CryptoCurrency.USDTTRON.getCode())).isEqualTo("15505811"); @@ -151,10 +152,10 @@ public void convertBalance() { Assertions.assertThat(wallet.fromSatoshis(CryptoCurrency.BTC.getCode(), new BigDecimal("1.000"))).isEqualByComparingTo("0.00000001"); Assertions.assertThat(wallet.fromSatoshis(CryptoCurrency.BTC.getCode(), new BigDecimal("1.999"))).isEqualByComparingTo("0.00000001"); - Exception exception = Assert.assertThrows(NullPointerException.class, () -> wallet.fromSatoshis("unknown", BigDecimal.ONE)); + Exception exception = assertThrows(NullPointerException.class, () -> wallet.fromSatoshis("unknown", BigDecimal.ONE)); Assertions.assertThat(exception.getMessage()).contains("not supported"); for (String cryptoCurrency : wallet.getCryptoCurrencies()) { - Assert.assertEquals(BigDecimal.ONE, wallet.fromSatoshis(cryptoCurrency, new BigDecimal(wallet.toSatoshis(BigDecimal.ONE, cryptoCurrency)))); + assertEquals(BigDecimal.ONE, wallet.fromSatoshis(cryptoCurrency, new BigDecimal(wallet.toSatoshis(BigDecimal.ONE, cryptoCurrency)))); } } } diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoincash/BitcoinCashAddressInvalidTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoincash/BitcoinCashAddressInvalidTest.java index cb0f90349..ca8828784 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoincash/BitcoinCashAddressInvalidTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoincash/BitcoinCashAddressInvalidTest.java @@ -1,93 +1,95 @@ package com.generalbytes.batm.server.extensions.extra.bitcoincash; import com.generalbytes.batm.server.coinutil.AddressFormatException; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; -public class BitcoinCashAddressInvalidTest { +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class BitcoinCashAddressInvalidTest { private static final BitcoinCashAddressValidator BCH_ADDRESS_VALIDATOR = new BitcoinCashAddressValidator(); private static final SlpAddressValidator SLP_ADDRESS_VALIDATOR = new SlpAddressValidator(); private void test(String in) throws AddressFormatException { - Assert.assertFalse("bitcoincash invalid", BCH_ADDRESS_VALIDATOR.isAddressValid(in)); - Assert.assertFalse("simpleledger invalid", SLP_ADDRESS_VALIDATOR.isAddressValid(in)); + assertFalse(BCH_ADDRESS_VALIDATOR.isAddressValid(in), "bitcoincash invalid"); + assertFalse(SLP_ADDRESS_VALIDATOR.isAddressValid(in), "simpleledger invalid"); BitcoinCashAddress.valueOf(in); } - @Test(expected = AddressFormatException.class) - public void nullInput() throws AddressFormatException { - test(null); + @Test + void nullInput() { + assertThrows(AddressFormatException.class, () -> test(null)); } - @Test(expected = AddressFormatException.class) - public void emptyInput() throws AddressFormatException { - test(""); + @Test + void emptyInput() { + assertThrows(AddressFormatException.class, () -> test("")); } - @Test(expected = AddressFormatException.class) - public void invalidInput1() throws AddressFormatException { - test("1"); + @Test + void invalidInput1() { + assertThrows(AddressFormatException.class, () -> test("1")); } - @Test(expected = AddressFormatException.class) - public void invalidInput1111() throws AddressFormatException { - test("1111111111111111111111111111111111"); + @Test + void invalidInput1111() { + assertThrows(AddressFormatException.class, () -> test("1111111111111111111111111111111111")); } - @Test(expected = AddressFormatException.class) - public void invalidInput3() throws AddressFormatException { - test("3"); + @Test + void invalidInput3() { + assertThrows(AddressFormatException.class, () -> test("3")); } - @Test(expected = AddressFormatException.class) - public void invalidInput3333() throws AddressFormatException { - test("3333333333333333333333333333333333"); + @Test + void invalidInput3333() { + assertThrows(AddressFormatException.class, () -> test("3333333333333333333333333333333333")); } - @Test(expected = AddressFormatException.class) - public void invalidInput0() throws AddressFormatException { - test("0"); + @Test + void invalidInput0() { + assertThrows(AddressFormatException.class, () -> test("0")); } - @Test(expected = AddressFormatException.class) - public void invalidInputCol() throws AddressFormatException { - test(":"); + @Test + void invalidInputCol() { + assertThrows(AddressFormatException.class, () -> test(":")); } - @Test(expected = AddressFormatException.class) - public void invalidInputColQqqq() throws AddressFormatException { - test("bitcoincash:qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"); + @Test + void invalidInputColQqqq() { + assertThrows(AddressFormatException.class, () -> test("bitcoincash:qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq")); } - @Test(expected = AddressFormatException.class) - public void invalidInputQqqq() throws AddressFormatException { - test("qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"); + @Test + void invalidInputQqqq() { + assertThrows(AddressFormatException.class, () -> test("qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq")); } - @Test(expected = AddressFormatException.class) - public void invalidInputColPppp() throws AddressFormatException { - test("bitcoincash:pppppppppppppppppppppppppppppppppppppppppp"); + @Test + void invalidInputColPppp() { + assertThrows(AddressFormatException.class, () -> test("bitcoincash:pppppppppppppppppppppppppppppppppppppppppp")); } - @Test(expected = AddressFormatException.class) - public void invalidInputPppp() throws AddressFormatException { - test("pppppppppppppppppppppppppppppppppppppppppp"); + @Test + void invalidInputPppp() { + assertThrows(AddressFormatException.class, () -> test("pppppppppppppppppppppppppppppppppppppppppp")); } - @Test(expected = AddressFormatException.class) - public void invalidInputSlpPppp() throws AddressFormatException { - test("simpleledger:pppppppppppppppppppppppppppppppppppppppppp"); + @Test + void invalidInputSlpPppp() { + assertThrows(AddressFormatException.class, () -> test("simpleledger:pppppppppppppppppppppppppppppppppppppppppp")); } - @Test(expected = AddressFormatException.class) - public void invalidInputSlpQqqq() throws AddressFormatException { - test("simpleledger:qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"); + @Test + void invalidInputSlpQqqq() { + assertThrows(AddressFormatException.class, () -> test("simpleledger:qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq")); } - @Test(expected = AddressFormatException.class) - public void invalidInputSlpE() throws AddressFormatException { - test("simpleledger:"); + @Test + void invalidInputSlpE() { + assertThrows(AddressFormatException.class, () -> test("simpleledger:")); } } \ No newline at end of file diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoincash/BitcoinCashAddressTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoincash/BitcoinCashAddressTest.java index 687dc18c6..bdcbf6100 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoincash/BitcoinCashAddressTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/bitcoincash/BitcoinCashAddressTest.java @@ -1,53 +1,44 @@ package com.generalbytes.batm.server.extensions.extra.bitcoincash; import com.generalbytes.batm.server.coinutil.AddressFormatException; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; -import java.util.Arrays; -import java.util.Collection; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; -@RunWith(Parameterized.class) -public class BitcoinCashAddressTest { +class BitcoinCashAddressTest { private static final BitcoinCashAddressValidator BCH_ADDRESS_VALIDATOR = new BitcoinCashAddressValidator(); private static final SlpAddressValidator SLP_ADDRESS_VALIDATOR = new SlpAddressValidator(); - private final BitcoinCashAddress input; - @Parameterized.Parameters - public static Collection getTestData() throws AddressFormatException { - return Arrays.asList(new Object[][]{ + public static Object[][] getTestData() throws AddressFormatException { + return new Object[][]{ {new BitcoinCashAddress("1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu", "qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a", "qpm2qsznhks23z7629mms6s4cwef74vcwvg3pncxyr")}, {new BitcoinCashAddress("1KXrWXciRDZUpQwQmuM1DbwsKDLYAYsVLR", "qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy", "qr95sy3j9xwd2ap32xkykttr4cvcu7as4yrm8zkfz6")}, {new BitcoinCashAddress("16w1D5WRVKJuZUsSRzdLp9w3YGcgoxDXb", "qqq3728yw0y47sqn6l2na30mcw6zm78dzqre909m2r", "qqq3728yw0y47sqn6l2na30mcw6zm78dzq0zw5sm5a")}, {new BitcoinCashAddress("3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC", "ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq", "ppm2qsznhks23z7629mms6s4cwef74vcwvl5uul9l7")}, {new BitcoinCashAddress("3LDsS579y7sruadqu11beEJoTjdFiFCdX4", "pr95sy3j9xwd2ap32xkykttr4cvcu7as4yc93ky28e", "pr95sy3j9xwd2ap32xkykttr4cvcu7as4y576d32e8")}, {new BitcoinCashAddress("31nwvkZwyPdgzjBJZXfDmSWsC4ZLKpYyUw", "pqq3728yw0y47sqn6l2na30mcw6zm78dzq5ucqzc37", "pqq3728yw0y47sqn6l2na30mcw6zm78dzqc8nmhc0q")}, - }); + }; } - // @Parameterized.Parameters annotated method results are passed here - public BitcoinCashAddressTest(BitcoinCashAddress input) { - this.input = input; + @ParameterizedTest + @MethodSource("getTestData") + void convert(BitcoinCashAddress input) throws AddressFormatException { + assertValues(input, BitcoinCashAddress.valueOf(input.getLegacy())); + assertValues(input, BitcoinCashAddress.valueOf(input.getBitcoincash(true))); + assertValues(input, BitcoinCashAddress.valueOf(input.getBitcoincash(false))); + assertValues(input, BitcoinCashAddress.valueOf(input.getSimpleledger(true))); + assertValues(input, BitcoinCashAddress.valueOf(input.getSimpleledger(false))); } - @Test - public void convert() throws AddressFormatException { - assertEquals(input, BitcoinCashAddress.valueOf(input.getLegacy())); - assertEquals(input, BitcoinCashAddress.valueOf(input.getBitcoincash(true))); - assertEquals(input, BitcoinCashAddress.valueOf(input.getBitcoincash(false))); - assertEquals(input, BitcoinCashAddress.valueOf(input.getSimpleledger(true))); - assertEquals(input, BitcoinCashAddress.valueOf(input.getSimpleledger(false))); - } - - private void assertEquals(BitcoinCashAddress expected, BitcoinCashAddress actual) { - Assert.assertEquals("legacy", expected.getLegacy(), actual.getLegacy()); - Assert.assertEquals("bitcoincash", expected.getBitcoincash(true), actual.getBitcoincash(true)); - Assert.assertEquals("bitcoincash", expected.getBitcoincash(false), actual.getBitcoincash(false)); - Assert.assertEquals("simpleledger", expected.getSimpleledger(true), actual.getSimpleledger(true)); - Assert.assertEquals("simpleledger", expected.getSimpleledger(false), actual.getSimpleledger(false)); - Assert.assertTrue("bitcoincash valid", BCH_ADDRESS_VALIDATOR.isAddressValid(actual.getBitcoincash(false))); - Assert.assertTrue("simpleledger valid", SLP_ADDRESS_VALIDATOR.isAddressValid(actual.getSimpleledger(false))); + private void assertValues(BitcoinCashAddress expected, BitcoinCashAddress actual) { + assertEquals(expected.getLegacy(), actual.getLegacy(), "legacy"); + assertEquals(expected.getBitcoincash(true), actual.getBitcoincash(true), "bitcoincash"); + assertEquals(expected.getBitcoincash(false), actual.getBitcoincash(false), "bitcoincash"); + assertEquals(expected.getSimpleledger(true), actual.getSimpleledger(true), "simpleledger"); + assertEquals(expected.getSimpleledger(false), actual.getSimpleledger(false), "simpleledger"); + assertTrue(BCH_ADDRESS_VALIDATOR.isAddressValid(actual.getBitcoincash(false)), "bitcoincash valid"); + assertTrue(SLP_ADDRESS_VALIDATOR.isAddressValid(actual.getSimpleledger(false)), "simpleledger valid"); } } \ No newline at end of file diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/cardano/CardanoAddressValidatorTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/cardano/CardanoAddressValidatorTest.java index e6729293d..aeb0665b9 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/cardano/CardanoAddressValidatorTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/cardano/CardanoAddressValidatorTest.java @@ -1,14 +1,14 @@ package com.generalbytes.batm.server.extensions.extra.cardano; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; -public class CardanoAddressValidatorTest { +class CardanoAddressValidatorTest { @Test - public void isAddressValid() { + void isAddressValid() { CardanoAddressValidator v = new CardanoAddressValidator(); assertTrue(v.isAddressValid("addr1u8pcjgmx7962w6hey5hhsd502araxp26kdtgagakhaqtq8sxy9w7g")); assertTrue(v.isAddressValid("addr1g9u5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5evph3wczvf2kd5vam")); diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/common/QueryableWalletPaymentSupportTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/common/QueryableWalletPaymentSupportTest.java index 8026d02e2..3643dc72d 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/common/QueryableWalletPaymentSupportTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/common/QueryableWalletPaymentSupportTest.java @@ -6,20 +6,20 @@ import com.generalbytes.batm.server.extensions.extra.ethereum.UsdtPaymentSupport; import com.generalbytes.batm.server.extensions.payment.PaymentRequest; import com.generalbytes.batm.server.extensions.payment.ReceivedAmount; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.math.BigDecimal; import java.util.Set; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; -public class QueryableWalletPaymentSupportTest { +class QueryableWalletPaymentSupportTest { private ReceivedAmount received; private final QueryableWalletPaymentSupport paymentSupport = new UsdtPaymentSupport(); @Test - public void invalidAmount() { + void invalidAmount() { received = ReceivedAmount.ZERO; PaymentRequest request = getPaymentRequest("1", "0", false); paymentSupport.poll(request); @@ -30,7 +30,7 @@ public void invalidAmount() { } @Test - public void exactAmount() { + void exactAmount() { PaymentRequest request = getPaymentRequest("1", "0", false); received = new ReceivedAmount(new BigDecimal("1"), 0); paymentSupport.poll(request); @@ -41,32 +41,32 @@ public void exactAmount() { } @Test - public void inTolerance() { + void inTolerance() { test("1", "0.2", false, "0.85", PaymentRequest.STATE_SEEN_IN_BLOCK_CHAIN); } @Test - public void notInTolerance() { + void notInTolerance() { test("1", "0.1", false, "0.85", PaymentRequest.STATE_TRANSACTION_INVALID); } @Test - public void overInTolerance() { + void overInTolerance() { test("1", "0.2", false, "1.15", PaymentRequest.STATE_SEEN_IN_BLOCK_CHAIN); } @Test - public void overNotInTolerance() { + void overNotInTolerance() { test("1", "0.1", false, "1.15", PaymentRequest.STATE_TRANSACTION_INVALID); } @Test - public void overInToleranceWithOverageAllowed() { + void overInToleranceWithOverageAllowed() { test("1", "0.2", true, "1.15", PaymentRequest.STATE_SEEN_IN_BLOCK_CHAIN); } @Test - public void overNotInToleranceWithOverageAllowed() { + void overNotInToleranceWithOverageAllowed() { test("1", "0.1", true, "1.15", PaymentRequest.STATE_SEEN_IN_BLOCK_CHAIN); } diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/common/RPCBlockchainWatcherTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/common/RPCBlockchainWatcherTest.java index 66bc22885..d0cf76acc 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/common/RPCBlockchainWatcherTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/common/RPCBlockchainWatcherTest.java @@ -2,19 +2,19 @@ import com.generalbytes.batm.common.currencies.CryptoCurrency; import com.generalbytes.batm.server.extensions.payment.IBlockchainWatcherTransactionListener; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.MalformedURLException; -public class RPCBlockchainWatcherTest { +class RPCBlockchainWatcherTest { private static final Logger log = LoggerFactory.getLogger(RPCBlockchainWatcherTest.class); - @Ignore + @Disabled @Test - public void test() { + void test() { try { RPCClient rpcClient = new RPCClient(CryptoCurrency.BCH.getCode(), "..."); final RPCBlockchainWatcher w = new RPCBlockchainWatcher(rpcClient); diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/dash/sources/coinmarketcap/CoinmarketcapAPITest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/dash/sources/coinmarketcap/CoinmarketcapAPITest.java index 898d4ecdd..20206da18 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/dash/sources/coinmarketcap/CoinmarketcapAPITest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/dash/sources/coinmarketcap/CoinmarketcapAPITest.java @@ -1,9 +1,8 @@ package com.generalbytes.batm.server.extensions.extra.dash.sources.coinmarketcap; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import si.mazi.rescu.HttpStatusIOException; @@ -13,37 +12,39 @@ import java.math.BigDecimal; import java.util.Map; -@Ignore("Requires external systems connectivity") -public class CoinmarketcapAPITest { +import static org.junit.jupiter.api.Assertions.*; + +@Disabled("Requires external systems connectivity") +class CoinmarketcapAPITest { private static final Logger log = LoggerFactory.getLogger("batm.master.extensions.CoinmarketcapAPITest"); public static final String API_KEY = "ba025ccf-579b-40e4-be05-cbcebd83c476"; private static ICoinmarketcapAPI api; - @BeforeClass - public static void setup() { + @BeforeEach + void setUp() { api = RestProxyFactory.createProxy(ICoinmarketcapAPI.class, "https://sandbox-api.coinmarketcap.com"); } @Test - public void jsonTest() throws IOException { + void jsonTest() throws IOException { Map> r = RestProxyFactory.createProxy(TestCoinmarketcapAPI.class, "https://sandbox-api.coinmarketcap.com") .getTicker(API_KEY, "BTC", "USD"); - Assert.assertEquals(2, r.size()); - Assert.assertEquals(5, r.get("status").size()); - Assert.assertNull(r.get("status").get("error_message")); - Assert.assertEquals(1, r.get("data").size()); - Assert.assertNotNull(r.get("data").get("BTC")); + assertEquals(2, r.size()); + assertEquals(5, r.get("status").size()); + assertNull(r.get("status").get("error_message")); + assertEquals(1, r.get("data").size()); + assertNotNull(r.get("data").get("BTC")); } @Test - public void nullApiKey() throws IOException { + void nullApiKey() throws IOException { try { api.getTicker(null, "BTC", "USD"); } catch (HttpStatusIOException e) { - Assert.assertTrue(e.getHttpBody().contains("API key missing")); + assertTrue(e.getHttpBody().contains("API key missing")); return; } - Assert.fail(); + fail(); } /** @@ -51,22 +52,22 @@ public void nullApiKey() throws IOException { * of type string is correctly called and that data is received. */ @Test - public void getTickerOneIntegerOneStringParametersTest() throws IOException { + void getTickerOneIntegerOneStringParametersTest() throws IOException { CmcTickerResponse result = api.getTicker(API_KEY, "BTC", "USD"); - Assert.assertNotNull(result); + assertNotNull(result); CmcTickerData data = result.getData().get("BTC"); - Assert.assertNotNull(data); + assertNotNull(data); } @Test - public void getTickerDogecoinTest() throws IOException { + void getTickerDogecoinTest() throws IOException { CmcTickerResponse result = api.getTicker(API_KEY, "DOGE", "USD"); - Assert.assertNotNull(result); + assertNotNull(result); CmcTickerData data = result.getData().get("DOGE"); - Assert.assertNotNull(data); - Assert.assertNotNull(data.getQuote()); + assertNotNull(data); + assertNotNull(data.getQuote()); //CmcTickerQuote quote = (CmcTickerQuote) data.getQuotes(); log.info(data.toString()); @@ -78,12 +79,12 @@ public void getTickerDogecoinTest() throws IOException { * of type string is correctly called and that data is received. */ @Test - public void getTickerOneIntegerOneNullParametersTest() throws IOException { + void getTickerOneIntegerOneNullParametersTest() throws IOException { CmcTickerResponse result = api.getTicker(API_KEY, "BTC", null); - Assert.assertNotNull(result); + assertNotNull(result); CmcTickerData data = result.getData().get("BTC"); - Assert.assertNotNull(data); + assertNotNull(data); } /** @@ -91,22 +92,22 @@ public void getTickerOneIntegerOneNullParametersTest() throws IOException { * and fiat of type String is correctly called and that data and quotes has been received. */ @Test - public void getQuotesTest() throws IOException { + void getQuotesTest() throws IOException { CmcTickerResponse result = api.getTicker(API_KEY, "BTC", "EUR"); - Assert.assertNotNull(result); + assertNotNull(result); CmcTickerData data = result.getData().get("BTC"); - Assert.assertNotNull(data); + assertNotNull(data); final Map quotes = data.getQuote(); - Assert.assertNotNull(quotes); + assertNotNull(quotes); CmcTickerQuote quote = quotes.get("EUR"); - Assert.assertNotNull(quote); + assertNotNull(quote); BigDecimal price = quote.getPrice(); - Assert.assertNotNull(price); - Assert.assertTrue(price.doubleValue() != 0); + assertNotNull(price); + assertTrue(price.doubleValue() != 0); } /** @@ -114,24 +115,24 @@ public void getQuotesTest() throws IOException { * and for particular fiat currency and makes sure that data is provided. */ @Test - public void getQuoteByCurrencySymbolAndFiatCurrencyTest() throws IOException { + void getQuoteByCurrencySymbolAndFiatCurrencyTest() throws IOException { String currency = "BTC"; String fiat = "EUR"; CmcTickerResponse result = api.getTicker(API_KEY, currency, fiat); - Assert.assertNotNull(result); + assertNotNull(result); CmcTickerData data = result.getData().get(currency); - Assert.assertNotNull(data); + assertNotNull(data); final Map quotes = data.getQuote(); - Assert.assertNotNull(quotes); + assertNotNull(quotes); CmcTickerQuote quote = quotes.get(fiat); - Assert.assertNotNull(quote); + assertNotNull(quote); BigDecimal price = quote.getPrice(); - Assert.assertNotNull(price); - Assert.assertTrue(price.doubleValue() != 0); + assertNotNull(price); + assertTrue(price.doubleValue() != 0); } } diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/dash/sources/coinmarketcap/CoinmarketcapRateSourceTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/dash/sources/coinmarketcap/CoinmarketcapRateSourceTest.java index c99dc0eab..c4ba78778 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/dash/sources/coinmarketcap/CoinmarketcapRateSourceTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/dash/sources/coinmarketcap/CoinmarketcapRateSourceTest.java @@ -1,23 +1,24 @@ package com.generalbytes.batm.server.extensions.extra.dash.sources.coinmarketcap; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.math.BigDecimal; +import static org.junit.jupiter.api.Assertions.*; + /** * Class CoinmarketcapRateSourceV2Test tests method getExchangeRateLast() of class CoinmarketcapRateSourceV2 */ -@Ignore("Requires external systems connectivity") -public class CoinmarketcapRateSourceTest { +@Disabled("Requires external systems connectivity") +class CoinmarketcapRateSourceTest { private static final Logger log = LoggerFactory.getLogger("batm.master.extensions.CoinmarketcapRateSourceTest"); - @Test() - public void nullApi() { - Assert.assertNull(new CoinmarketcapRateSource(null, "USD").getExchangeRateLast("BTC", "CZK")); + @Test + void nullApi() { + assertNull(new CoinmarketcapRateSource(null, "USD").getExchangeRateLast("BTC", "CZK")); } /** @@ -25,28 +26,28 @@ public void nullApi() { * with parameter null */ @Test - public void getExchangeRateLastTest() { + void getExchangeRateLastTest() { CoinmarketcapRateSource rateSource = new CoinmarketcapRateSource("ce83e9de-db96-4cdd-871d-cf9eb07a8381", "USD"); final BigDecimal priceUSD = rateSource.getExchangeRateLast("BTC", "USD"); - Assert.assertNotNull(priceUSD); + assertNotNull(priceUSD); log.info("price for btc = " + priceUSD.doubleValue() + " usd"); final BigDecimal priceEUR = rateSource.getExchangeRateLast("BTC", "EUR"); - Assert.assertNotNull(priceEUR); + assertNotNull(priceEUR); log.info("price for btc = " + priceEUR.doubleValue() + " eur"); - Assert.assertNotSame(priceUSD, priceEUR); + assertNotSame(priceUSD, priceEUR); final BigDecimal priceEthEur = rateSource.getExchangeRateLast("ETH", "EUR"); - Assert.assertNotNull(priceEthEur); + assertNotNull(priceEthEur); log.info("price for eth = " + priceEthEur.doubleValue() + " eur"); final BigDecimal priceEthUsd = rateSource.getExchangeRateLast("ETH", "USD"); - Assert.assertNotNull(priceEthUsd); + assertNotNull(priceEthUsd); log.info("price for eth = " + priceEthUsd.doubleValue() + " usd"); - Assert.assertNotSame(priceEthUsd, priceEthEur); + assertNotSame(priceEthUsd, priceEthEur); } } diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/elrond/ElrondAddressValidatorTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/elrond/ElrondAddressValidatorTest.java index d7db7ac56..a655b4f0d 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/elrond/ElrondAddressValidatorTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/elrond/ElrondAddressValidatorTest.java @@ -1,15 +1,15 @@ package com.generalbytes.batm.server.extensions.extra.elrond; +import org.junit.jupiter.api.Test; -import org.junit.Assert; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.assertTrue; -public class ElrondAddressValidatorTest { +class ElrondAddressValidatorTest { @Test - public void addressValidTest() { + void addressValidTest() { ElrondAddressValidator lav = new ElrondAddressValidator(); String address = "erd1kvwvfpn3ncvt5dn8e0rnm8s7qv0u4yz3sl7dqcwuc0vq8533mevqnvvawh"; boolean isValid = lav.isAddressValid(address); - Assert.assertTrue(isValid); + assertTrue(isValid); } } diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/identityverification/IdentityCheckWebhookRunnableTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/identityverification/IdentityCheckWebhookRunnableTest.java index 88efa04e7..b1774d8fb 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/identityverification/IdentityCheckWebhookRunnableTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/identityverification/IdentityCheckWebhookRunnableTest.java @@ -1,16 +1,17 @@ package com.generalbytes.batm.server.extensions.extra.identityverification; import com.generalbytes.batm.server.extensions.aml.verification.IdentityCheckWebhookException; -import org.junit.Test; +import org.junit.jupiter.api.Test; import javax.ws.rs.core.Response; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; -public class IdentityCheckWebhookRunnableTest { +class IdentityCheckWebhookRunnableTest { @Test - public void getResponse() { + void getResponse() { Response ok = IdentityCheckWebhookRunnable.getResponse("label", () -> {}); assertEquals(200, ok.getStatus()); assertNull(ok.getEntity()); diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/identityverification/veriff/VeriffVerificationResultMapperTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/identityverification/veriff/VeriffVerificationResultMapperTest.java index 7734c25b3..5a05cb031 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/identityverification/veriff/VeriffVerificationResultMapperTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/identityverification/veriff/VeriffVerificationResultMapperTest.java @@ -5,22 +5,20 @@ import com.generalbytes.batm.server.extensions.aml.verification.DocumentType; import com.generalbytes.batm.server.extensions.extra.identityverification.veriff.api.webhook.VerificationDecisionWebhookRequest; import com.generalbytes.batm.server.extensions.extra.identityverification.veriff.api.webhook.VerificationDecisionWebhookRequest.Verification.Person; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.LocalDate; import java.time.ZoneId; import java.util.Collections; import java.util.Date; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.*; -public class VeriffVerificationResultMapperTest { +class VeriffVerificationResultMapperTest { private static final VeriffVerificationResultMapper mapper = new VeriffVerificationResultMapper(); @Test - public void testMapping() { + void testMapping() { ApplicantCheckResult result = mapper.mapResult(createRequest()); assertNotNull(result); @@ -44,7 +42,7 @@ public void testMapping() { } @Test - public void testStreetAddress() { + void testStreetAddress() { assertNull(mapStreetAddress(null, null, null)); assertNull(mapStreetAddress("us", null, null)); diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/identityverification/veriff/VeriffWebhookParserTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/identityverification/veriff/VeriffWebhookParserTest.java index ac0f85ad5..7896615f4 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/identityverification/veriff/VeriffWebhookParserTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/identityverification/veriff/VeriffWebhookParserTest.java @@ -3,37 +3,37 @@ import com.generalbytes.batm.server.extensions.aml.verification.IdentityCheckWebhookException; import com.generalbytes.batm.server.extensions.extra.identityverification.veriff.api.webhook.VerificationDecisionWebhookRequest; import com.generalbytes.batm.server.extensions.extra.identityverification.veriff.api.webhook.VerificationEventWebhookRequest; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Paths; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; -public class VeriffWebhookParserTest { +class VeriffWebhookParserTest { private static final VeriffWebhookParser parser = new VeriffWebhookParser(); @Test - public void invalidJson() { + void invalidJson() { assertThrows(IdentityCheckWebhookException.class, () -> test("{invalid")); } @Test - public void unknownType() { + void unknownType() { assertThrows(IdentityCheckWebhookException.class, () -> test("{}")); } @Test - public void event() throws IOException, URISyntaxException, IdentityCheckWebhookException { + void event() throws IOException, URISyntaxException, IdentityCheckWebhookException { AcceptResults results = test(readResource("verification-event.json")); assertEquals("submitted", results.event.action); assertEquals("QWE123", results.event.vendorData); } @Test - public void decision() throws IOException, URISyntaxException, IdentityCheckWebhookException { + void decision() throws IOException, URISyntaxException, IdentityCheckWebhookException { AcceptResults results = test(readResource("decision.json")); assertEquals("MORGAN", results.decision.verification.person.lastName); assertEquals("1234 Snowy Ridge Road, Indiana, 56789 USA", results.decision.verification.person.addresses.get(0).fullAddress); diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/identityverification/veriff/api/VeriffDigestTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/identityverification/veriff/api/VeriffDigestTest.java index e35cdc452..cad6fa8f2 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/identityverification/veriff/api/VeriffDigestTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/identityverification/veriff/api/VeriffDigestTest.java @@ -1,13 +1,13 @@ package com.generalbytes.batm.server.extensions.extra.identityverification.veriff.api; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -public class VeriffDigestTest { +class VeriffDigestTest { @Test - public void digest() { + void digest() { // https://developers.veriff.com/#generating-x-hmac-signature VeriffDigest d = new VeriffDigest("abcdef12-abcd-abcd-abcd-abcdef012345"); String testPayload = "{\"verification\":{\"callback\":\"https://veriff.com\",\"person\":{\"firstName\":\"John\",\"lastName\":\"Smith\"},\"document\":{\"type\":\"PASSPORT\",\"country\":\"EE\"},\"vendorData\":\"unique id of a user\",\"timestamp\":\"2016-05-19T08:30:25.597Z\"}}"; diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/lightningbitcoin/wallets/eclair/EclairWalletTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/lightningbitcoin/wallets/eclair/EclairWalletTest.java index cfa6f1104..c170986e3 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/lightningbitcoin/wallets/eclair/EclairWalletTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/lightningbitcoin/wallets/eclair/EclairWalletTest.java @@ -1,33 +1,33 @@ package com.generalbytes.batm.server.extensions.extra.lightningbitcoin.wallets.eclair; import com.generalbytes.batm.common.currencies.CryptoCurrency; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import java.math.BigDecimal; -public class EclairWalletTest { +class EclairWalletTest { private EclairWallet w = new EclairWallet("http", "localhost", 8090, "6atfe28d"); - @Ignore + @Disabled @Test - public void sendCoins() { + void sendCoins() { String paymentHash = w.sendCoins("lnbc1pws08zjpp5cnjgqj4qapr98uawh58wx6mc6s7vvtrg3ev7ezj4n7xpkztrhlzsdqu2askcmr9wssx7e3q2dshgmmndp5scqzysxqrrssug40fe2sj24n848p69w8093y0j5fl809qvxhjultkudcnnn3x8r8u3l5xq0ulllxdcthvzp5p2x3f3zrzfs23gmvvxkauewx2vqqnjcp629yl5", new BigDecimal("0.00000001"), CryptoCurrency.LBTC.getCode(), ""); System.out.println(paymentHash); } - @Ignore + @Disabled @Test - public void getCryptoAddress() { + void getCryptoAddress() { String a = w.getCryptoAddress("LBTC"); System.out.println(a); } - @Ignore + @Disabled @Test - public void getCryptoBalance() { + void getCryptoBalance() { BigDecimal cryptoBalance = w.getCryptoBalance(CryptoCurrency.LBTC.getCode()); System.out.println(cryptoBalance); } diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/lightningbitcoin/wallets/lnd/LndWalletTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/lightningbitcoin/wallets/lnd/LndWalletTest.java index 84a1c7ab7..cede47b1a 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/lightningbitcoin/wallets/lnd/LndWalletTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/lightningbitcoin/wallets/lnd/LndWalletTest.java @@ -2,13 +2,13 @@ import com.generalbytes.batm.common.currencies.CryptoCurrency; import com.generalbytes.batm.server.extensions.IWalletInformation; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import java.math.BigDecimal; import java.security.GeneralSecurityException; -public class LndWalletTest { +class LndWalletTest { private LndWallet w; @@ -16,34 +16,34 @@ public LndWalletTest() throws GeneralSecurityException { w = new LndWallet("https://localhost:8080/", "0201036C6E6402CF01030A104B6A274FD380DE6103954B40555DB71C1201301A160A0761646472657373120472656164120577726974651A130A04696E666F120472656164120577726974651A170A08696E766F69636573120472656164120577726974651A160A076D657373616765120472656164120577726974651A170A086F6666636861696E120472656164120577726974651A160A076F6E636861696E120472656164120577726974651A140A057065657273120472656164120577726974651A120A067369676E6572120867656E657261746500000620E52BA8D3D646C5662EEFE5CF04691CA6924E58C791CAF6BB29C3CFFB485E0815", "2D2D2D2D2D424547494E2043455254494649434154452D2D2D2D2D0A4D49494239544343415A71674177494241674952414B2F61774F7143625A4C6866776E364768582F4C4A3877436759494B6F5A497A6A3045417749774E6A45660A4D4230474131554543684D576247356B494746316447396E5A57356C636D46305A57516759325679644445544D4245474131554541784D4B63485A35614735680A62433177597A4165467730784F5441334D4467784E4449304D7A4A61467730794D4441354D4445784E4449304D7A4A614D445978487A416442674E5642416F540A466D78755A434268645852765A3256755A584A686447566B49474E6C636E5178457A415242674E5642414D54436E4232655768755957777463474D77575441540A42676371686B6A4F5051494242676771686B6A4F50514D4242774E43414151434B7A5232773459796A6E4351582B77496F4639356C68326A6B324476514E5A480A62593045686B385A7955474F6C78447154334957625348466344524C77494F636C30547A44687031744A7857536E6B7473377A446F3447494D4947464D4134470A41315564447745422F775145417749437044415042674E5648524D4241663845425441444151482F4D47494741315564455152624D466D43436E4232655768750A5957777463474F4343577876593246736147397A64494945645735706549494B64573570654842685932746C64496345667741414159635141414141414141410A41414141414141414141414141596345774B677A794963512F6F41414141414141414479472B76776E64704D6244414B42676771686B6A4F5051514441674E4A0A41444247416945416F557858736E342B4172786B76364F7A466F70454830762F6C55695670616F31366E35436B536755625073434951436B35443555596D356A0A513170443769617A556C4E717856714D346B5270334F7276782F5936504753674B513D3D0A2D2D2D2D2D454E442043455254494649434154452D2D2D2D2D0A", "5"); } - @Ignore + @Disabled @Test - public void sendCoins() { + void sendCoins() { IWalletInformation i = w.getWalletInformation(); String paymentHash = w.sendCoins("LNBC1U1PWJMJJNPP5YRDV8EPPK74UZ69QVHK940EHE9469H8GHXE626MZGCLZ202REZKQDPY2PKXZ7FQVYSXWCTDV5SX7E3QWD3HYCT5VD5QCQZPGQNL8L0227LDNLEJA3HQWUFHF788ADMD640YKFZ8A9FAGYG4RQE7XGC4CXFMTVU8SAWPE3WVU8WNUHW52R6LSD4797RZ0DPMPTHH3K0CQ378HK2", new BigDecimal("0.000001"), CryptoCurrency.LBTC.getCode(), "R23V4C"); System.out.println(paymentHash); } - @Ignore + @Disabled @Test - public void getCryptoAddress() { + void getCryptoAddress() { String a = w.getCryptoAddress("LBTC"); System.out.println(a); String i = w.getInvoice(new BigDecimal("0.0001"), "LBTC", 1000l, "test invoice"); System.out.println(i); } - @Ignore + @Disabled @Test - public void getCryptoBalance() { + void getCryptoBalance() { BigDecimal cryptoBalance = w.getCryptoBalance(CryptoCurrency.LBTC.getCode()); System.out.println(cryptoBalance); } - @Ignore + @Disabled @Test - public void receive() { + void receive() { String invoice = w.getInvoice(new BigDecimal("0.00000003"), CryptoCurrency.LBTC.getCode(), 1000l, "test"); System.out.println(invoice); System.out.println(w.getReceivedAmount(invoice, "LBTC")); diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/litecoin/LitecoinAddressValidatorTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/litecoin/LitecoinAddressValidatorTest.java index d6377fd6a..f97f03b51 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/litecoin/LitecoinAddressValidatorTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/litecoin/LitecoinAddressValidatorTest.java @@ -1,15 +1,16 @@ package com.generalbytes.batm.server.extensions.extra.litecoin; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; -public class LitecoinAddressValidatorTest { +import static org.junit.jupiter.api.Assertions.assertTrue; + +class LitecoinAddressValidatorTest { @Test - public void addressValidTest() { + void addressValidTest() { LitecoinAddressValidator lav = new LitecoinAddressValidator(); String address = "MMR2PV6EqX617NYzVsztfMFRZTr6zDHWQx"; boolean isValid = lav.isAddressValid(address); - Assert.assertTrue(isValid); + assertTrue(isValid); } } diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/nano/NanoAddressValidatorTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/nano/NanoAddressValidatorTest.java index 1a005ea6c..d67d50a30 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/nano/NanoAddressValidatorTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/nano/NanoAddressValidatorTest.java @@ -2,21 +2,22 @@ import com.generalbytes.batm.common.currencies.CryptoCurrency; import com.generalbytes.batm.server.extensions.extra.nano.util.NanoUtil; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author Karl Oczadly */ -public class NanoAddressValidatorTest { +class NanoAddressValidatorTest { private static final NanoAddressValidator VALIDATOR = new NanoAddressValidator( new NanoExtensionContext(CryptoCurrency.NANO, null, NanoUtil.NANO)); @Test - public void testValidAddresses() { + void testValidAddresses() { assertTrue(VALIDATOR.isAddressValid("nano:nano_396sch48s3jmzq1bk31pxxpz64rn7joj38emj4ueypkb9p9mzrym34obze6c")); assertTrue(VALIDATOR.isAddressValid("nano_396sch48s3jmzq1bk31pxxpz64rn7joj38emj4ueypkb9p9mzrym34obze6c")); assertTrue(VALIDATOR.isAddressValid("nano_34qjpc8t1u6wnb584pc4iwsukwa8jhrobpx4oea5gbaitnqafm6qsgoacpiz")); @@ -25,7 +26,7 @@ public void testValidAddresses() { } @Test - public void testInvalidAddresses() { + void testInvalidAddresses() { assertFalse(VALIDATOR.isAddressValid("nano_396sch48s3jmzq1bk312xxpz64rn7joj38emj4ueypkb9p9mzrym34obze6c")); assertFalse(VALIDATOR.isAddressValid("ban:ban_396sch48s3jmzq1bk31pxxpz64rn7joj38emj4ueypkb9p9mzrym34obze6c")); assertFalse(VALIDATOR.isAddressValid("ban_396sch48s3jmzq1bk31pxxpz64rn7joj38emj4ueypkb9p9mzrym34obze6c")); diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/nano/NanoUtilTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/nano/NanoUtilTest.java index f78e786f1..d08e3f353 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/nano/NanoUtilTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/nano/NanoUtilTest.java @@ -1,25 +1,25 @@ package com.generalbytes.batm.server.extensions.extra.nano; import com.generalbytes.batm.server.extensions.extra.nano.util.NanoUtil; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.math.BigDecimal; import java.math.BigInteger; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; /** * @author Karl Oczadly */ -public class NanoUtilTest { +class NanoUtilTest { static final NanoUtil UTIL = NanoUtil.NANO; static final String ACCOUNT = "nano_34qjpc8t1u6wnb584pc4iwsukwa8jhrobpx4oea5gbaitnqafm6qsgoacpiz"; @Test - public void testParseAddress() { + void testParseAddress() { assertEquals(ACCOUNT, UTIL.parseAddress( // Standard nano address "nano_34qjpc8t1u6wnb584pc4iwsukwa8jhrobpx4oea5gbaitnqafm6qsgoacpiz")); assertEquals(ACCOUNT, UTIL.parseAddress( // Standard xrb address @@ -31,7 +31,7 @@ public void testParseAddress() { } @Test - public void testAmountToRaw() { + void testAmountToRaw() { assertEquals(new BigInteger("1000000000000000000000000000000"), UTIL.amountToRaw(BigDecimal.ONE)); assertEquals(new BigInteger("8029000000000000000000000000000001"), @@ -43,7 +43,7 @@ public void testAmountToRaw() { } @Test - public void testAmountFromRaw() { + void testAmountFromRaw() { assertEquals(0, BigDecimal.ONE.compareTo( UTIL.amountFromRaw(new BigInteger("1000000000000000000000000000000")))); assertEquals(0, new BigDecimal("8029.000000000000000000000000000001").compareTo( @@ -55,7 +55,7 @@ public void testAmountFromRaw() { } @Test - public void testInvalidValues() { + void testInvalidValues() { try { UTIL.validateAmount(new BigDecimal("-1")); fail(); diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/nano/dev/TestNodeWallet.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/nano/dev/TestNodeWallet.java index fe16eb09e..4ac683cbe 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/nano/dev/TestNodeWallet.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/nano/dev/TestNodeWallet.java @@ -6,7 +6,7 @@ import com.generalbytes.batm.server.extensions.extra.nano.rpc.NanoRpcClient; import com.generalbytes.batm.server.extensions.extra.nano.wallet.node.NanoNodeWallet; import com.generalbytes.batm.server.extensions.payment.ReceivedAmount; -import org.junit.Ignore; +import org.junit.jupiter.api.Disabled; import java.math.BigDecimal; import java.net.URL; @@ -16,8 +16,8 @@ * THIS CLASS MAY BE IGNORED. * Its only purpose is to test and help during development. */ -@Ignore -public class TestNodeWallet { +@Disabled +class TestNodeWallet { public static void main(String[] args) throws Exception { NanoRpcClient rpcClient = new NanoRpcClient(new URL("http://[::1]:7076")); diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/nano/dev/TestPaymentSupport.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/nano/dev/TestPaymentSupport.java index 28a3243ba..049508e75 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/nano/dev/TestPaymentSupport.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/nano/dev/TestPaymentSupport.java @@ -11,7 +11,7 @@ import com.generalbytes.batm.server.extensions.extra.nano.wallet.node.NanoNodeWallet; import com.generalbytes.batm.server.extensions.payment.IPaymentRequestListener; import com.generalbytes.batm.server.extensions.payment.PaymentRequest; -import org.junit.Ignore; +import org.junit.jupiter.api.Disabled; import java.math.BigDecimal; import java.net.URI; @@ -21,8 +21,8 @@ * THIS CLASS MAY BE IGNORED. * Its only purpose is to test and help during development. */ -@Ignore -public class TestPaymentSupport { +@Disabled +class TestPaymentSupport { public static void main(String[] args) throws Exception { try { diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/nano/dev/TestWebSocket.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/nano/dev/TestWebSocket.java index 6d0d059f0..29b0b1d6a 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/nano/dev/TestWebSocket.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/nano/dev/TestWebSocket.java @@ -1,7 +1,7 @@ package com.generalbytes.batm.server.extensions.extra.nano.dev; import com.generalbytes.batm.server.extensions.extra.nano.rpc.NanoWsClient; -import org.junit.Ignore; +import org.junit.jupiter.api.Disabled; import java.net.URI; @@ -9,8 +9,8 @@ * THIS CLASS MAY BE IGNORED. * Its only purpose is to test and help during development. */ -@Ignore -public class TestWebSocket { +@Disabled +class TestWebSocket { public static void main(String[] args) throws Exception { NanoWsClient wsClient = new NanoWsClient(new URI("ws://[::1]:7078")); diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/syscoin/SyscoinAddressValidatorTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/syscoin/SyscoinAddressValidatorTest.java index 3bdb58711..5246f7faf 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/syscoin/SyscoinAddressValidatorTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/syscoin/SyscoinAddressValidatorTest.java @@ -1,14 +1,15 @@ package com.generalbytes.batm.server.extensions.extra.syscoin; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; -public class SyscoinAddressValidatorTest { +class SyscoinAddressValidatorTest { private final SyscoinAddressValidator validator = new SyscoinAddressValidator(); @Test - public void isAddressValid() { + void isAddressValid() { assertTrue(validator.isAddressValid("SSt4EYRJGeQv6vtQy3BgH2NbyFpHPZL27P")); assertTrue(validator.isAddressValid("SWu38cLXzEdxwTCiCKLsmUEMu6nh9ext57")); assertFalse(validator.isAddressValid("SSt4EYRJGeQv6xxxxxxxxxxxxxxxxxxxxx")); diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/watchlists/ca/CaWatchListTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/watchlists/ca/CaWatchListTest.java index eed9f0dc7..51ef97c5f 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/watchlists/ca/CaWatchListTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/watchlists/ca/CaWatchListTest.java @@ -3,16 +3,17 @@ import com.generalbytes.batm.server.extensions.watchlist.WatchListMatch; import com.generalbytes.batm.server.extensions.watchlist.WatchListQuery; import com.generalbytes.batm.server.extensions.watchlist.WatchListResult; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; -public class CaWatchListTest { +class CaWatchListTest { private static final String RESOURCE_PATH = "src/test/resources/"; @Test - public void testWatchlistPositive() { + void testWatchlistPositive() { final CaWatchList caWatchList = new CaWatchList(); caWatchList.init(RESOURCE_PATH); diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/watchlists/ca/ParsedSanctionsTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/watchlists/ca/ParsedSanctionsTest.java index 27648e3a7..da6989daa 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/watchlists/ca/ParsedSanctionsTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/watchlists/ca/ParsedSanctionsTest.java @@ -3,38 +3,39 @@ import com.generalbytes.batm.server.extensions.extra.watchlists.Match; import com.generalbytes.batm.server.extensions.extra.watchlists.ca.tags.DataSet; import com.generalbytes.batm.server.extensions.extra.watchlists.ca.tags.Record; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.Set; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; -public class ParsedSanctionsTest { +class ParsedSanctionsTest { @Test - public void testSearchOk() { + void testSearchOk() { final Record record = createRecord("FirstName MiddleName ", " LastName "); final ParsedSanctions parsedSanctions = initializeParsedSanctions(record); assertMatch(parsedSanctions.search("fIrStNaMe", "lastNAME"), 100); } @Test - public void testSearchLastNameMatch() { + void testSearchLastNameMatch() { final Record record = createRecord("Unknown", " LastName "); final ParsedSanctions parsedSanctions = initializeParsedSanctions(record); assertMatch(parsedSanctions.search("fIrStNaMe", "lastNAME"), 50); } @Test - public void testSearchLastNameMatchNoFirstName() { + void testSearchLastNameMatchNoFirstName() { final Record record = createRecord("Unknown", " LastName "); final ParsedSanctions parsedSanctions = initializeParsedSanctions(record); assertMatch(parsedSanctions.search(null, "lastNAME"), 100); } @Test - public void testSearchAliasMatch() { + void testSearchAliasMatch() { final Record record = createRecord("Unknown", "Stranger"); record.setAliases("Someone with lastname"); final ParsedSanctions parsedSanctions = initializeParsedSanctions(record); @@ -42,7 +43,7 @@ public void testSearchAliasMatch() { } @Test - public void testSearchAliasMatchNoFirstName() { + void testSearchAliasMatchNoFirstName() { final Record record = createRecord("Unknown", "Stranger"); record.setAliases("Someone with lastname"); final ParsedSanctions parsedSanctions = initializeParsedSanctions(record); @@ -50,7 +51,7 @@ public void testSearchAliasMatchNoFirstName() { } @Test - public void testSearchNotFound() { + void testSearchNotFound() { final Record record = createRecord("Unknown", " LastName "); final ParsedSanctions parsedSanctions = initializeParsedSanctions(record); @@ -59,7 +60,7 @@ public void testSearchNotFound() { } @Test - public void testSearchAliasNoMatch() { + void testSearchAliasNoMatch() { final Record record = createRecord("Vladimir Rudolfovich", "SOLOVYOV"); record.setAliases("Владимир Рудольфович Соловьёв; Vladimir Solovev; Soloviev"); final ParsedSanctions parsedSanctions = initializeParsedSanctions(record); diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/watchlists/eu/EUSanctionsListTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/watchlists/eu/EUSanctionsListTest.java index d865cf944..c6caf89fa 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/watchlists/eu/EUSanctionsListTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/watchlists/eu/EUSanctionsListTest.java @@ -3,16 +3,17 @@ import com.generalbytes.batm.server.extensions.watchlist.WatchListMatch; import com.generalbytes.batm.server.extensions.watchlist.WatchListQuery; import com.generalbytes.batm.server.extensions.watchlist.WatchListResult; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; -public class EUSanctionsListTest { +class EUSanctionsListTest { private static final String RESOURCE_PATH = "src/test/resources/"; @Test - public void testWatchlistPositive() { + void testWatchlistPositive() { final EUSanctionsList euSanctionsList = new EUSanctionsList(); euSanctionsList.init(RESOURCE_PATH); diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/watchlists/ofac/OFACWatchListTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/watchlists/ofac/OFACWatchListTest.java index 8e64088a7..e0961e48a 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/watchlists/ofac/OFACWatchListTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/extra/watchlists/ofac/OFACWatchListTest.java @@ -3,16 +3,17 @@ import com.generalbytes.batm.server.extensions.watchlist.WatchListMatch; import com.generalbytes.batm.server.extensions.watchlist.WatchListQuery; import com.generalbytes.batm.server.extensions.watchlist.WatchListResult; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; -public class OFACWatchListTest { +class OFACWatchListTest { private static final String RESOURCE_PATH = "src/test/resources/"; @Test - public void testWatchlistPositive() { + void testWatchlistPositive() { final OFACWatchList ofacWatchList = new OFACWatchList(); ofacWatchList.init(RESOURCE_PATH); diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneApiFactoryTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneApiFactoryTest.java new file mode 100644 index 000000000..e9820f45f --- /dev/null +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneApiFactoryTest.java @@ -0,0 +1,62 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene; + +import com.generalbytes.batm.server.extensions.travelrule.notabene.api.NotabeneApi; +import com.generalbytes.batm.server.extensions.travelrule.notabene.api.NotabeneAuthApi; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.junit.jupiter.MockitoExtension; +import si.mazi.rescu.RestProxyFactory; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class NotabeneApiFactoryTest { + + private static final String NOTABENE_API_URL = "https://api.notabene.id"; + private static final String NOTABENE_AUTH_API_URL = "https://auth.notabene.id"; + + @Mock + private NotabeneConfiguration configuration; + @InjectMocks + private NotabeneApiFactory notabeneApiFactory; + + @Test + void testGetNotabeneApi() { + NotabeneApi notabeneApiMock = mock(NotabeneApi.class); + + try (MockedStatic mockedRestProxyFactory = mockStatic(RestProxyFactory.class)) { + when(configuration.getApiUrl()).thenReturn(NOTABENE_API_URL); + mockedRestProxyFactory.when(() -> RestProxyFactory.createProxy(NotabeneApi.class, NOTABENE_API_URL)) + .thenReturn(notabeneApiMock); + + NotabeneApi result = notabeneApiFactory.getNotabeneApi(); + + assertEquals(notabeneApiMock, result); + mockedRestProxyFactory.verify(() -> RestProxyFactory.createProxy(NotabeneApi.class, NOTABENE_API_URL), times(1)); + } + } + + @Test + void testGetNotabeneAuthApi() { + NotabeneAuthApi notabeneAuthApiMock = mock(NotabeneAuthApi.class); + + try (MockedStatic mockedRestProxyFactory = mockStatic(RestProxyFactory.class)) { + when(configuration.getAuthApiUrl()).thenReturn(NOTABENE_AUTH_API_URL); + mockedRestProxyFactory.when(() -> RestProxyFactory.createProxy(NotabeneAuthApi.class, NOTABENE_AUTH_API_URL)) + .thenReturn(notabeneAuthApiMock); + + NotabeneAuthApi result = notabeneApiFactory.getNotabeneAuthApi(); + + assertEquals(notabeneAuthApiMock, result); + mockedRestProxyFactory.verify(() -> RestProxyFactory.createProxy(NotabeneAuthApi.class, NOTABENE_AUTH_API_URL), times(1)); + } + } + +} \ No newline at end of file diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneApiServiceTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneApiServiceTest.java new file mode 100644 index 000000000..d37c72d3d --- /dev/null +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneApiServiceTest.java @@ -0,0 +1,160 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene; + +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleProviderCredentials; +import com.generalbytes.batm.server.extensions.travelrule.notabene.api.NotabeneApiCall; +import com.generalbytes.batm.server.extensions.travelrule.notabene.api.NotabeneApiException; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneApiError; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferInfo; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import javax.servlet.http.HttpServletResponse; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class NotabeneApiServiceTest { + + @Mock + private NotabeneAuthService authService; + + @InjectMocks + private NotabeneApiService notabeneApiService; + + @Test + void testCallApi_validToken() { + ITravelRuleProviderCredentials providerCredentials = mock(ITravelRuleProviderCredentials.class); + NotabeneApiCall notabeneApiCall = mock(NotabeneApiCall.class); + NotabeneTransferInfo expectedResponse = mock(NotabeneTransferInfo.class); + + when(authService.getAccessToken(providerCredentials)).thenReturn("validAccessToken"); + when(notabeneApiCall.execute("Bearer validAccessToken")).thenReturn(expectedResponse); + + NotabeneTransferInfo response = notabeneApiService.callApi(providerCredentials, notabeneApiCall); + + assertEquals(expectedResponse, response); + verify(notabeneApiCall, times(1)).execute("Bearer validAccessToken"); + verify(authService, times(1)).getAccessToken(providerCredentials); + verify(authService, never()).refreshAccessToken(any()); + verifyNoInteractions(providerCredentials, expectedResponse); + } + + @Test + void testCallApi_invalidToken_refresh() { + ITravelRuleProviderCredentials providerCredentials = mock(ITravelRuleProviderCredentials.class); + NotabeneApiCall notabeneApiCall = mock(NotabeneApiCall.class); + NotabeneTransferInfo expectedResponse = mock(NotabeneTransferInfo.class); + + when(authService.getAccessToken(providerCredentials)) + .thenReturn("invalidAccessToken") + .thenReturn("validAccessToken"); + when(notabeneApiCall.execute("Bearer invalidAccessToken")).thenThrow(createUnauthorizedException()); + when(notabeneApiCall.execute("Bearer validAccessToken")).thenReturn(expectedResponse); + + NotabeneTransferInfo response = notabeneApiService.callApi(providerCredentials, notabeneApiCall); + + assertEquals(expectedResponse, response); + verify(notabeneApiCall, times(1)).execute("Bearer invalidAccessToken"); + verify(notabeneApiCall, times(1)).execute("Bearer validAccessToken"); + verify(authService, times(2)).getAccessToken(providerCredentials); + verify(authService, times(1)).refreshAccessToken(providerCredentials); + verifyNoInteractions(providerCredentials, expectedResponse); + } + + private static Stream provideNonUnauthorizedExceptions() { + return Stream.of( + Arguments.arguments(new RuntimeException("Test Exception")), + Arguments.arguments(createNotabeneApiException(HttpServletResponse.SC_FORBIDDEN, "UnauthorizedError")), + Arguments.arguments(createNotabeneApiException(HttpServletResponse.SC_UNAUTHORIZED, "NotabenePermissionsError")) + ); + } + + @ParameterizedTest + @MethodSource("provideNonUnauthorizedExceptions") + void testCallApi_exception(Exception exception) { + ITravelRuleProviderCredentials providerCredentials = mock(ITravelRuleProviderCredentials.class); + NotabeneApiCall notabeneApiCall = mock(NotabeneApiCall.class); + + when(authService.getAccessToken(providerCredentials)).thenReturn("validAccessToken"); + when(notabeneApiCall.execute("Bearer validAccessToken")).thenThrow(exception); + + RuntimeException resultException = assertThrows(RuntimeException.class, + () -> notabeneApiService.callApi(providerCredentials, notabeneApiCall)); + + assertEquals(exception, resultException); + verify(notabeneApiCall, times(1)).execute("Bearer validAccessToken"); + verify(authService, times(1)).getAccessToken(providerCredentials); + verify(authService, never()).refreshAccessToken(any()); + verifyNoInteractions(providerCredentials); + } + + @Test + void testCallApi_refreshThrows_fail() { + ITravelRuleProviderCredentials providerCredentials = mock(ITravelRuleProviderCredentials.class); + NotabeneApiCall notabeneApiCall = mock(NotabeneApiCall.class); + + when(authService.getAccessToken(providerCredentials)).thenReturn("validAccessToken"); + when(notabeneApiCall.execute("Bearer validAccessToken")).thenThrow(createUnauthorizedException()); + doThrow(new RuntimeException("Test Exception")).when(authService).refreshAccessToken(providerCredentials); + + RuntimeException exception = assertThrows(RuntimeException.class, + () -> notabeneApiService.callApi(providerCredentials, notabeneApiCall)); + + assertEquals("Test Exception", exception.getMessage()); + verify(notabeneApiCall, times(1)).execute("Bearer validAccessToken"); + verify(authService, times(1)).getAccessToken(providerCredentials); + verify(authService, times(1)).refreshAccessToken(providerCredentials); + verifyNoInteractions(providerCredentials); + } + + @Test + void testCallApi_invalidToken_refresh_exceeded_repetitions() { + ITravelRuleProviderCredentials providerCredentials = mock(ITravelRuleProviderCredentials.class); + NotabeneApiCall notabeneApiCall = mock(NotabeneApiCall.class); + NotabeneTransferInfo expectedResponse = mock(NotabeneTransferInfo.class); + + when(authService.getAccessToken(providerCredentials)).thenReturn("invalidAccessToken"); + + RuntimeException unauthorizedException = createUnauthorizedException(); + when(notabeneApiCall.execute("Bearer invalidAccessToken")).thenThrow(unauthorizedException); + + NotabeneApiException exception = assertThrows(NotabeneApiException.class, + () -> notabeneApiService.callApi(providerCredentials, notabeneApiCall)); + + assertEquals(unauthorizedException, exception); + + verify(notabeneApiCall, times(6)).execute("Bearer invalidAccessToken"); + verify(authService, times(6)).getAccessToken(providerCredentials); + verify(authService, times(5)).refreshAccessToken(providerCredentials); + verifyNoInteractions(providerCredentials, expectedResponse); + } + + private static RuntimeException createUnauthorizedException() { + return createNotabeneApiException(HttpServletResponse.SC_UNAUTHORIZED, "UnauthorizedError"); + } + + private static RuntimeException createNotabeneApiException(int statusCode, String name) { + NotabeneApiError notabeneApiError = new NotabeneApiError(); + notabeneApiError.setCode(statusCode); + notabeneApiError.setMessage("Test Exception"); + notabeneApiError.setName(name); + return new NotabeneApiException(notabeneApiError); + } + +} \ No newline at end of file diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneApiWrapperTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneApiWrapperTest.java new file mode 100644 index 000000000..d6d00886a --- /dev/null +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneApiWrapperTest.java @@ -0,0 +1,212 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene; + +import com.fasterxml.jackson.core.JsonParseException; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleProviderCredentials; +import com.generalbytes.batm.server.extensions.travelrule.notabene.api.NotabeneApi; +import com.generalbytes.batm.server.extensions.travelrule.notabene.api.NotabeneApiCall; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneAddressOwnershipInfoRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneAddressOwnershipInfoResponse; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneFullyValidateTransferResponse; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneListVaspsQueryParams; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneListVaspsResponse; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneRegisterWebhookRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferCreateRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferInfo; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferUpdateRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneUnregisterWebhookRequest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.function.Supplier; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class NotabeneApiWrapperTest { + + @Mock + private NotabeneApi api; + @Mock + private NotabeneApiService apiService; + @Mock + private NotabeneApiFactory apiFactory; + + private NotabeneApiWrapper apiWrapper; + + @BeforeEach + void setUp() { + when(apiFactory.getNotabeneApi()).thenReturn(api); + + apiWrapper = new NotabeneApiWrapper(apiFactory, apiService); + } + + private static Stream provideQueryParametersForListVasps() { + return Stream.of( + Arguments.arguments("query", true), + Arguments.arguments("query", false), + Arguments.arguments("query", null), + Arguments.arguments(null, true), + Arguments.arguments(null, false), + Arguments.arguments(null, null) + ); + } + + @ParameterizedTest + @MethodSource("provideQueryParametersForListVasps") + void testListVasps(String query, Boolean returnAllRecords) { + NotabeneListVaspsQueryParams params = mock(NotabeneListVaspsQueryParams.class); + when(params.getQuery()).thenReturn(query); + when(params.getAll()).thenReturn(returnAllRecords); + NotabeneListVaspsResponse response = mock(NotabeneListVaspsResponse.class); + ITravelRuleProviderCredentials providerCredentials = mock(ITravelRuleProviderCredentials.class); + + testApiWrapperMethod(providerCredentials, response, + () -> apiWrapper.listVasps(providerCredentials, params), + authorization -> verify(api).listVasps(authorization, query, returnAllRecords)); + } + + @Test + void testValidateFull() { + NotabeneTransferCreateRequest request = mock(NotabeneTransferCreateRequest.class); + NotabeneFullyValidateTransferResponse response = mock(NotabeneFullyValidateTransferResponse.class); + ITravelRuleProviderCredentials providerCredentials = mock(ITravelRuleProviderCredentials.class); + + testApiWrapperMethod(providerCredentials, response, + () -> apiWrapper.validateFull(providerCredentials, request), + authorization -> verify(api).validateFull(authorization, request)); + } + + @Test + void testCreateTransfer() { + NotabeneTransferCreateRequest request = mock(NotabeneTransferCreateRequest.class); + NotabeneTransferInfo response = mock(NotabeneTransferInfo.class); + ITravelRuleProviderCredentials providerCredentials = mock(ITravelRuleProviderCredentials.class); + + testApiWrapperMethod(providerCredentials, response, + () -> apiWrapper.createTransfer(providerCredentials, request), + authorization -> verify(api).createTransfer(authorization, request)); + } + + @Test + void testUpdateTransfer() { + NotabeneTransferUpdateRequest request = mock(NotabeneTransferUpdateRequest.class); + NotabeneTransferInfo response = mock(NotabeneTransferInfo.class); + ITravelRuleProviderCredentials providerCredentials = mock(ITravelRuleProviderCredentials.class); + + testApiWrapperMethod(providerCredentials, response, + () -> apiWrapper.updateTransfer(providerCredentials, request), + authorization -> verify(api).updateTransfer(authorization, request)); + } + + @Test + void testGetAddressOwnershipInformation() { + NotabeneAddressOwnershipInfoRequest request = mock(NotabeneAddressOwnershipInfoRequest.class); + when(request.getAddress()).thenReturn("address"); + when(request.getVaspDid()).thenReturn("vaspDID"); + when(request.getAsset()).thenReturn("asset"); + NotabeneAddressOwnershipInfoResponse response = mock(NotabeneAddressOwnershipInfoResponse.class); + ITravelRuleProviderCredentials providerCredentials = mock(ITravelRuleProviderCredentials.class); + + testApiWrapperMethod(providerCredentials, response, + () -> apiWrapper.getAddressOwnershipInformation(providerCredentials, request), + authorization -> verify(api).getAddressOwnershipInformation(authorization, "address", "vaspDID", "asset")); + } + + @Test + void testApproveTransfer() { + NotabeneTransferInfo response = mock(NotabeneTransferInfo.class); + ITravelRuleProviderCredentials providerCredentials = mock(ITravelRuleProviderCredentials.class); + + testApiWrapperMethod(providerCredentials, response, + () -> apiWrapper.approveTransfer(providerCredentials, "transferId"), + authorization -> verify(api).approveTransfer(authorization, "transferId")); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testRegisterWebhook(boolean shouldThrowJsonParseException) throws JsonParseException { + NotabeneRegisterWebhookRequest request = mock(NotabeneRegisterWebhookRequest.class); + ITravelRuleProviderCredentials providerCredentials = mock(ITravelRuleProviderCredentials.class); + + if (shouldThrowJsonParseException) { + doThrow(new JsonParseException(null, "Test Exception")).when(api).registerWebhook(anyString(), any()); + } + + testApiWrapperMethod(providerCredentials, null, + () -> { + apiWrapper.registerWebhook(providerCredentials, request); + return null; + }, + authorization -> verify(api).registerWebhook(authorization, request)); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testUnregisterWebhook(boolean shouldThrowJsonParseException) throws JsonParseException { + NotabeneUnregisterWebhookRequest request = mock(NotabeneUnregisterWebhookRequest.class); + ITravelRuleProviderCredentials providerCredentials = mock(ITravelRuleProviderCredentials.class); + + if (shouldThrowJsonParseException) { + doThrow(new JsonParseException(null, "Test Exception")).when(api).unregisterWebhook(anyString(), any()); + } + + testApiWrapperMethod(providerCredentials, null, + () -> { + apiWrapper.unregisterWebhook(providerCredentials, request); + return null; + }, + authorization -> verify(api).unregisterWebhook(authorization, request)); + } + + private void testApiWrapperMethod(ITravelRuleProviderCredentials providerCredentialsMock, R responseMock, + Supplier apiWrapperCall, ThrowingConsumer apiCallVerification) { + when(apiService.callApi(eq(providerCredentialsMock), any())).thenReturn(responseMock); + + R result = apiWrapperCall.get(); + + assertEquals(responseMock, result); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(NotabeneApiCall.class); + verify(apiService).callApi(eq(providerCredentialsMock), captor.capture()); + + NotabeneApiCall capturedCall = captor.getValue(); + assertNotNull(capturedCall); + + capturedCall.execute("authorization"); + try { + apiCallVerification.accept("authorization"); + } catch (Exception e) { + fail("Unexpected exception thrown from api call verification"); + } + + verifyNoInteractions(providerCredentialsMock); + if (responseMock != null) { + verifyNoInteractions(responseMock); + } + } + + @FunctionalInterface + private interface ThrowingConsumer { + void accept(T t) throws Exception; + } + +} \ No newline at end of file diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneAuthServiceTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneAuthServiceTest.java new file mode 100644 index 000000000..73d7f57ac --- /dev/null +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneAuthServiceTest.java @@ -0,0 +1,297 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene; + +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleProviderCredentials; +import com.generalbytes.batm.server.extensions.travelrule.notabene.api.NotabeneAuthApi; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneGenerateAccessTokenRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneGenerateAccessTokenResponse; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import si.mazi.rescu.HttpStatusIOException; + +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class NotabeneAuthServiceTest { + + private static final String NOTABENE_API_URL = "https://api.notabene.id"; + + @Mock + private NotabeneAuthApi authApi; + @Mock + private NotabeneApiFactory apiFactory; + @Mock + private NotabeneConfiguration configuration; + + private NotabeneAuthService authService; + + @BeforeEach + void setUp() { + when(apiFactory.getNotabeneAuthApi()).thenReturn(authApi); + + authService = new NotabeneAuthService(apiFactory, configuration); + } + + @Test + void testRefreshAccessToken_nullProviderDetails() { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> authService.refreshAccessToken(null)); + + assertEquals("providerCredentials cannot be null", exception.getMessage()); + verifyNoInteractions(authApi); + } + + @Test + void testRefreshAccessToken_valid() throws HttpStatusIOException { + ITravelRuleProviderCredentials providerCredentials = createTravelRuleProviderIdentification(); + when(authApi.generateAccessToken(any())).thenReturn(createAccessTokenResponse("accessToken1")); + + when(configuration.getApiUrl()).thenReturn(NOTABENE_API_URL); + + authService.refreshAccessToken(providerCredentials); + String result = authService.getAccessToken(providerCredentials); + + assertEquals("accessToken1", result); + verifyAccessTokenRequested(); + } + + private static Object[][] invalidGetAccessTokenResponseSource() { + return new Object[][]{ + {401, "unauthorized request"}, + {403, "forbidden request"}, + }; + } + + @ParameterizedTest + @MethodSource("invalidGetAccessTokenResponseSource") + void testRefreshAccessToken_invalidGetAccessTokenResponse400(int statusCode, String expectedError) throws HttpStatusIOException { + ITravelRuleProviderCredentials providerCredentials = createTravelRuleProviderIdentification(); + HttpStatusIOException httpStatusIOException = mock(HttpStatusIOException.class); + when(httpStatusIOException.getHttpStatusCode()).thenReturn(statusCode); + + when(authApi.generateAccessToken(any())).thenThrow(httpStatusIOException); + + when(configuration.getApiUrl()).thenReturn(NOTABENE_API_URL); + authService.refreshAccessToken(providerCredentials); + try { + authService.getAccessToken(providerCredentials); + fail("Should throw an exception"); + } catch (CompletionException e) { + assertEquals(expectedError, e.getMessage()); + } + + verifyAccessTokenRequested(); + } + + private static Object[][] invalidGetAccessTokenResponseUnexpectedExceptionSource() { + HttpStatusIOException httpStatusIOException = mock(HttpStatusIOException.class); + when(httpStatusIOException.getHttpStatusCode()).thenReturn(500); + + return new Object[][]{ + {httpStatusIOException}, + {new RuntimeException("unexpected exception")}, + }; + } + + @ParameterizedTest + @MethodSource("invalidGetAccessTokenResponseUnexpectedExceptionSource") + void testRefreshAccessToken_invalidGetAccessTokenResponseUnexpected(Exception exception) throws HttpStatusIOException { + ITravelRuleProviderCredentials providerCredentials = createTravelRuleProviderIdentification(); + when(authApi.generateAccessToken(any())).thenThrow(exception); + when(configuration.getApiUrl()).thenReturn(NOTABENE_API_URL); + + authService.refreshAccessToken(providerCredentials); + String accessToken = authService.getAccessToken(providerCredentials); + assertNull(accessToken); + verifyAccessTokenRequested(); + } + + @Test + void testGetAccessToken_nullTravelRuleProviderIdentification() { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> authService.getAccessToken(null)); + + assertEquals("providerCredentials cannot be null", exception.getMessage()); + verifyNoInteractions(authApi); + } + + @Test + void testGetAccessToken_noCachedToken() { + ITravelRuleProviderCredentials providerCredentials = createTravelRuleProviderIdentification(); + + String result = authService.getAccessToken(providerCredentials); + + assertNull(result); + } + + @Test + void testGetAccessToken() throws HttpStatusIOException { + ITravelRuleProviderCredentials providerCredentials = createTravelRuleProviderIdentification(); + + assertNull(authService.getAccessToken(providerCredentials)); + + when(authApi.generateAccessToken(any())).thenReturn(createAccessTokenResponse("accessToken1")); + authService.refreshAccessToken(providerCredentials); + + assertEquals("accessToken1", authService.getAccessToken(providerCredentials)); + + when(authApi.generateAccessToken(any())).thenReturn(createAccessTokenResponse("accessToken2")); + authService.refreshAccessToken(providerCredentials); + + assertEquals("accessToken2", authService.getAccessToken(providerCredentials)); + } + + @Test + void testRefreshAccessToken_concurrentExecution() throws InterruptedException, ExecutionException, HttpStatusIOException { + ITravelRuleProviderCredentials providerCredentials = createTravelRuleProviderIdentification(); + NotabeneGenerateAccessTokenResponse mockResponse = createAccessTokenResponse("testToken"); + when(authApi.generateAccessToken(any())).thenAnswer(invocation -> { + Thread.sleep(100); // Delay to simulate a time-consuming API call + return mockResponse; + }); + ExecutorService executorService = Executors.newFixedThreadPool(5); + Callable task = () -> { + authService.refreshAccessToken(providerCredentials); + return null; + }; + List> futures = executorService.invokeAll(List.of(task, task, task, task, task)); + for (Future future : futures) { + future.get(); + } + // Calling #getAccessToken to wait for the refresh to be finished + assertEquals("testToken", authService.getAccessToken(providerCredentials)); + verify(authApi, times(1)).generateAccessToken(any()); + executorService.shutdown(); + } + + @Test + void testRemoveAccessToken_accessTokenSet() throws HttpStatusIOException { + ITravelRuleProviderCredentials providerCredentials = createTravelRuleProviderIdentification(); + + when(authApi.generateAccessToken(any())).thenReturn(createAccessTokenResponse("accessToken1")); + authService.refreshAccessToken(providerCredentials); + + String accessToken = authService.getAccessToken(providerCredentials); + assertNotNull(accessToken); + + authService.removeAccessToken(providerCredentials); + + String accessTokenAfterRemoval = authService.getAccessToken(providerCredentials); + assertNull(accessTokenAfterRemoval); + } + + @Test + void testRemoveAccessToken__accessTokenNotSet() { + ITravelRuleProviderCredentials providerCredentials = createTravelRuleProviderIdentification(); + + String accessToken = authService.getAccessToken(providerCredentials); + assertNull(accessToken); + + authService.removeAccessToken(providerCredentials); + + String accessTokenAfterRemoval = authService.getAccessToken(providerCredentials); + assertNull(accessTokenAfterRemoval); + } + + + @Test + void testRemoveAccessToken_failedToFetchAccessToken() throws HttpStatusIOException { + ITravelRuleProviderCredentials providerCredentials = createTravelRuleProviderIdentification(); + HttpStatusIOException httpStatusIOException = mock(HttpStatusIOException.class); + when(httpStatusIOException.getHttpStatusCode()).thenReturn(401); + + when(authApi.generateAccessToken(any())).thenThrow(httpStatusIOException); + authService.refreshAccessToken(providerCredentials); + + try { + authService.getAccessToken(providerCredentials); + fail("Should throw an exception"); + } catch (CompletionException e) { + assertEquals("unauthorized request", e.getMessage()); + } + + authService.removeAccessToken(providerCredentials); + + String accessTokenAfterRemoval = authService.getAccessToken(providerCredentials); + assertNull(accessTokenAfterRemoval); + } + + @Test + void testRemoveAccessToken_accessTokenGetPending() throws HttpStatusIOException { + ITravelRuleProviderCredentials providerCredentials = createTravelRuleProviderIdentification(); + + when(authApi.generateAccessToken(any())).thenAnswer(invocation -> { + Thread.sleep(100); // Delay to simulate a time-consuming API call + return createAccessTokenResponse("accessToken1"); + }); + authService.refreshAccessToken(providerCredentials); + + authService.removeAccessToken(providerCredentials); + + String accessTokenAfterRemoval = authService.getAccessToken(providerCredentials); + assertNull(accessTokenAfterRemoval); + } + + private void verifyAccessTokenRequested() throws HttpStatusIOException { + ArgumentCaptor captor = ArgumentCaptor.forClass(NotabeneGenerateAccessTokenRequest.class); + verify(authApi, times(1)).generateAccessToken(captor.capture()); + captor.getAllValues().forEach(this::assertGenerateAccessTokenRequest); + } + + private void assertGenerateAccessTokenRequest(NotabeneGenerateAccessTokenRequest request) { + assertNotNull(request); + assertEquals("clientId", request.getClientId()); + assertEquals("clientSecret", request.getClientSecret()); + assertEquals("client_credentials", request.getGrantType()); + assertEquals(NOTABENE_API_URL, request.getAudience()); + } + + private NotabeneGenerateAccessTokenResponse createAccessTokenResponse(String accessToken) { + NotabeneGenerateAccessTokenResponse response = new NotabeneGenerateAccessTokenResponse(); + response.setAccessToken(accessToken); + return response; + } + + private ITravelRuleProviderCredentials createTravelRuleProviderIdentification() { + return new ITravelRuleProviderCredentials() { + @Override + public String getClientId() { + return "clientId"; + } + + @Override + public String getClientSecret() { + return "clientSecret"; + } + + @Override + public String getVaspDid() { + return "vaspDid"; + } + }; + } + +} \ No newline at end of file diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneExtensionTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneExtensionTest.java new file mode 100644 index 000000000..b1445f611 --- /dev/null +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneExtensionTest.java @@ -0,0 +1,79 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene; + +import com.generalbytes.batm.server.extensions.IExtensionContext; +import com.generalbytes.batm.server.extensions.IRestService; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleProviderFactory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class NotabeneExtensionTest { + + private NotabeneExtension extension; + + @BeforeEach + void setUp() { + extension = new NotabeneExtension(); + } + + @Test + void testGetName() { + assertEquals("Travel rule extension for Notabene", extension.getName()); + } + + @Test + void testInit() { + IExtensionContext extensionContext = mock(IExtensionContext.class); + when(extensionContext.getConfigProperty("notabene", "webhooksEnabled", null)).thenReturn("true"); + + extension.init(extensionContext); + + verify(extensionContext).getConfigProperty("notabene", "apiUrl", "https://api.notabene.id"); + verify(extensionContext).getConfigProperty("notabene", "authApiUrl", "https://auth.notabene.id"); + verify(extensionContext).getConfigProperty("notabene", "webhooksEnabled", null); + verify(extensionContext).getConfigProperty("notabene", "automaticallyApproveOutgoingTransfers", null); + verify(extensionContext).getConfigProperty("network", "master_bind_ip", null); + + // after initialization, the extension should have a set of rest services + Set restServices = extension.getRestServices(); + assertEquals(1, restServices.size()); + IRestService restService = restServices.iterator().next(); + assertInstanceOf(NotabeneWebhookRestService.class, restService); + + Set travelRuleProviderFactories = extension.getTravelRuleProviderFactories(); + assertEquals(1, travelRuleProviderFactories.size()); + ITravelRuleProviderFactory providerFactory = travelRuleProviderFactories.iterator().next(); + assertInstanceOf(NotabeneProviderFactory.class, providerFactory); + } + + @Test + void testInit_webhookNotEnabled() { + IExtensionContext extensionContext = mock(IExtensionContext.class); + + extension.init(extensionContext); + + // after initialization, the extension should have a set of rest services + Set restServices = extension.getRestServices(); + assertTrue(restServices.isEmpty()); + } + + @Test + void testGetTravelRuleProviderFactories_notInitialized() { + IllegalStateException exception = assertThrows(IllegalStateException.class, + () -> extension.getTravelRuleProviderFactories()); + assertEquals("Extension not initialized yet", exception.getMessage()); + } + + @Test + void testGetRestServices_notInitialized() { + IllegalStateException exception = assertThrows(IllegalStateException.class, + () -> extension.getRestServices()); + assertEquals("Extension not initialized yet", exception.getMessage()); + } +} \ No newline at end of file diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneProviderFactoryTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneProviderFactoryTest.java new file mode 100644 index 000000000..7a012db85 --- /dev/null +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneProviderFactoryTest.java @@ -0,0 +1,67 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene; + +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleProvider; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleProviderCredentials; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class NotabeneProviderFactoryTest { + @Mock + private NotabeneConfiguration configuration; + @InjectMocks + private NotabeneProviderFactory notabeneProviderFactory; + + @Test + void testGetProviderName() { + assertEquals(NotabeneTravelRuleProvider.NAME, notabeneProviderFactory.getProviderName()); + } + + @Test + void testGetProvider() { + ITravelRuleProviderCredentials credentials = createCredentials("vaspDid"); + + ITravelRuleProvider provider = notabeneProviderFactory.getProvider(credentials); + assertNotNull(provider); + assertInstanceOf(NotabeneTravelRuleProvider.class, provider); + + ITravelRuleProvider repeatedCall = notabeneProviderFactory.getProvider(credentials); + assertEquals(provider, repeatedCall); + } + + @Test + void testGetProvider_differentProvidersPerVaspDid() { + ITravelRuleProviderCredentials credentials1 = createCredentials("vaspDid1"); + ITravelRuleProviderCredentials credentials2 = createCredentials("vaspDid2"); + + ITravelRuleProvider provider1 = notabeneProviderFactory.getProvider(credentials1); + ITravelRuleProvider provider2 = notabeneProviderFactory.getProvider(credentials2); + assertNotEquals(provider1, provider2); + } + + @Test + void testGetProvider_unknownVaspDid() { + ITravelRuleProviderCredentials credentials1 = createCredentials(null); + ITravelRuleProviderCredentials credentials2 = createCredentials(null); + + ITravelRuleProvider provider1 = notabeneProviderFactory.getProvider(credentials1); + ITravelRuleProvider provider2 = notabeneProviderFactory.getProvider(credentials2); + assertNotEquals(provider1, provider2); + } + + private static ITravelRuleProviderCredentials createCredentials(String vaspDid) { + ITravelRuleProviderCredentials credentials1 = mock(ITravelRuleProviderCredentials.class); + when(credentials1.getVaspDid()).thenReturn(vaspDid); + return credentials1; + } +} \ No newline at end of file diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneServiceTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneServiceTest.java new file mode 100644 index 000000000..4d0aaf3f9 --- /dev/null +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneServiceTest.java @@ -0,0 +1,385 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene; + +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleProviderCredentials; +import com.generalbytes.batm.server.extensions.travelrule.TravelRuleProviderException; +import com.generalbytes.batm.server.extensions.travelrule.notabene.api.NotabeneApiException; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneAddressOwnershipInfoRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneAddressOwnershipInfoResponse; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneApiError; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneCryptoAddressType; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneListVaspsQueryParams; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneListVaspsResponse; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneRegisterWebhookRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferCreateRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferInfo; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferUpdateRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneUnregisterWebhookRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneVaspInfoSimple; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.stubbing.Answer; + +import javax.servlet.http.HttpServletResponse; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class NotabeneServiceTest { + @Mock + private NotabeneApiWrapper notabeneApiWrapper; + @Mock + private NotabeneConfiguration configuration; + @InjectMocks + private NotabeneService service; + + @Test + void testGetAllVasps_valid() { + ITravelRuleProviderCredentials providerCredentials = createTravelRuleProviderIdentification(); + NotabeneVaspInfoSimple vasp = new NotabeneVaspInfoSimple(); + NotabeneListVaspsResponse response = createNotabeneListVaspsResponse(vasp); + + when(notabeneApiWrapper.listVasps(any(), any())).thenReturn(response); + + List allVasps = service.getAllVasps(providerCredentials); + + assertNotNull(allVasps); + assertEquals(1, allVasps.size()); + assertEquals(vasp, allVasps.get(0)); + verifyListAllVaspsCalled(providerCredentials); + } + + @Test + void testGetAllVasps_nullResponse_returnEmptyList() { + ITravelRuleProviderCredentials providerCredentials = createTravelRuleProviderIdentification(); + + when(notabeneApiWrapper.listVasps(any(), any())).thenReturn(null); + + TravelRuleProviderException exception = assertThrows(TravelRuleProviderException.class, () -> service.getAllVasps(providerCredentials)); + + assertEquals("Failed to fetch VASPs from Notabene.", exception.getMessage()); + verifyListAllVaspsCalled(providerCredentials); + } + + @Test + void testGetAllVasps_exceptionThrown() { + ITravelRuleProviderCredentials providerCredentials = createTravelRuleProviderIdentification(); + + when(notabeneApiWrapper.listVasps(any(), any())).thenThrow(new RuntimeException("Test Exception")); + + TravelRuleProviderException exception = assertThrows(TravelRuleProviderException.class, () -> service.getAllVasps(providerCredentials)); + + assertEquals("Failed to fetch VASPs from Notabene.", exception.getMessage()); + verifyListAllVaspsCalled(providerCredentials); + } + + @Test + void testCreateTransfer_valid() { + ITravelRuleProviderCredentials providerCredentials = mock(ITravelRuleProviderCredentials.class); + NotabeneTransferCreateRequest request = mock(NotabeneTransferCreateRequest.class); + NotabeneTransferInfo response = mock(NotabeneTransferInfo.class); + + when(notabeneApiWrapper.createTransfer(any(), any())).thenReturn(response); + + NotabeneTransferInfo result = service.createTransfer(providerCredentials, request); + + assertEquals(response, result); + verify(notabeneApiWrapper, times(1)).createTransfer(providerCredentials, request); + verifyNoInteractions(providerCredentials, request); + } + + @Test + void testCreateTransfer_exception() { + ITravelRuleProviderCredentials providerCredentials = mock(ITravelRuleProviderCredentials.class); + NotabeneTransferCreateRequest request = mock(NotabeneTransferCreateRequest.class); + + when(notabeneApiWrapper.createTransfer(any(), any())).thenThrow(new RuntimeException("Test Exception")); + + NotabeneTransferInfo result = service.createTransfer(providerCredentials, request); + + assertNull(result); + verify(notabeneApiWrapper, times(1)).createTransfer(providerCredentials, request); + verifyNoInteractions(providerCredentials, request); + } + + @Test + void testGetAddressOwnershipInformation_valid() { + ITravelRuleProviderCredentials providerCredentials = mock(ITravelRuleProviderCredentials.class); + NotabeneAddressOwnershipInfoRequest request = mock(NotabeneAddressOwnershipInfoRequest.class); + NotabeneAddressOwnershipInfoResponse response = mock(NotabeneAddressOwnershipInfoResponse.class); + + when(notabeneApiWrapper.getAddressOwnershipInformation(any(), any())).thenReturn(response); + + NotabeneAddressOwnershipInfoResponse result = service.getAddressOwnershipInformation(providerCredentials, request); + + assertEquals(response, result); + verify(notabeneApiWrapper, times(1)).getAddressOwnershipInformation(providerCredentials, request); + verifyNoInteractions(providerCredentials, request); + } + + private static Stream provideGetAddressOwnershipInformationException() { + return Stream.of( + Arguments.arguments(new RuntimeException("Test Exception")), + Arguments.arguments(createNotabeneApiException(HttpServletResponse.SC_BAD_REQUEST)) + ); + } + + @ParameterizedTest + @MethodSource("provideGetAddressOwnershipInformationException") + void testGetAddressOwnershipInformation_exception(Exception exception) { + ITravelRuleProviderCredentials providerCredentials = mock(ITravelRuleProviderCredentials.class); + NotabeneAddressOwnershipInfoRequest request = mock(NotabeneAddressOwnershipInfoRequest.class); + + when(notabeneApiWrapper.getAddressOwnershipInformation(any(), any())).thenThrow(exception); + + NotabeneAddressOwnershipInfoResponse result = service.getAddressOwnershipInformation(providerCredentials, request); + + assertNull(result); + verify(notabeneApiWrapper, times(1)).getAddressOwnershipInformation(providerCredentials, request); + verifyNoInteractions(providerCredentials, request); + } + + @Test + void testGetAddressOwnershipInformation_notFound() { + ITravelRuleProviderCredentials providerCredentials = mock(ITravelRuleProviderCredentials.class); + NotabeneAddressOwnershipInfoRequest request = mock(NotabeneAddressOwnershipInfoRequest.class); + + when(notabeneApiWrapper.getAddressOwnershipInformation(any(), any())) + .thenThrow(createNotabeneApiException(HttpServletResponse.SC_NOT_FOUND)); + + NotabeneAddressOwnershipInfoResponse result = service.getAddressOwnershipInformation(providerCredentials, request); + + assertNotNull(result); + assertNull(result.getOwnerVaspDid()); + assertEquals(NotabeneCryptoAddressType.UNKNOWN, result.getAddressType()); + verify(notabeneApiWrapper, times(1)).getAddressOwnershipInformation(providerCredentials, request); + verifyNoInteractions(providerCredentials, request); + } + + @Test + void testUpdateTransfer_valid() { + ITravelRuleProviderCredentials providerCredentials = mock(ITravelRuleProviderCredentials.class); + NotabeneTransferUpdateRequest request = mock(NotabeneTransferUpdateRequest.class); + NotabeneTransferInfo response = mock(NotabeneTransferInfo.class); + + when(notabeneApiWrapper.updateTransfer(any(), any())).thenReturn(response); + + NotabeneTransferInfo result = service.updateTransfer(providerCredentials, request); + + assertEquals(response, result); + verify(notabeneApiWrapper, times(1)).updateTransfer(providerCredentials, request); + verifyNoInteractions(providerCredentials, request); + } + + @Test + void testUpdateTransfer_exception() { + ITravelRuleProviderCredentials providerCredentials = mock(ITravelRuleProviderCredentials.class); + NotabeneTransferUpdateRequest request = mock(NotabeneTransferUpdateRequest.class); + + when(notabeneApiWrapper.updateTransfer(any(), any())).thenThrow(new RuntimeException("Test Exception")); + + NotabeneTransferInfo result = service.updateTransfer(providerCredentials, request); + + assertNull(result); + verify(notabeneApiWrapper, times(1)).updateTransfer(providerCredentials, request); + verifyNoInteractions(providerCredentials, request); + } + + @Test + void testApproveTransfer_valid() { + ITravelRuleProviderCredentials providerCredentials = mock(ITravelRuleProviderCredentials.class); + NotabeneTransferInfo response = mock(NotabeneTransferInfo.class); + + when(notabeneApiWrapper.approveTransfer(any(), any())).thenReturn(response); + + NotabeneTransferInfo result = service.approveTransfer(providerCredentials, "transferId"); + + assertEquals(response, result); + verify(notabeneApiWrapper, times(1)).approveTransfer(providerCredentials, "transferId"); + verifyNoInteractions(providerCredentials); + } + + @Test + void testApproveTransfer_exception() { + ITravelRuleProviderCredentials providerCredentials = mock(ITravelRuleProviderCredentials.class); + + when(notabeneApiWrapper.approveTransfer(any(), any())).thenThrow(new RuntimeException("Test Exception")); + + NotabeneTransferInfo result = service.approveTransfer(providerCredentials, "transferId"); + + assertNull(result); + verify(notabeneApiWrapper, times(1)).approveTransfer(providerCredentials, "transferId"); + verifyNoInteractions(providerCredentials); + } + + @Test + void testRegisterWebhook_valid() { + ITravelRuleProviderCredentials providerCredentials = createTravelRuleProviderIdentification(); + + when(configuration.getMasterExtensionsUrl()).thenReturn("https://localhost:7743/extensions"); + + boolean result = service.registerWebhook(providerCredentials); + + assertTrue(result); + assertRegisterWebhookRequest(providerCredentials); + } + + @Test + void testRegisterWebhook_exception() { + ITravelRuleProviderCredentials providerCredentials = createTravelRuleProviderIdentification(); + + when(configuration.getMasterExtensionsUrl()).thenReturn("https://localhost:7743/extensions"); + + doThrow(new RuntimeException("Test Exception")).when(notabeneApiWrapper).registerWebhook(any(), any()); + + boolean result = service.registerWebhook(providerCredentials); + + assertFalse(result); + assertRegisterWebhookRequest(providerCredentials); + } + + private static Stream provideTestUnregisteredWebhookValid() { + return Stream.of( + Arguments.arguments((Answer) invocation -> null), + Arguments.arguments((Answer) invocation -> { + throw createNotabeneApiException(HttpServletResponse.SC_NOT_FOUND); + }) + ); + } + + @ParameterizedTest + @MethodSource("provideTestUnregisteredWebhookValid") + void testUnregisterWebhook_valid(Answer answer) { + ITravelRuleProviderCredentials providerCredentials = createTravelRuleProviderIdentification(); + + doAnswer(answer).when(notabeneApiWrapper).unregisterWebhook(any(), any()); + + boolean result = service.unregisterWebhook(providerCredentials); + + assertTrue(result); + assertUnregisterWebhookRequest(providerCredentials); + } + + private static Stream provideTestUnregisteredWebhookException() { + return Stream.of( + Arguments.arguments(new RuntimeException("Test Exception")), + Arguments.arguments(createNotabeneApiException(HttpServletResponse.SC_BAD_REQUEST)) + ); + } + + @ParameterizedTest + @MethodSource("provideTestUnregisteredWebhookException") + void testUnregisterWebhook_exception(Exception exception) { + ITravelRuleProviderCredentials providerCredentials = createTravelRuleProviderIdentification(); + + doThrow(exception).when(notabeneApiWrapper).unregisterWebhook(any(), any()); + + boolean result = service.unregisterWebhook(providerCredentials); + + assertFalse(result); + assertUnregisterWebhookRequest(providerCredentials); + } + + @Test + void testTestProviderCredentials_valid() { + ITravelRuleProviderCredentials providerCredentials = createTravelRuleProviderIdentification(); + + boolean result = service.testProviderCredentials(providerCredentials); + + assertTrue(result); + verify(notabeneApiWrapper, times(1)).listVasps(eq(providerCredentials), any()); + } + + @Test + void testTestProviderCredentials_invalid() { + ITravelRuleProviderCredentials providerCredentials = createTravelRuleProviderIdentification(); + + when(notabeneApiWrapper.listVasps(eq(providerCredentials), any())).thenThrow(NotabeneApiException.class); + + boolean result = service.testProviderCredentials(providerCredentials); + + assertFalse(result); + } + + private ITravelRuleProviderCredentials createTravelRuleProviderIdentification() { + return new ITravelRuleProviderCredentials() { + @Override + public String getClientId() { + return "clientId"; + } + + @Override + public String getClientSecret() { + return "clientSecret"; + } + + @Override + public String getVaspDid() { + return "vaspDid"; + } + }; + } + + private void assertRegisterWebhookRequest(ITravelRuleProviderCredentials providerCredentials) { + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(NotabeneRegisterWebhookRequest.class); + verify(notabeneApiWrapper).registerWebhook(eq(providerCredentials), requestCaptor.capture()); + NotabeneRegisterWebhookRequest request = requestCaptor.getValue(); + assertNotNull(request); + assertEquals(providerCredentials.getVaspDid(), request.getVaspDid()); + assertEquals("https://localhost:7743/extensions/notabene/webhooks", request.getUrl()); + } + + private void assertUnregisterWebhookRequest(ITravelRuleProviderCredentials providerCredentials) { + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(NotabeneUnregisterWebhookRequest.class); + verify(notabeneApiWrapper).unregisterWebhook(eq(providerCredentials), requestCaptor.capture()); + NotabeneUnregisterWebhookRequest request = requestCaptor.getValue(); + assertNotNull(request); + assertEquals(providerCredentials.getVaspDid(), request.getVaspDid()); + } + + private NotabeneListVaspsResponse createNotabeneListVaspsResponse(NotabeneVaspInfoSimple... vasp) { + NotabeneListVaspsResponse response = new NotabeneListVaspsResponse(); + response.setVasps(Arrays.asList(vasp)); + return response; + } + + private void verifyListAllVaspsCalled(ITravelRuleProviderCredentials providerCredentials) { + ArgumentCaptor captor = ArgumentCaptor.forClass(NotabeneListVaspsQueryParams.class); + verify(notabeneApiWrapper, times(1)).listVasps(eq(providerCredentials), captor.capture()); + NotabeneListVaspsQueryParams params = captor.getValue(); + assertNotNull(params); + assertTrue(params.getAll()); + assertNull(params.getQuery()); + } + + private static NotabeneApiException createNotabeneApiException(int httpStatus) { + NotabeneApiError notabeneApiError = new NotabeneApiError(); + notabeneApiError.setMessage("Test Exception"); + notabeneApiError.setCode(httpStatus); + return new NotabeneApiException(notabeneApiError); + } + +} \ No newline at end of file diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneTransferPublisherTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneTransferPublisherTest.java new file mode 100644 index 000000000..25481638c --- /dev/null +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneTransferPublisherTest.java @@ -0,0 +1,138 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene; + +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferInfo; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +class NotabeneTransferPublisherTest { + + private NotabeneTransferPublisher publisher; + + @BeforeEach + void setUp() { + publisher = NotabeneTransferPublisher.getInstance(); + } + + @Test + void testPublishEvent() { + NotabeneTransferUpdateListener listener = mock(NotabeneTransferUpdateListener.class); + NotabeneTransferInfo transferInfo = createTransferInfo("originatorDid", "beneficiaryDid"); + publisher.registerListener("originatorDid", listener); + + publisher.publishEvent(transferInfo); + + verify(listener, times(1)).onTransferUpdate(transferInfo); + } + + @Test + void testPublishEvent_noListener() { + NotabeneTransferInfo transferInfo = createTransferInfo("originatorDid", "beneficiaryDid"); + + publisher.publishEvent(transferInfo); + } + + @Test + void testPublishEvent_noListenerForVasps() { + NotabeneTransferUpdateListener listener = mock(NotabeneTransferUpdateListener.class); + NotabeneTransferInfo transferInfo = createTransferInfo("originatorDid", "beneficiaryDid"); + publisher.registerListener("anotherDid", listener); + + publisher.publishEvent(transferInfo); + + verify(listener, never()).onTransferUpdate(transferInfo); + } + + @Test + void testPublishEvent_nullOriginatorDid() { + NotabeneTransferUpdateListener listener = mock(NotabeneTransferUpdateListener.class); + NotabeneTransferInfo transferInfo = createTransferInfo(null, "beneficiaryDid"); + publisher.registerListener("originatorDid", listener); + + publisher.publishEvent(transferInfo); + + verify(listener, never()).onTransferUpdate(transferInfo); + } + + @Test + void testPublishEvent_nullBeneficiaryDid() { + NotabeneTransferUpdateListener listener = mock(NotabeneTransferUpdateListener.class); + NotabeneTransferInfo transferInfo = createTransferInfo("originatorDid", null); + publisher.registerListener("beneficiaryDid", listener); + + publisher.publishEvent(transferInfo); + + verify(listener, never()).onTransferUpdate(transferInfo); + } + + @Test + void testPublishEvent_equalOriginatorAndBeneficiary() { + NotabeneTransferUpdateListener listener = mock(NotabeneTransferUpdateListener.class); + NotabeneTransferInfo transferInfo = createTransferInfo("did", "did"); + publisher.registerListener("did", listener); + + publisher.publishEvent(transferInfo); + + verify(listener, times(1)).onTransferUpdate(transferInfo); + } + + @Test + void testPublishEvent_unregisterListener() { + NotabeneTransferUpdateListener listener = mock(NotabeneTransferUpdateListener.class); + NotabeneTransferInfo transferInfo = createTransferInfo("originatorDid", "beneficiaryDid"); + publisher.registerListener("originatorDid", listener); + + publisher.publishEvent(transferInfo); + + verify(listener, times(1)).onTransferUpdate(transferInfo); + + clearInvocations(listener); + publisher.unregisterListener("originatorDid"); + + // Event should not be sent to an unregistered listener + publisher.publishEvent(transferInfo); + + verify(listener, never()).onTransferUpdate(transferInfo); + } + + @Test + void testPublishEvent_multipleListeners() { + NotabeneTransferUpdateListener listener1 = mock(NotabeneTransferUpdateListener.class); + NotabeneTransferUpdateListener listener2 = mock(NotabeneTransferUpdateListener.class); + NotabeneTransferInfo transferInfo = createTransferInfo("originatorDid", "beneficiaryDid"); + + publisher.registerListener("originatorDid", listener1); + publisher.registerListener("beneficiaryDid", listener2); + + publisher.publishEvent(transferInfo); + + verify(listener1, times(1)).onTransferUpdate(transferInfo); + verify(listener2, times(1)).onTransferUpdate(transferInfo); + } + + @Test + void testPublishEvent_duplicateListener() { + NotabeneTransferUpdateListener listener = mock(NotabeneTransferUpdateListener.class); + NotabeneTransferInfo transferInfo = createTransferInfo("originatorDid", "beneficiaryDid"); + + publisher.registerListener("originatorDid", listener); + publisher.registerListener("originatorDid", listener); + + publisher.publishEvent(transferInfo); + + verify(listener, times(1)).onTransferUpdate(transferInfo); + } + + private NotabeneTransferInfo createTransferInfo(String originatorDid, String beneficiaryDid) { + NotabeneTransferInfo transferInfo = new NotabeneTransferInfo(); + transferInfo.setOriginatorVaspDid(originatorDid); + transferInfo.setBeneficiaryVaspDid(beneficiaryDid); + return transferInfo; + } + +} \ No newline at end of file diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneTransferStatusUpdateListenerTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneTransferStatusUpdateListenerTest.java new file mode 100644 index 000000000..85b40ef96 --- /dev/null +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneTransferStatusUpdateListenerTest.java @@ -0,0 +1,66 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene; + +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleTransferUpdateListener; +import com.generalbytes.batm.server.extensions.travelrule.TravelRuleProviderTransferStatus; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferInfo; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferStatus; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.UUID; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +class NotabeneTransferStatusUpdateListenerTest { + + @Mock + private ITravelRuleTransferUpdateListener travelRuleTransferUpdateListener; + + @InjectMocks + private NotabeneTransferStatusUpdateListener listener; + + private static Stream provideValidStatuses() { + return Stream.of( + Arguments.arguments(NotabeneTransferStatus.NEW, TravelRuleProviderTransferStatus.IN_PROGRESS), + Arguments.arguments(NotabeneTransferStatus.MISSING_BENEFICIARY_DATA, TravelRuleProviderTransferStatus.IN_PROGRESS), + Arguments.arguments(NotabeneTransferStatus.WAITING_FOR_INFORMATION, TravelRuleProviderTransferStatus.IN_PROGRESS), + Arguments.arguments(NotabeneTransferStatus.CANCELLED, TravelRuleProviderTransferStatus.REJECTED), + Arguments.arguments(NotabeneTransferStatus.INCOMPLETE, TravelRuleProviderTransferStatus.IN_PROGRESS), + Arguments.arguments(NotabeneTransferStatus.SENT, TravelRuleProviderTransferStatus.IN_PROGRESS), + Arguments.arguments(NotabeneTransferStatus.ACK, TravelRuleProviderTransferStatus.IN_PROGRESS), + Arguments.arguments(NotabeneTransferStatus.ACCEPTED, TravelRuleProviderTransferStatus.APPROVED), + Arguments.arguments(NotabeneTransferStatus.DECLINED, TravelRuleProviderTransferStatus.REJECTED), + Arguments.arguments(NotabeneTransferStatus.REJECTED, TravelRuleProviderTransferStatus.REJECTED), + Arguments.arguments(NotabeneTransferStatus.NOT_READY, TravelRuleProviderTransferStatus.REJECTED), + Arguments.arguments(NotabeneTransferStatus.SAVED, TravelRuleProviderTransferStatus.APPROVED) + ); + } + + @ParameterizedTest + @MethodSource("provideValidStatuses") + void testOnTransferUpdate_completed(NotabeneTransferStatus notabeneTransferStatus, TravelRuleProviderTransferStatus expectedTransferStatus) { + String transferId = UUID.randomUUID().toString(); + NotabeneTransferInfo updatedTransferInfo = new NotabeneTransferInfo(); + updatedTransferInfo.setTransactionRef(transferId); + updatedTransferInfo.setStatus(notabeneTransferStatus); + + listener.onTransferUpdate(updatedTransferInfo); + + verify(travelRuleTransferUpdateListener, times(1)).onTransferStatusUpdate(argThat(event -> { + assertEquals(transferId, event.getTransferPublicId()); + assertEquals(expectedTransferStatus, event.getNewTransferStatus()); + return true; + })); + } + +} \ No newline at end of file diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneTravelRuleProviderTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneTravelRuleProviderTest.java new file mode 100644 index 000000000..d6f937f6f --- /dev/null +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneTravelRuleProviderTest.java @@ -0,0 +1,581 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene; + +import com.generalbytes.batm.server.extensions.travelrule.CryptoWalletType; +import com.generalbytes.batm.server.extensions.travelrule.IIdentityWalletEvaluationRequest; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleNaturalPerson; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleNaturalPersonName; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleProviderCredentials; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleTransferData; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleTransferInfo; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleTransferUpdateListener; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleTransferUpdateRequest; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleVasp; +import com.generalbytes.batm.server.extensions.travelrule.ITravelRuleWalletInfo; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneAddressOwnershipInfoRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneAddressOwnershipInfoResponse; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneBeneficiary; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneCryptoAddressType; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneNameIdentifier; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneOriginator; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabenePerson; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferCreateRequest; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferInfo; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferStatus; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneVaspInfoSimple; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockedConstruction; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.math.BigDecimal; +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockConstruction; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class NotabeneTravelRuleProviderTest { + + @Mock + private NotabeneConfiguration configuration; + @Mock + private NotabeneAuthService notabeneAuthService; + @Mock + private NotabeneService notabeneService; + @Mock + private NotabeneTransferPublisher notabeneTransferPublisher; + + private ITravelRuleProviderCredentials credentials; + private NotabeneTravelRuleProvider provider; + + @BeforeEach + void setUp() { + credentials = createITravelRuleProviderCredentials(); + provider = new NotabeneTravelRuleProvider(credentials, configuration, notabeneAuthService, notabeneService, notabeneTransferPublisher); + } + + @Test + void testGetName() { + assertEquals("Notabene Travel Rule Provider", provider.getName()); + } + + private static Stream provideTestGetWalletInfo() { + return Stream.of( + Arguments.arguments(NotabeneCryptoAddressType.HOSTED, "ownerVaspDid", CryptoWalletType.CUSTODIAL), + Arguments.arguments(NotabeneCryptoAddressType.UNHOSTED, null, CryptoWalletType.UNHOSTED), + Arguments.arguments(NotabeneCryptoAddressType.UNKNOWN, null, CryptoWalletType.UNKNOWN), + Arguments.arguments(null, null, CryptoWalletType.UNKNOWN) + ); + } + + @ParameterizedTest + @MethodSource("provideTestGetWalletInfo") + void testGetWalletInfo(NotabeneCryptoAddressType cryptoAddressType, String ownerVaspDid, CryptoWalletType expectedWalletType) { + IIdentityWalletEvaluationRequest walletContext = createWalletContext(); + NotabeneAddressOwnershipInfoResponse response = new NotabeneAddressOwnershipInfoResponse(); + response.setAddressType(cryptoAddressType); + response.setOwnerVaspDid(ownerVaspDid); + + when(notabeneService.getAddressOwnershipInformation(any(), any())).thenReturn(response); + + ITravelRuleWalletInfo walletInfo = provider.getWalletInfo(walletContext); + + assertNotNull(walletInfo); + assertEquals(expectedWalletType, walletInfo.getCryptoWalletType()); + assertEquals(ownerVaspDid, walletInfo.getOwnerVaspDid()); + verifyGetAddressOwnershipCalled(walletContext); + } + + @Test + void testGetWalletInfo_nullAddressOwnershipInfo() { + IIdentityWalletEvaluationRequest walletContext = createWalletContext(); + + when(notabeneService.getAddressOwnershipInformation(any(), any())).thenReturn(null); + + ITravelRuleWalletInfo walletInfo = provider.getWalletInfo(walletContext); + + assertNotNull(walletInfo); + assertEquals(CryptoWalletType.UNKNOWN, walletInfo.getCryptoWalletType()); + assertNull(walletInfo.getOwnerVaspDid()); + } + + private static Stream provideTestGetAllVaspsValid() { + return Stream.of( + Arguments.arguments(List.of( + createVaspInfoSimple(1), + createVaspInfoSimple(2), + createVaspInfoSimple(3) + ), new int[]{1, 2, 3}), + Arguments.arguments(List.of(), new int[0]) + ); + } + + @ParameterizedTest + @MethodSource("provideTestGetAllVaspsValid") + void testGetAllVasps_valid(List notabeneVasps, int[] expectedVasps) { + when(notabeneService.getAllVasps(any())).thenReturn(notabeneVasps); + + List result = provider.getAllVasps(); + assertNotNull(result); + assertEquals(notabeneVasps.size(), result.size()); + for (int i = 0; i < expectedVasps.length; i++) { + ITravelRuleVasp vasp = result.get(i); + assertVasp(expectedVasps[i], vasp); + } + } + + @Test + void testCreateTransfer_valid() { + ITravelRuleTransferData outgoingTransferData = createTravelRuleMessageData(); + NotabeneTransferInfo notabeneTransferInfo = createNotabeneTransferInfo(NotabeneTransferStatus.NEW); + + when(configuration.isAutomaticApprovalOfOutgoingTransfersEnabled()).thenReturn(false); + when(notabeneService.createTransfer(any(), any())).thenReturn(notabeneTransferInfo); + + ITravelRuleTransferInfo result = provider.createTransfer(outgoingTransferData); + + assertNotNull(result); + assertEquals("id", result.getId()); + verify(notabeneService).createTransfer(eq(credentials), argThat(request -> { + assertCreateRequest(outgoingTransferData, request); + return true; + })); + } + + @Test + void testCreateTransfer_exception() { + ITravelRuleTransferData outgoingTransferData = createTravelRuleMessageData(); + + when(notabeneService.createTransfer(any(), any())).thenThrow(new RuntimeException("Test Exception")); + + RuntimeException exception = assertThrows(RuntimeException.class, () -> provider.createTransfer(outgoingTransferData)); + + assertEquals("Test Exception", exception.getMessage()); + verify(notabeneService).createTransfer(eq(credentials), argThat(request -> { + assertCreateRequest(outgoingTransferData, request); + return true; + })); + } + + @Test + void testCreateTransfer_automaticApproval() { + ITravelRuleTransferData outgoingTransferData = createTravelRuleMessageData(); + NotabeneTransferInfo notabeneTransferInfo = createNotabeneTransferInfo(NotabeneTransferStatus.NEW); + + when(configuration.isAutomaticApprovalOfOutgoingTransfersEnabled()).thenReturn(true); + when(notabeneService.createTransfer(any(), any())).thenReturn(notabeneTransferInfo); + when(notabeneService.approveTransfer(any(), any())).thenAnswer(invocation -> { + notabeneTransferInfo.setStatus(NotabeneTransferStatus.SENT); + return notabeneTransferInfo; + }); + + ITravelRuleTransferInfo result = provider.createTransfer(outgoingTransferData); + + assertNotNull(result); + assertEquals("id", result.getId()); + verify(notabeneService, times(1)).approveTransfer(credentials, "id"); + } + + @Test + void testCreateTransfer_automaticApprovalSkip() { + ITravelRuleTransferData outgoingTransferData = createTravelRuleMessageData(); + NotabeneTransferInfo notabeneTransferInfo = createNotabeneTransferInfo(NotabeneTransferStatus.SENT); + + when(configuration.isAutomaticApprovalOfOutgoingTransfersEnabled()).thenReturn(true); + when(notabeneService.createTransfer(any(), any())).thenReturn(notabeneTransferInfo); + + ITravelRuleTransferInfo result = provider.createTransfer(outgoingTransferData); + + assertNotNull(result); + assertEquals("id", result.getId()); + verify(notabeneService, never()).approveTransfer(any(), any()); + } + + @Test + void testCreateTransfer_automaticApprovalFail() { + ITravelRuleTransferData outgoingTransferData = createTravelRuleMessageData(); + NotabeneTransferInfo notabeneTransferInfo = createNotabeneTransferInfo(NotabeneTransferStatus.NEW); + + when(configuration.isAutomaticApprovalOfOutgoingTransfersEnabled()).thenReturn(true); + when(notabeneService.createTransfer(any(), any())).thenReturn(notabeneTransferInfo); + when(notabeneService.approveTransfer(any(), any())).thenReturn(null); + + ITravelRuleTransferInfo result = provider.createTransfer(outgoingTransferData); + + assertNotNull(result); + assertEquals("id", result.getId()); + verify(notabeneService, times(1)).approveTransfer(credentials, "id"); + } + + @Test + void testCreateTransfer_automaticApprovalException() { + ITravelRuleTransferData outgoingTransferData = createTravelRuleMessageData(); + NotabeneTransferInfo notabeneTransferInfo = createNotabeneTransferInfo(NotabeneTransferStatus.NEW); + + when(configuration.isAutomaticApprovalOfOutgoingTransfersEnabled()).thenReturn(true); + when(notabeneService.createTransfer(any(), any())).thenReturn(notabeneTransferInfo); + when(notabeneService.approveTransfer(any(), any())).thenThrow(new RuntimeException("Test Exception")); + + RuntimeException exception = assertThrows(RuntimeException.class, () -> provider.createTransfer(outgoingTransferData)); + + assertEquals("Test Exception", exception.getMessage()); + verify(notabeneService, times(1)).approveTransfer(credentials, "id"); + } + + @Test + void testCreateTransfer_fail() { + ITravelRuleTransferData outgoingTransferData = createTravelRuleMessageData(); + + when(notabeneService.createTransfer(any(), any())).thenReturn(null); + + ITravelRuleTransferInfo result = provider.createTransfer(outgoingTransferData); + + assertNull(result); + verify(notabeneService).createTransfer(eq(credentials), argThat(request -> { + assertCreateRequest(outgoingTransferData, request); + return true; + })); + } + + @Test + void testRegisterListener_valid() { + ITravelRuleTransferUpdateListener listener = mock(ITravelRuleTransferUpdateListener.class); + when(notabeneService.registerWebhook(any())).thenReturn(true); + + try (MockedConstruction listenerMockedConstruction = mockNotabeneListenerConstruction(listener)) { + provider.registerStatusUpdateListener(listener); + + assertEquals(1, listenerMockedConstruction.constructed().size()); + NotabeneTransferStatusUpdateListener notabeneListener = listenerMockedConstruction.constructed().get(0); + verify(notabeneTransferPublisher, times(1)).registerListener("vaspDid", notabeneListener); + } + } + + @Test + void testRegisterListener_fail() { + ITravelRuleTransferUpdateListener listener = mock(ITravelRuleTransferUpdateListener.class); + when(notabeneService.registerWebhook(any())).thenReturn(false); + + try (MockedConstruction listenerMockedConstruction = mockNotabeneListenerConstruction(listener)) { + provider.registerStatusUpdateListener(listener); + + assertEquals(0, listenerMockedConstruction.constructed().size()); + verify(notabeneTransferPublisher, never()).registerListener(any(), any()); + } + } + + @Test + void testUnregisterListener_valid() { + provider.unregisterStatusUpdateListener(); + + verify(notabeneTransferPublisher, times(1)).unregisterListener("vaspDid"); + } + + @Test + void testUpdateTransfer_nullResult() { + ITravelRuleTransferUpdateRequest request = mock(ITravelRuleTransferUpdateRequest.class); + when(request.getId()).thenReturn("publicId"); + when(request.getTransactionHash()).thenReturn("transactionHash"); + + when(notabeneService.updateTransfer(any(), any())).thenReturn(null); + + ITravelRuleTransferInfo result = provider.updateTransfer(request); + + assertNull(result); + verify(notabeneService, times(1)).updateTransfer(eq(credentials), argThat(notabeneRequest -> { + assertEquals("publicId", notabeneRequest.getId()); + assertEquals("transactionHash", notabeneRequest.getTxHash()); + return true; + })); + } + + @Test + void testUpdateTransfer_valid() { + ITravelRuleTransferUpdateRequest request = mock(ITravelRuleTransferUpdateRequest.class); + when(request.getId()).thenReturn("publicId"); + when(request.getTransactionHash()).thenReturn("transactionHash"); + NotabeneTransferInfo notabeneTransferInfo = new NotabeneTransferInfo(); + notabeneTransferInfo.setId("id"); + + when(notabeneService.updateTransfer(any(), any())).thenReturn(notabeneTransferInfo); + + ITravelRuleTransferInfo result = provider.updateTransfer(request); + + assertNotNull(result); + assertEquals("id", result.getId()); + verify(notabeneService, times(1)).updateTransfer(eq(credentials), argThat(notabeneRequest -> { + assertEquals("publicId", notabeneRequest.getId()); + assertEquals("transactionHash", notabeneRequest.getTxHash()); + return true; + })); + } + + @Test + void testNotifyProviderConfigurationChanged() { + provider.notifyProviderConfigurationChanged(); + verify(notabeneAuthService).removeAccessToken(credentials); + } + + @Test + void testProviderConfiguration_valid() { + when(notabeneService.testProviderCredentials(credentials)).thenReturn(true); + + boolean validationResult = provider.testProviderConfiguration(); + + assertTrue(validationResult); + } + + @Test + void testProviderConfiguration_invalid() { + when(notabeneService.testProviderCredentials(credentials)).thenReturn(false); + + boolean validationResult = provider.testProviderConfiguration(); + + assertFalse(validationResult); + } + + private MockedConstruction mockNotabeneListenerConstruction( + ITravelRuleTransferUpdateListener listener) { + return mockConstruction(NotabeneTransferStatusUpdateListener.class, (mock, context) -> { + assertInstanceOf(ITravelRuleTransferUpdateListener.class, context.arguments().get(0)); + assertEquals(listener, context.arguments().get(0)); + }); + } + + private void assertCreateRequest(ITravelRuleTransferData expected, NotabeneTransferCreateRequest actual) { + assertEquals(expected.getPublicId(), actual.getTransactionRef()); + assertOriginator(expected.getOriginator(), actual.getOriginator()); + assertBeneficiary(expected, actual.getBeneficiary()); + assertEquals(expected.getOriginatorVasp().getDid(), actual.getOriginatorVaspDid()); + assertEquals(expected.getBeneficiaryVasp().getDid(), actual.getBeneficiaryVaspDid()); + assertEquals(expected.getTransactionAsset(), actual.getTransactionAsset()); + assertEquals(String.valueOf(expected.getTransactionAmount()), actual.getTransactionAmount()); + assertEquals(expected.getDestinationAddress(), actual.getTransactionBlockchainInfo().getDestination()); + assertEquals(expected.getTransactionHash(), actual.getTransactionBlockchainInfo().getTxHash()); + } + + private void assertOriginator(ITravelRuleNaturalPerson expected, NotabeneOriginator originator) { + NotabenePerson person = originator.getOriginatorPersons().get(0); + assertPerson(expected, person); + assertNotNull(originator.getAccountNumber()); + assertEquals(1, originator.getAccountNumber().size()); + assertEquals(expected.getIdentityPublicId(), originator.getAccountNumber().get(0)); + } + + private void assertBeneficiary(ITravelRuleTransferData outgoingTransferData, NotabeneBeneficiary beneficiary) { + NotabenePerson person = beneficiary.getBeneficiaryPersons().get(0); + assertNotNull(beneficiary.getAccountNumber()); + assertEquals(1, beneficiary.getAccountNumber().size()); + assertEquals(outgoingTransferData.getBeneficiary().getIdentityPublicId(), beneficiary.getAccountNumber().get(0)); + assertPerson(outgoingTransferData.getBeneficiary(), person); + } + + private void assertPerson(ITravelRuleNaturalPerson expected, NotabenePerson person) { + NotabeneNameIdentifier nameIdentifier = person.getNaturalPerson().getName().get(0).getNameIdentifier().get(0); + assertEquals(expected.getName().getPrimaryName(), nameIdentifier.getPrimaryIdentifier()); + assertEquals(expected.getName().getSecondaryName(), nameIdentifier.getSecondaryIdentifier()); + if (expected.getName().getNameType() != null) { + assertEquals(expected.getName().getNameType(), nameIdentifier.getNameIdentifierType().name()); + } else { + assertNull(nameIdentifier.getNameIdentifierType()); + } + assertEquals(expected.getIdentityPublicId(), person.getNaturalPerson().getCustomerIdentification()); + } + + private ITravelRuleTransferData createTravelRuleMessageData() { + return new ITravelRuleTransferData() { + @Override + public String getPublicId() { + return "publicId"; + } + + @Override + public ITravelRuleNaturalPerson getOriginator() { + return createCounterParty("originator", "ORIGINATOR", "LEGL"); + } + + @Override + public ITravelRuleNaturalPerson getBeneficiary() { + return createCounterParty("beneficiary", "BENEFICIARY", null); + } + + @Override + public ITravelRuleVasp getOriginatorVasp() { + return createVasp("originatorVaspDid"); + } + + @Override + public ITravelRuleVasp getBeneficiaryVasp() { + return createVasp("beneficiaryVaspDid"); + } + + @Override + public String getTransactionAsset() { + return "BTC"; + } + + @Override + public long getTransactionAmount() { + return 1_000_000_000; + } + + @Override + public String getDestinationAddress() { + return "destinationAddress"; + } + + @Override + public BigDecimal getFiatAmount() { + return BigDecimal.valueOf(100); + } + + @Override + public String getFiatCurrency() { + return "CZK"; + } + + @Override + public String getTransactionHash() { + return "transactionHash"; + } + }; + } + + private ITravelRuleVasp createVasp(String did) { + return new ITravelRuleVasp() { + @Override + public String getDid() { + return did; + } + + @Override + public String getName() { + return null; + } + }; + } + + private ITravelRuleNaturalPerson createCounterParty(String primaryName, String secondaryName, String nameType) { + ITravelRuleNaturalPersonName name = new ITravelRuleNaturalPersonName() { + @Override + public String getPrimaryName() { + return primaryName; + } + + @Override + public String getSecondaryName() { + return secondaryName; + } + + @Override + public String getNameType() { + return nameType; + } + }; + + return new ITravelRuleNaturalPerson() { + @Override + public ITravelRuleNaturalPersonName getName() { + return name; + } + + @Override + public String getIdentityPublicId() { + return primaryName + "identityPublicId"; + } + }; + } + + private void verifyGetAddressOwnershipCalled(IIdentityWalletEvaluationRequest walletContext) { + ArgumentCaptor captor = ArgumentCaptor.forClass(NotabeneAddressOwnershipInfoRequest.class); + verify(notabeneService).getAddressOwnershipInformation(eq(credentials), captor.capture()); + NotabeneAddressOwnershipInfoRequest request = captor.getValue(); + assertEquals(walletContext.getCryptoAddress(), request.getAddress()); + assertEquals(walletContext.getCryptocurrency(), request.getAsset()); + assertEquals(credentials.getVaspDid(), request.getVaspDid()); + } + + private IIdentityWalletEvaluationRequest createWalletContext() { + return new IIdentityWalletEvaluationRequest() { + @Override + public String getIdentityPublicId() { + return "identityPublicId"; + } + + @Override + public String getIdentityExternalId() { + return "identityExternalId"; + } + + @Override + public String getCryptoAddress() { + return "cryptoAddress"; + } + + @Override + public String getCryptocurrency() { + return "cryptocurrency"; + } + }; + } + + private void assertVasp(int id, ITravelRuleVasp vasp) { + assertNotNull(vasp); + assertEquals("did" + id, vasp.getDid()); + assertEquals("name" + id, vasp.getName()); + } + + private static NotabeneVaspInfoSimple createVaspInfoSimple(int id) { + NotabeneVaspInfoSimple vaspInfoSimple = new NotabeneVaspInfoSimple(); + vaspInfoSimple.setDid("did" + id); + vaspInfoSimple.setName("name" + id); + return vaspInfoSimple; + } + + private static NotabeneTransferInfo createNotabeneTransferInfo(NotabeneTransferStatus status) { + NotabeneTransferInfo notabeneTransferInfo = new NotabeneTransferInfo(); + notabeneTransferInfo.setId("id"); + notabeneTransferInfo.setStatus(status); + return notabeneTransferInfo; + } + + private static ITravelRuleProviderCredentials createITravelRuleProviderCredentials() { + return new ITravelRuleProviderCredentials() { + @Override + public String getClientId() { + return "clientId"; + } + + @Override + public String getClientSecret() { + return "clientSecret"; + } + + @Override + public String getVaspDid() { + return "vaspDid"; + } + }; + } +} \ No newline at end of file diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneWebhookRestServiceTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneWebhookRestServiceTest.java new file mode 100644 index 000000000..576fd3e97 --- /dev/null +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/travelrule/notabene/NotabeneWebhookRestServiceTest.java @@ -0,0 +1,67 @@ +package com.generalbytes.batm.server.extensions.travelrule.notabene; + +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneTransferInfo; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneWebhookMessage; +import com.generalbytes.batm.server.extensions.travelrule.notabene.dto.NotabeneWebhookMessagePayload; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +class NotabeneWebhookRestServiceTest { + + private NotabeneWebhookRestService notabeneWebhookRestService; + + @Test + void testGetPrefixPath() { + notabeneWebhookRestService = new NotabeneWebhookRestService(); + String prefixPath = notabeneWebhookRestService.getPrefixPath(); + assertEquals("notabene", prefixPath); + } + + @Test + void testGetImplementation() { + notabeneWebhookRestService = new NotabeneWebhookRestService(); + Class implementation = notabeneWebhookRestService.getImplementation(); + assertEquals(NotabeneWebhookRestService.class, implementation); + } + + @Test + void testHandleWebhookMessage() { + NotabeneWebhookMessage message = new NotabeneWebhookMessage(); + message.setMessage(NotabeneWebhookMessage.TYPE_TRANSACTION_UPDATED); + NotabeneWebhookMessagePayload payload = new NotabeneWebhookMessagePayload(); + NotabeneTransferInfo transferInfo = new NotabeneTransferInfo(); + payload.setTransaction(transferInfo); + message.setPayload(payload); + + NotabeneTransferPublisher transferPublisher = mock(NotabeneTransferPublisher.class); + + try (MockedStatic transferPublisherMock = mockStatic(NotabeneTransferPublisher.class)) { + transferPublisherMock.when(NotabeneTransferPublisher::getInstance).thenReturn(transferPublisher); + notabeneWebhookRestService = new NotabeneWebhookRestService(); + notabeneWebhookRestService.handleWebhookMessage(message); + verify(transferPublisher).publishEvent(transferInfo); + } + } + + @Test + void testHandleWebhookMessage_notUpdate() { + NotabeneWebhookMessage message = new NotabeneWebhookMessage(); + message.setMessage("different message"); + + NotabeneTransferPublisher transferPublisher = mock(NotabeneTransferPublisher.class); + + try (MockedStatic transferPublisherMock = mockStatic(NotabeneTransferPublisher.class)) { + transferPublisherMock.when(NotabeneTransferPublisher::getInstance).thenReturn(transferPublisher); + notabeneWebhookRestService = new NotabeneWebhookRestService(); + notabeneWebhookRestService.handleWebhookMessage(message); + verify(transferPublisher, never()).publishEvent(any()); + } + } +} \ No newline at end of file diff --git a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/util/OrderBookPriceCalculatorTest.java b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/util/OrderBookPriceCalculatorTest.java index afa2d33ec..3411a2195 100644 --- a/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/util/OrderBookPriceCalculatorTest.java +++ b/server_extensions_extra/src/test/java/com/generalbytes/batm/server/extensions/util/OrderBookPriceCalculatorTest.java @@ -1,15 +1,16 @@ package com.generalbytes.batm.server.extensions.util; import org.assertj.core.api.Assertions; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; -public class OrderBookPriceCalculatorTest { +import static org.junit.jupiter.api.Assertions.assertThrows; + +class OrderBookPriceCalculatorTest { private static class Order { public final BigDecimal price; @@ -23,13 +24,13 @@ private Order(int price, int amount) { private static final OrderBookPriceCalculator calc = new OrderBookPriceCalculator<>(order -> order.price, order -> order.amount); @Test - public void test() throws IOException { + void test() throws IOException { List asks = new ArrayList<>(); List bids = new ArrayList<>(); - Assert.assertThrows(IllegalArgumentException.class, () -> calc.getBuyPrice(BigDecimal.ONE, asks)); - Assert.assertThrows(IllegalArgumentException.class, () -> calc.getSellPrice(BigDecimal.ONE, bids)); + assertThrows(IllegalArgumentException.class, () -> calc.getBuyPrice(BigDecimal.ONE, asks)); + assertThrows(IllegalArgumentException.class, () -> calc.getSellPrice(BigDecimal.ONE, bids)); bids.add(new Order(50, 2)); bids.add(new Order(70, 2)); @@ -51,8 +52,8 @@ public void test() throws IOException { Assertions.assertThat(calc.getBuyPrice(new BigDecimal("6"), asks)).isEqualByComparingTo("150"); Assertions.assertThat(calc.getSellPrice(new BigDecimal("6"), bids)).isEqualByComparingTo("50"); - Assert.assertThrows(IllegalArgumentException.class, () -> calc.getBuyPrice(new BigDecimal("6.1"), asks)); - Assert.assertThrows(IllegalArgumentException.class, () -> calc.getSellPrice(new BigDecimal("6.1"), bids)); + assertThrows(IllegalArgumentException.class, () -> calc.getBuyPrice(new BigDecimal("6.1"), asks)); + assertThrows(IllegalArgumentException.class, () -> calc.getSellPrice(new BigDecimal("6.1"), bids)); }