diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index c1ffdb6..5c6a693 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -24,6 +24,6 @@ jobs:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
- # Build the .jar
+ # Build the .jar without running test suite
- name: Build
run: mvn clean && mvn install -DskipTests=true
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index c6f0698..bcf739a 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -15,7 +15,7 @@ on:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
- build:
+ test:
# The type of runner that the job will run on
runs-on: ubuntu-latest
@@ -24,6 +24,6 @@ jobs:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
- # Runs the test suite [WIP]
- - name: Build
- run: mvn test
+ # Runs the test suite
+ - name: Test
+ run: mvn clean install
diff --git a/.gitignore b/.gitignore
index 549e00a..67ce31b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,3 +31,6 @@ build/
### VS Code ###
.vscode/
+
+### db
+data.mv.db
\ No newline at end of file
diff --git a/README.md b/README.md
index dd057e9..a3866b6 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ If successfully settled the equivalent amount in Monero is sent
3. Run bitcoind on [regtest](https://developer.bitcoin.org/examples/testing.html)
4. Setup LND nodes for invoice generation and settling. *[Polar](https://lightningpolar.com/) is a cool tool!
5. Run Monero on [stagenet](https://monerodocs.org/infrastructure/networks/)
-6. H2 db runs at host/h2-console. Execute the `src/main/resources/schema.sql` first
+6. H2 db runs at host/h2-console.
7. Currently working on Bitcoin core 0.21, LND 0.12.x, Debian 10, Java 11, Maven 3.6 and Monero 0.17.2
NOTE: currently have an issue with Monero digest authentication rpc calls, so use `--disable-rpc-login`
@@ -38,7 +38,7 @@ request:
```json
{
- "amount": 0.0123,
+ "amount": 0.123,
"address": "54gqcJZAtgzBFnQWEQHec3RoWfmoHqL4H8sASqdQMGshfqdpG1fzT5ddCpz9y4C2MwQkB5GE2o6vUVCGKbokJJa6S6NSatn"
}
```
@@ -47,12 +47,18 @@ response:
```json
{
- "quoteId": "e8754cd5189125b46490b30cb958792e88ae34e76b954d32ad70ced27ac21c2a",
+ "quoteId": "63eb4534535a4c4afa9455f7dacde8cecbbac91e2bcd390407e1b88704a9a758",
"address": "54gqcJZAtgzBFnQWEQHec3RoWfmoHqL4H8sASqdQMGshfqdpG1fzT5ddCpz9y4C2MwQkB5GE2o6vUVCGKbokJJa6S6NSatn",
"isValidAddress": true,
- "amount": 0.0123,
- "rate": 0.006363,
- "invoice": "lnbcrt78260n1psw3ax6pp5ap65e4gcjyjmgeyskvxtjkre96y2ud88dw256v4dwr8dy7kzrs4qdq8d4shxuccqzpgxqzjcsp5wyywgyzhek48wdpwq3jl04jn203d07s9huwpl7dyducstjh2eqcs9qyyssq6j27kf9vzydqqhqaal2cdryzn7u4xgm3vnltvj4qsd9aqhavpwcre5q4sy0megg005gj0zycs3j3l3nvleqqxklppknjgug30sauq8cpe2sm74"
+ "reserveProof": {
+ "signature": "ReserveProofV21AhtWZDjV1SG7AcQFSxfSVWZvQB9QG99kgr2havWLjWgewkBnnKYBt3UqQycx7A9sTaNYfiCo8PLGi28kjP7f9SvN16QNUMNaLKH7kuqySYJ4kYtnPT8qPnHK72weEpQXZhmAm3ebXEjZiH9wskFnVEfVjeCBegqcAVNsXjBZHfv95NZBoE4MgKZvfDT2ank1cqLj7VLUyC4pGVR2Y8bNdv8R1gjjQEQFo6r4YFcPKUz59k6yQ1iokWr6ZJwEauMviEk5CNEK8XYUr47TWJTzM5S3whFW5NhDZFeQ1fdsHTHbV332kwcHoDjGf3ZKaeGa5hNMHbb1XjjM5MURdHR6N59vHXPkN3xTnmZd2k1d6Dg8btwutBZujBBzWT5QNswm1V4ewutYTBBcg1cT8XsZh5MtG7cpobgaHGYYxEtGSfpD9R3agCJBpF5EZ9vsm5",
+ "proofAddress": "56fK1PpmCjz5CEiAb8dbpyQZtpJ8s4B2WaXPqyFD2FU9DUobuSDJZztEPeppvAqT2DPWcdp7qtW6KasCbYoWJC7qBcwWrSH"
+ },
+ "minSwapAmt": 10000,
+ "maxSwapAmt": 10000000,
+ "amount": 0.123,
+ "rate": 0.00629129,
+ "invoice": "lnbcrt773820n1ps0xdf8pp5v0452dzntfxy47552hma4n0gem9m4jg790xnjpq8uxugwp9f5avqdq8d4shxuccqzpgxqzjcsp5a75z3kfuwvas78t2a8rmm7j04su4e7t2dwh02x3e0dvwpc6w4urs9qyyssqqqryuthw0sgmtpwymmqjue89ltsre8vnh9uzrey9fs46tynqfk4rxnq5jwyjwvq3vksndfklxa578540zhuu9dprjweyezqjhcg9n8qp068g75"
}
```
@@ -66,19 +72,54 @@ request:
```json
{
- "hash": "e8754cd5189125b46490b30cb958792e88ae34e76b954d32ad70ced27ac21c2a"
+ "hash": "63eb4534535a4c4afa9455f7dacde8cecbbac91e2bcd390407e1b88704a9a758"
}
```
response:
+lncli
+
+```bash
+user@server:~$ lncli -n regtest payinvoice $PAY_REQ
+Payment hash: 63eb4534535a4c4afa9455f7dacde8cecbbac91e2bcd390407e1b88704a9a758
+Description: mass
+Amount (in satoshis): 77382
+Fee limit (in satoshis): 77382
+Destination: 03e420f400087f0640ee6dfd5b0b589908133c8cf36a737e2d0c3c908661477597
+Confirm payment (yes/no): yes
++------------+--------------+--------------+--------------+-----+----------+-----------------+----------------------+
+| HTLC_STATE | ATTEMPT_TIME | RESOLVE_TIME | RECEIVER_AMT | FEE | TIMELOCK | CHAN_OUT | ROUTE |
++------------+--------------+--------------+--------------+-----+----------+-----------------+----------------------+
+| SUCCEEDED | 0.041 | 33.868 | 77382 | 0 | 792 | 713583046557696 | 03e420f400087f0640ee |
++------------+--------------+--------------+--------------+-----+----------+-----------------+----------------------+
+Amount + fee: 77382 + 0 sat
+Payment hash: 63eb4534535a4c4afa9455f7dacde8cecbbac91e2bcd390407e1b88704a9a758
+Payment status: SUCCEEDED, preimage: cb4605aa339a21d70e20db617a2853214759999cac90c35e5a65fd2462bc0447
+```
+
```json
+
{
- "hash": "e8754cd5189125b46490b30cb958792e88ae34e76b954d32ad70ced27ac21c2a",
- "txId": "fc30f5dceccc9a5514af8ec6d01e2bd8405282382a973ed8185d8d2ac8a03934"
+ "hash": "63eb4534535a4c4afa9455f7dacde8cecbbac91e2bcd390407e1b88704a9a758",
+ "metadata": "02000102000b8e8ee801a5a31b..."
}
+
```
+relay the transaction with [relay_tx](https://web.getmonero.org/resources/developer-guides/wallet-rpc.html#relay_tx)
+
+## Tests
+
+MASS uses JUnit5 - [junit-jupiter](https://junit.org/junit5/) framework
+
+Run `mvn clean install` from the root directory
+
+View test coverage with web browser `./target/site/jacoco/index.htm`
+
+![image](https://user-images.githubusercontent.com/13033037/126047819-09fe351a-be62-4bf9-bd5f-cb3580862c6e.png)
+
+
## TODOs
-Refund / Cancel logic, Tests, etc
+See [milestones](https://github.com/hyahatiph-labs/mass/milestones)
diff --git a/api.http b/api.http
index a991789..3a2537d 100644
--- a/api.http
+++ b/api.http
@@ -9,7 +9,7 @@ Content-Type: application/json
{
"amount": 0.123,
- "address": "54gqcJZAtgzBFnQWEQHec3RoWfmoHqL4H8sASqdQMGshfqdpG1fzT5ddCpz9y4C2MwQkB5GE2o6vUVCGKbokJJa6S6NSatn"
+ "address": "56fK1PpmCjz5CEiAb8dbpyQZtpJ8s4B2WaXPqyFD2FU9DUobuSDJZztEPeppvAqT2DPWcdp7qtW6KasCbYoWJC7qBcwWrSH"
}
### Validate Monero address
@@ -57,7 +57,7 @@ POST http://localhost:6789/swap/xmr
Content-Type: application/json
{
- "hash": "fbbdbe08b60d66514dfa295f9f192413bb549f217479fabf5ae3887f1ccdc1a2"
+ "hash": "2161997ea4adcaa2806c0a0e66f30b3ac48ed9f969108742a6b163148f5c7b6c"
}
### get balances of xmr wallet
diff --git a/pom.xml b/pom.xml
index 237d427..6fc6f6b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,8 @@
org.hiahatf
mass
- 0.0.1-SNAPSHOT
+
+ 0.1.0-beta
mass
lightning powered xmr swaps
@@ -39,6 +40,65 @@
runtime
true
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 5.7.2
+ test
+
+
+ org.junit.platform
+ junit-platform-runner
+ 1.2.0
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+ 5.2.0
+ test
+
+
+ org.mockito
+ mockito-core
+ 3.11.2
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ 2.23.0
+ test
+
+
+ com.squareup.okhttp3
+ okhttp
+ 4.0.1
+ test
+
+
+ com.squareup.okhttp3
+ mockwebserver
+ 4.0.1
+ test
+
+
+ io.projectreactor
+ reactor-test
+ test
+ 3.2.3.RELEASE
+
+
org.projectlombok
lombok
@@ -80,6 +140,7 @@
30.1.1-jre
+
@@ -89,6 +150,13 @@
pom
import
+
+ org.junit
+ junit-bom
+ 5.7.2
+ pom
+ import
+
@@ -106,6 +174,61 @@
+
+ maven-surefire-plugin
+ 2.19.1
+
+
+ org.junit.platform
+ junit-platform-surefire-provider
+ 1.0.1
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.7
+
+
+
+ prepare-agent
+
+
+
+ report
+ prepare-package
+
+ report
+
+
+
+ jacoco-check
+
+ check
+
+
+
+
+
+ BUNDLE
+
+
+ LINE
+ COVEREDRATIO
+ 0.80
+
+
+
+
+
+
+ **/MassApplication.class
+
+
+
+
+
diff --git a/src/main/java/org/hiahatf/mass/controllers/SwapController.java b/src/main/java/org/hiahatf/mass/controllers/SwapController.java
index 8a0781b..fdee508 100644
--- a/src/main/java/org/hiahatf/mass/controllers/SwapController.java
+++ b/src/main/java/org/hiahatf/mass/controllers/SwapController.java
@@ -25,7 +25,7 @@ public class SwapController extends BaseController {
/**
* Swap Controller constructor dependency injection
- * @param quoteService
+ * @param service
*/
@Autowired
public SwapController(SwapService service) {
@@ -40,7 +40,7 @@ public SwapController(SwapService service) {
*/
@PostMapping(Constants.XMR_SWAP_PATH)
@ResponseStatus(HttpStatus.CREATED)
- public Mono fetchMoneroQuote(@RequestBody SwapRequest request) {
+ public Mono fetchMoneroSwap(@RequestBody SwapRequest request) {
return swapService.processMoneroSwap(request);
}
diff --git a/src/main/java/org/hiahatf/mass/models/Constants.java b/src/main/java/org/hiahatf/mass/models/Constants.java
index b5ad561..a217ddb 100644
--- a/src/main/java/org/hiahatf/mass/models/Constants.java
+++ b/src/main/java/org/hiahatf/mass/models/Constants.java
@@ -57,9 +57,9 @@ public final class Constants {
public static final String PARSE_RATE_MSG = "parsed rate: {} => real rate: {}";
// quote service values
-
public static final String SHA_256 = "SHA-256";
public static final Long COIN = 100000000L;
+ public static final String RP_ADDRESS = "${rp-address}";
// swap service values
public static final String MIN_PAY = "${min-pay}";
diff --git a/src/main/java/org/hiahatf/mass/models/monero/Quote.java b/src/main/java/org/hiahatf/mass/models/monero/Quote.java
index a531ba0..4a7f14b 100644
--- a/src/main/java/org/hiahatf/mass/models/monero/Quote.java
+++ b/src/main/java/org/hiahatf/mass/models/monero/Quote.java
@@ -20,7 +20,7 @@ public class Quote {
// proof of address validity
private Boolean isValidAddress;
// reserve proof
- private String reserveProof;
+ private ReserveProof reserveProof;
// minimum swap amount in satoshis
private Long minSwapAmt;
// maximum swap amount in satoshis
diff --git a/src/main/java/org/hiahatf/mass/models/monero/ReserveProof.java b/src/main/java/org/hiahatf/mass/models/monero/ReserveProof.java
new file mode 100644
index 0000000..9ec1c13
--- /dev/null
+++ b/src/main/java/org/hiahatf/mass/models/monero/ReserveProof.java
@@ -0,0 +1,22 @@
+package org.hiahatf.mass.models.monero;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * POJO for the quote proof
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class ReserveProof {
+ // proof signature
+ private String signature;
+ // proof address
+ // configure in application.yml
+ // TODO: dynamic configuration
+ private String proofAddress;
+}
diff --git a/src/main/java/org/hiahatf/mass/services/monero/QuoteService.java b/src/main/java/org/hiahatf/mass/services/monero/QuoteService.java
index b4ce657..78bc27a 100644
--- a/src/main/java/org/hiahatf/mass/services/monero/QuoteService.java
+++ b/src/main/java/org/hiahatf/mass/services/monero/QuoteService.java
@@ -18,6 +18,7 @@
import org.hiahatf.mass.models.Constants;
import org.hiahatf.mass.models.monero.Quote;
import org.hiahatf.mass.models.monero.Request;
+import org.hiahatf.mass.models.monero.ReserveProof;
import org.hiahatf.mass.models.monero.XmrQuoteTable;
import org.hiahatf.mass.repo.QuoteRepository;
import org.hiahatf.mass.services.rate.RateService;
@@ -43,6 +44,7 @@ public class QuoteService {
private Lightning lightning;
private MassUtil massUtil;
private QuoteRepository quoteRepository;
+ private String proofAddress;
private Long minPay;
private Long maxPay;
@@ -50,7 +52,8 @@ public class QuoteService {
public QuoteService(RateService rateService, MassUtil massUtil,
Monero moneroRpc, Lightning lightning, QuoteRepository quoteRepository,
@Value(Constants.MIN_PAY) long minPay,
- @Value(Constants.MAX_PAY) long maxPay) {
+ @Value(Constants.MAX_PAY) long maxPay,
+ @Value(Constants.RP_ADDRESS) String rpAddress){
this.rateService = rateService;
this.massUtil = massUtil;
this.moneroRpc = moneroRpc;
@@ -58,6 +61,7 @@ public QuoteService(RateService rateService, MassUtil massUtil,
this.quoteRepository = quoteRepository;
this.minPay = minPay;
this.maxPay = maxPay;
+ this.proofAddress = rpAddress;
}
/**
@@ -67,22 +71,21 @@ public QuoteService(RateService rateService, MassUtil massUtil,
* @return Mono
*/
public Mono processMoneroQuote(Request request) {
- return rateService.getMoneroRate().flatMap(r -> {
- Double rate = massUtil.parseMoneroRate(r);
- Double value = (rate * request.getAmount()) * Constants.COIN;
- /*
- * The quote amount is validated before a response is sent.
- * Minimum and maximum payments are configured via the MASS
- * application.yml. There is no limit on requests. The amount
- * is also validated with Monero reserve proof.
- */
- return validateInboundLiquidity(value).flatMap(l -> {
- if(l) {
- return generateReserveProof(request, value, rate);
- }
- // edge case, this should never happen...
- return Mono.error(new MassException(Constants.UNK_ERROR));
- });
+ String rate = rateService.getMoneroRate();
+ Double parsedRate = massUtil.parseMoneroRate(rate);
+ Double value = (parsedRate* request.getAmount()) * Constants.COIN;
+ /*
+ * The quote amount is validated before a response is sent.
+ * Minimum and maximum payments are configured via the MASS
+ * application.yml. There is no limit on requests. The amount
+ * is also validated with Monero reserve proof.
+ */
+ return validateInboundLiquidity(value).flatMap(l -> {
+ if(l) {
+ return generateReserveProof(request, value, parsedRate);
+ }
+ // edge case, this should never happen...
+ return Mono.error(new MassException(Constants.UNK_ERROR));
});
}
@@ -131,8 +134,7 @@ private Mono validateInboundLiquidity(Double value) {
*/
private Mono generateReserveProof(Request request,
Double value, Double rate) {
- return moneroRpc.getReserveProof(request.getAddress(),
- request.getAmount()).flatMap(r -> {
+ return moneroRpc.getReserveProof(request.getAmount()).flatMap(r -> {
if(r.getResult() == null) {
return Mono.error(
new MassException(Constants.RESERVE_PROOF_ERROR)
@@ -223,6 +225,9 @@ private void persistQuote(Request request, byte[] preimage,
*/
private Mono generateMoneroQuote(Double value, byte[] hash,
Request request, Double rate, Boolean v, String proof) {
+ ReserveProof reserveProof = ReserveProof.builder()
+ .proofAddress(proofAddress)
+ .signature(proof).build();
try {
return lightning.generateInvoice(value, hash).flatMap(i -> {
Quote quote = Quote.builder()
@@ -231,7 +236,7 @@ private Mono generateMoneroQuote(Double value, byte[] hash,
.isValidAddress(v)
.amount(request.getAmount())
.invoice(i.getPayment_request())
- .reserveProof(proof)
+ .reserveProof(reserveProof)
.rate(rate)
.minSwapAmt(minPay)
.maxSwapAmt(maxPay)
diff --git a/src/main/java/org/hiahatf/mass/services/rate/RateService.java b/src/main/java/org/hiahatf/mass/services/rate/RateService.java
index 0ab9cd6..335ea0f 100644
--- a/src/main/java/org/hiahatf/mass/services/rate/RateService.java
+++ b/src/main/java/org/hiahatf/mass/services/rate/RateService.java
@@ -20,7 +20,7 @@ public class RateService {
private Logger logger = LoggerFactory.getLogger(RateService.class);
private static final int FREQUENCY = 600000;
private static final int INITIAL_DELAY = 10000;
- private Mono moneroRate;
+ private String moneroRate;
private String xmrPriceUrl;
@@ -32,7 +32,7 @@ public RateService(@Value(Constants.RATE_HOST) String url) {
* Accessor for the Monero rate
* @return monero rate
*/
- public Mono getMoneroRate() {
+ public String getMoneroRate() {
return this.moneroRate;
}
@@ -54,7 +54,8 @@ public void updateMoneroRate() {
.build())
.retrieve()
.bodyToMono(String.class);
- this.moneroRate = xmrRate.retry(1);
+ // normally wouldn't use block, but is needed here to cache price data
+ this.moneroRate = xmrRate.retry(1).block();
}
}
diff --git a/src/main/java/org/hiahatf/mass/services/rpc/Monero.java b/src/main/java/org/hiahatf/mass/services/rpc/Monero.java
index aa44295..43227b1 100644
--- a/src/main/java/org/hiahatf/mass/services/rpc/Monero.java
+++ b/src/main/java/org/hiahatf/mass/services/rpc/Monero.java
@@ -101,11 +101,10 @@ public Mono transfer(String address, Double amount) {
* --rpc-disable-login flag.
* TODO: roll custom digest authentication support
* @param value
- * @param address
* @return Mono
*/
public Mono
- getReserveProof(String address, Double amount) {
+ getReserveProof(Double amount) {
// build request
Double piconeroAmt = amount * PICONERO;
GetReserveProofParameters parameters = GetReserveProofParameters
diff --git a/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/src/main/resources/META-INF/additional-spring-configuration-metadata.json
index f761ec5..f0de1ba 100644
--- a/src/main/resources/META-INF/additional-spring-configuration-metadata.json
+++ b/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -33,5 +33,10 @@
"name": "max-pay",
"type": "java.lang.Long",
"description": "The maximum amount in satoshis accepted"
+ },
+ {
+ "name": "rp-address",
+ "type": "java.lang.String",
+ "description": "Address to share for reserve proof validation"
}
]}
\ No newline at end of file
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index c8136dc..16ed69c 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -5,8 +5,10 @@ host:
price: https://min-api.cryptocompare.com
monero: http://localhost:18082
lightning: https://localhost:8180
+# reserve proof address
+rp-address: 56fK1PpmCjz5CEiAb8dbpyQZtpJ8s4B2WaXPqyFD2FU9DUobuSDJZztEPeppvAqT2DPWcdp7qtW6KasCbYoWJC7qBcwWrSH
# macaroon path
-macaroon-path: /home/nigellchristian/.lnd2/data/chain/bitcoin/regtest/admin.macaroon
+macaroon-path: /home/rimuru/.lnd-regtest-2/data/chain/bitcoin/regtest/admin.macaroon
# markup so we can pay the bills
markup: 0.01
# payment thresholds in satoshis
@@ -23,8 +25,10 @@ server.error.include-stacktrace: never
# not a big fan of h2
# TODO: database upgrade
spring.datasource:
- url: jdbc:h2:file:/data
- driverClassName: org.h2.Driver
+ url: jdbc:h2:file:./data
+ driverClassName: org.h2.Driver
username: sa
- password: pass
-spring.jpa.database-platform: org.hibernate.dialect.H2Dialect
+ password: pass
+spring.jpa:
+ database-platform: org.hibernate.dialect.H2Dialect
+ hibernate.ddl-auto: update
diff --git a/src/main/resources/data b/src/main/resources/data
deleted file mode 100644
index e69de29..0000000
diff --git a/src/test/java/org/hiahatf/mass/MassApplicationTests.java b/src/test/java/org/hiahatf/mass/MassApplicationTests.java
deleted file mode 100644
index b8cb68f..0000000
--- a/src/test/java/org/hiahatf/mass/MassApplicationTests.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.hiahatf.mass;
-
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.SpringBootTest;
-
-@SpringBootTest
-class MassApplicationTests {
-
- @Test
- void contextLoads() {
- }
-
-}
diff --git a/src/test/java/org/hiahatf/mass/controllers/HealthControllerTest.java b/src/test/java/org/hiahatf/mass/controllers/HealthControllerTest.java
new file mode 100644
index 0000000..de9864e
--- /dev/null
+++ b/src/test/java/org/hiahatf/mass/controllers/HealthControllerTest.java
@@ -0,0 +1,40 @@
+package org.hiahatf.mass.controllers;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+
+import org.hiahatf.mass.models.lightning.Info;
+import org.hiahatf.mass.services.rpc.Lightning;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.platform.runner.JUnitPlatform;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import reactor.core.publisher.Mono;
+
+@ExtendWith(MockitoExtension.class)
+@RunWith(JUnitPlatform.class)
+public class HealthControllerTest {
+
+ @Mock
+ Lightning lightning;
+
+ @InjectMocks
+ HealthController controller = new HealthController(lightning);
+
+ @Test
+ @DisplayName("Health Controller Test")
+ public void fetchMoneroQuoteTest() throws IOException {
+ Info info = Info.builder().version("v0.0.0").build();
+ when(lightning.getInfo()).thenReturn(Mono.just(info));
+ Mono testInfo = controller.ping();
+ assertEquals(info.getVersion(), testInfo.block().getVersion());
+ }
+
+}
diff --git a/src/test/java/org/hiahatf/mass/controllers/QuoteControllerTest.java b/src/test/java/org/hiahatf/mass/controllers/QuoteControllerTest.java
new file mode 100644
index 0000000..4d96640
--- /dev/null
+++ b/src/test/java/org/hiahatf/mass/controllers/QuoteControllerTest.java
@@ -0,0 +1,41 @@
+package org.hiahatf.mass.controllers;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.when;
+
+import org.hiahatf.mass.models.monero.Quote;
+import org.hiahatf.mass.models.monero.Request;
+import org.hiahatf.mass.services.monero.QuoteService;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.platform.runner.JUnitPlatform;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import reactor.core.publisher.Mono;
+
+@ExtendWith(MockitoExtension.class)
+@RunWith(JUnitPlatform.class)
+public class QuoteControllerTest {
+
+ @Mock
+ QuoteService quoteService;
+
+ @InjectMocks
+ QuoteController controller = new QuoteController(quoteService);
+
+ @Test
+ @DisplayName("Quote Controller Test")
+ public void fetchMoneroQuoteTest() {
+ String address = "54xxx";
+ Request request = Request.builder().address(address).build();
+ Quote quote = Quote.builder().address(address).build();
+ when(quoteService.processMoneroQuote(request)).thenReturn(Mono.just(quote));
+ Mono testQuote = controller.fetchMoneroQuote(request);
+ assertEquals(quote.getAddress(), testQuote.block().getAddress());
+ }
+
+}
diff --git a/src/test/java/org/hiahatf/mass/controllers/SwapControllerTest.java b/src/test/java/org/hiahatf/mass/controllers/SwapControllerTest.java
new file mode 100644
index 0000000..006fd10
--- /dev/null
+++ b/src/test/java/org/hiahatf/mass/controllers/SwapControllerTest.java
@@ -0,0 +1,41 @@
+package org.hiahatf.mass.controllers;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.when;
+
+import org.hiahatf.mass.models.monero.SwapRequest;
+import org.hiahatf.mass.models.monero.SwapResponse;
+import org.hiahatf.mass.services.monero.SwapService;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.platform.runner.JUnitPlatform;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import reactor.core.publisher.Mono;
+
+@ExtendWith(MockitoExtension.class)
+@RunWith(JUnitPlatform.class)
+public class SwapControllerTest {
+
+ @Mock
+ SwapService swapService;
+
+ @InjectMocks
+ SwapController controller = new SwapController(swapService);
+
+ @Test
+ @DisplayName("Swap Controller Test")
+ public void fetchMoneroQuoteTest() {
+ String hash = "hash";
+ SwapRequest request = SwapRequest.builder().hash(hash).build();
+ SwapResponse swap = SwapResponse.builder().hash(hash).build();
+ when(swapService.processMoneroSwap(request)).thenReturn(Mono.just(swap));
+ Mono testSwap = controller.fetchMoneroSwap(request);
+ assertEquals(swap.getHash(), testSwap.block().getHash());
+ }
+
+}
diff --git a/src/test/java/org/hiahatf/mass/service/monero/QuoteServiceTest.java b/src/test/java/org/hiahatf/mass/service/monero/QuoteServiceTest.java
new file mode 100644
index 0000000..b35006f
--- /dev/null
+++ b/src/test/java/org/hiahatf/mass/service/monero/QuoteServiceTest.java
@@ -0,0 +1,172 @@
+package org.hiahatf.mass.service.monero;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+
+import javax.net.ssl.SSLException;
+
+import org.hiahatf.mass.models.lightning.AddHoldInvoiceResponse;
+import org.hiahatf.mass.models.lightning.Amount;
+import org.hiahatf.mass.models.lightning.Liquidity;
+import org.hiahatf.mass.models.monero.Quote;
+import org.hiahatf.mass.models.monero.Request;
+import org.hiahatf.mass.models.monero.proof.GetProofResult;
+import org.hiahatf.mass.models.monero.proof.GetReserveProofResponse;
+import org.hiahatf.mass.models.monero.validate.ValidateAddressResponse;
+import org.hiahatf.mass.models.monero.validate.ValidateAddressResult;
+import org.hiahatf.mass.repo.QuoteRepository;
+import org.hiahatf.mass.services.monero.QuoteService;
+import org.hiahatf.mass.services.rate.RateService;
+import org.hiahatf.mass.services.rpc.Lightning;
+import org.hiahatf.mass.services.rpc.Monero;
+import org.hiahatf.mass.util.MassUtil;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.platform.runner.JUnitPlatform;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+/**
+ * Tests for Monero Quote Service
+ */
+@ExtendWith(MockitoExtension.class)
+@RunWith(JUnitPlatform.class)
+public class QuoteServiceTest {
+
+ @Mock
+ private RateService rateService;
+ @Mock
+ private MassUtil util;
+ @Mock
+ private Monero moneroRpc;
+ @Mock
+ private Lightning lightning;
+ @Mock
+ private MassUtil massUtil;
+ @Mock
+ private QuoteRepository quoteRepository;
+ private final Long minPay = 10000L;
+ private final Long maxPay = 1000000L;
+ @InjectMocks
+ private QuoteService quoteService = new QuoteService(rateService, massUtil,
+ moneroRpc, lightning, quoteRepository, minPay, maxPay, "54rpvxxx");
+
+ @Test
+ @DisplayName("Monero Quote Service Test")
+ public void processQuoteTest() throws SSLException, IOException {
+ String prs = "proofresultsigxxx";
+ // build test data
+ Request req = Request.builder().address("54xxx")
+ .amount(0.1).build();
+ Amount amt = Amount.builder().sat("100000").build();
+ Liquidity liquidity = Liquidity.builder()
+ .remote_balance(amt).build();
+ GetProofResult getProofResult = GetProofResult.builder()
+ .signature(prs).build();
+ GetReserveProofResponse reserveProof = GetReserveProofResponse
+ .builder().result(getProofResult)
+ .build();
+ ValidateAddressResult validateAddressResult = ValidateAddressResult.builder()
+ .valid(true).build();
+ ValidateAddressResponse validateAddressResponse = ValidateAddressResponse.builder()
+ .result(validateAddressResult).build();
+ AddHoldInvoiceResponse addHoldInvoiceResponse = AddHoldInvoiceResponse.builder()
+ .payment_request("lntest123xxx").build();
+ // mocks
+ when(rateService.getMoneroRate()).thenReturn("{BTC: 0.00777}");
+ when(massUtil.parseMoneroRate(anyString())).thenReturn(0.008);
+ when(lightning.fetchBalance()).thenReturn(Mono.just(liquidity));
+ when(moneroRpc.getReserveProof(req.getAmount())).thenReturn(Mono.just(reserveProof));
+ when(moneroRpc.validateAddress(req.getAddress()))
+ .thenReturn(Mono.just(validateAddressResponse));
+ when(lightning.generateInvoice(any(), any())).thenReturn(Mono.just(addHoldInvoiceResponse));
+ Mono testQuote = quoteService.processMoneroQuote(req);
+
+ StepVerifier.create(testQuote)
+ .expectNextMatches(r -> r.getReserveProof().getSignature()
+ .equals(prs))
+ .verifyComplete();
+ }
+
+ @Test
+ @DisplayName("Monero Payment Threshold Error Test")
+ public void paymentThresholdErrorTest() throws SSLException, IOException {
+ // build test data
+ Request req = Request.builder().address("54xxx")
+ .amount(100.0).build();
+ // mocks
+ when(rateService.getMoneroRate()).thenReturn("{BTC: 0.00777}");
+ when(massUtil.parseMoneroRate(anyString())).thenReturn(0.008);
+ try {
+ Quote test = quoteService.processMoneroQuote(req).block();
+ assertNotNull(test);
+ } catch (Exception e) {
+ String expectedError = "org.hiahatf.mass.exception.MassException: " +
+ "Payment threshold error. (min: 10000, max: 1000000 satoshis)";
+ assertEquals(expectedError, e.getMessage());
+ }
+ }
+
+ @Test
+ @DisplayName("Monero Swap Liquidity Error Test")
+ public void liquidityErrorTest() throws SSLException, IOException {
+ // build test data
+ Request req = Request.builder().address("54xxx")
+ .amount(0.1).build();
+ Amount amt = Amount.builder().sat("10").build();
+ Liquidity liquidity = Liquidity.builder()
+ .remote_balance(amt).build();
+ // mocks
+ when(rateService.getMoneroRate()).thenReturn("{BTC: 0.00777}");
+ when(massUtil.parseMoneroRate(anyString())).thenReturn(0.008);
+ when(lightning.fetchBalance()).thenReturn(Mono.just(liquidity));
+ try {
+ Quote test = quoteService.processMoneroQuote(req).block();
+ assertNotNull(test);
+ } catch (Exception e) {
+ String expectedError = "org.hiahatf.mass.exception.MassException: " +
+ "Liquidity validation error";
+ assertEquals(expectedError, e.getMessage());
+ }
+ }
+
+ @Test
+ @DisplayName("Monero Swap Reserve Proof Error Test")
+ public void reserveProofErrorTest() throws SSLException, IOException {
+ // build test data
+ Request req = Request.builder().address("54xxx")
+ .amount(0.1).build();
+ Amount amt = Amount.builder().sat("100000").build();
+ Liquidity liquidity = Liquidity.builder()
+ .remote_balance(amt).build();
+ GetReserveProofResponse reserveProof = GetReserveProofResponse
+ .builder().result(null)
+ .build();
+ // mocks
+ when(rateService.getMoneroRate()).thenReturn("{BTC: 0.00777}");
+ when(massUtil.parseMoneroRate(anyString())).thenReturn(0.008);
+ when(lightning.fetchBalance()).thenReturn(Mono.just(liquidity));
+ when(moneroRpc.getReserveProof(req.getAmount())).thenReturn(Mono.just(reserveProof));
+ try {
+ Quote test = quoteService.processMoneroQuote(req).block();
+ assertNotNull(test);
+ } catch (Exception e) {
+ String expectedError = "org.hiahatf.mass.exception.MassException: " +
+ "Reserve proof error";
+ assertEquals(expectedError, e.getMessage());
+ }
+ }
+
+}
diff --git a/src/test/java/org/hiahatf/mass/service/monero/SwapServiceTest.java b/src/test/java/org/hiahatf/mass/service/monero/SwapServiceTest.java
new file mode 100644
index 0000000..a1deae9
--- /dev/null
+++ b/src/test/java/org/hiahatf/mass/service/monero/SwapServiceTest.java
@@ -0,0 +1,93 @@
+package org.hiahatf.mass.service.monero;
+
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.util.Optional;
+
+import javax.net.ssl.SSLException;
+
+import org.hiahatf.mass.models.lightning.InvoiceLookupResponse;
+import org.hiahatf.mass.models.lightning.InvoiceState;
+import org.hiahatf.mass.models.monero.SwapRequest;
+import org.hiahatf.mass.models.monero.SwapResponse;
+import org.hiahatf.mass.models.monero.XmrQuoteTable;
+import org.hiahatf.mass.models.monero.transfer.TransferResponse;
+import org.hiahatf.mass.models.monero.transfer.TransferResult;
+import org.hiahatf.mass.repo.QuoteRepository;
+import org.hiahatf.mass.services.monero.SwapService;
+import org.hiahatf.mass.services.rpc.Lightning;
+import org.hiahatf.mass.services.rpc.Monero;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.platform.runner.JUnitPlatform;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+/**
+ * Tests for Monero Swap Service
+ */
+@ExtendWith(MockitoExtension.class)
+@RunWith(JUnitPlatform.class)
+public class SwapServiceTest {
+
+ @Mock
+ QuoteRepository quoteRepository;
+ @Mock
+ Lightning lightning;
+ @Mock
+ Monero monero;
+ @Mock
+ ResponseEntity entity;
+ @InjectMocks
+ SwapService swapService;
+
+ @Test
+ @DisplayName("Monero Swap Service Test")
+ public void processMoneroSwapTest() throws SSLException, IOException {
+ String metadata = "expectedMetadata000";
+ SwapRequest swapRequest = SwapRequest.builder()
+ .hash("hash").build();
+ Optional table = Optional.of(XmrQuoteTable.builder()
+ .amount(0.1)
+ .payment_hash(new byte[32])
+ .preimage(new byte[32])
+ .quote_id("qid")
+ .xmr_address("54xxx")
+ .build());
+ InvoiceLookupResponse invoiceLookupResponse = InvoiceLookupResponse
+ .builder()
+ .state(InvoiceState.ACCEPTED)
+ .build();
+ TransferResult result = TransferResult.builder()
+ .tx_metadata(metadata)
+ .build();
+ TransferResponse transferResponse = TransferResponse.builder()
+ .result(result)
+ .build();
+
+ // mocks
+ when(quoteRepository.findById(swapRequest.getHash())).thenReturn(table);
+ when(lightning.lookupInvoice(table.get().getQuote_id()))
+ .thenReturn(Mono.just(invoiceLookupResponse));
+ when(monero.transfer(table.get().getXmr_address(), table.get().getAmount()))
+ .thenReturn(Mono.just(transferResponse));
+ when(entity.getStatusCode()).thenReturn(HttpStatus.OK);
+ when(lightning.handleInvoice(table.get(), true)).thenReturn(Mono.just(entity));
+ Mono testRes = swapService.processMoneroSwap(swapRequest);
+
+ StepVerifier.create(testRes)
+ .expectNextMatches(r -> r.getMetadata()
+ .equals(metadata))
+ .verifyComplete();
+ }
+
+}
diff --git a/src/test/java/org/hiahatf/mass/service/rate/RateServiceTest.java b/src/test/java/org/hiahatf/mass/service/rate/RateServiceTest.java
new file mode 100644
index 0000000..d0dfd94
--- /dev/null
+++ b/src/test/java/org/hiahatf/mass/service/rate/RateServiceTest.java
@@ -0,0 +1,73 @@
+package org.hiahatf.mass.service.rate;
+
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.net.HttpHeaders;
+
+import org.hiahatf.mass.services.rate.RateService;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.platform.runner.JUnitPlatform;
+import org.junit.runner.RunWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import io.netty.handler.codec.http.HttpHeaderValues;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+
+/**
+ * Tests for Rate Service
+ */
+@ExtendWith(MockitoExtension.class)
+@RunWith(JUnitPlatform.class)
+public class RateServiceTest {
+
+ public static MockWebServer mockBackEnd;
+ private ObjectMapper objectMapper = new ObjectMapper();
+ private RateService rateService;
+
+ @BeforeAll
+ static void setUp() throws IOException {
+ mockBackEnd = new MockWebServer();
+ mockBackEnd.start();
+ }
+
+ @AfterAll
+ static void tearDown() throws IOException {
+ mockBackEnd.shutdown();
+ }
+
+ @BeforeEach
+ void initialize() {
+ String baseUrl = String.format("http://localhost:%s",
+ mockBackEnd.getPort());
+ rateService = new RateService(baseUrl);
+ }
+
+ @Test
+ @DisplayName("Rate Service Test")
+ public void getRateTest() throws JsonProcessingException {
+ String expectedRate = "{\"BTC\":\"0.00777\"}";
+ HashMap res = new HashMap<>();
+ res.put("BTC", "0.00777");
+ mockBackEnd.enqueue(new MockResponse()
+ .setBody(objectMapper.writeValueAsString(res))
+ .addHeader(HttpHeaders.CONTENT_TYPE,
+ HttpHeaderValues.APPLICATION_JSON.toString()));
+
+ rateService.updateMoneroRate();
+ String testRate = rateService.getMoneroRate();
+ assertEquals(expectedRate, testRate);
+ }
+
+}
diff --git a/src/test/java/org/hiahatf/mass/service/rpc/LightningTest.java b/src/test/java/org/hiahatf/mass/service/rpc/LightningTest.java
new file mode 100644
index 0000000..d2a6793
--- /dev/null
+++ b/src/test/java/org/hiahatf/mass/service/rpc/LightningTest.java
@@ -0,0 +1,165 @@
+package org.hiahatf.mass.service.rpc;
+
+import java.io.IOException;
+
+import javax.net.ssl.SSLException;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.net.HttpHeaders;
+
+import org.hiahatf.mass.models.lightning.AddHoldInvoiceResponse;
+import org.hiahatf.mass.models.lightning.Amount;
+import org.hiahatf.mass.models.lightning.Info;
+import org.hiahatf.mass.models.lightning.InvoiceLookupResponse;
+import org.hiahatf.mass.models.lightning.InvoiceState;
+import org.hiahatf.mass.models.lightning.Liquidity;
+import org.hiahatf.mass.models.monero.XmrQuoteTable;
+import org.hiahatf.mass.services.rpc.Lightning;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.platform.runner.JUnitPlatform;
+import org.junit.runner.RunWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+
+import io.netty.handler.codec.http.HttpHeaderValues;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+/**
+ * Tests for Lightning RPC Service
+ */
+@ExtendWith(MockitoExtension.class)
+@RunWith(JUnitPlatform.class)
+public class LightningTest {
+
+ public static MockWebServer mockBackEnd;
+ private ObjectMapper objectMapper = new ObjectMapper();
+ private Lightning lightning;
+ private String testMacaroonPath =
+ "src/test/resources/test.macroon";
+
+ @BeforeAll
+ static void setUp() throws IOException {
+ mockBackEnd = new MockWebServer();
+ mockBackEnd.start();
+ }
+
+ @AfterAll
+ static void tearDown() throws IOException {
+ mockBackEnd.shutdown();
+ }
+
+ @BeforeEach
+ void initialize() {
+ String baseUrl = String.format("http://localhost:%s",
+ mockBackEnd.getPort());
+ lightning = new Lightning(baseUrl, testMacaroonPath);
+ }
+
+ @Test
+ @DisplayName("Get Info Test")
+ public void getInfoTest() throws JsonProcessingException, IOException,
+ SSLException {
+ String version = "v.0.0.0-test";
+ Info info = Info.builder().version(version).build();
+ mockBackEnd.enqueue(new MockResponse()
+ .setBody(objectMapper.writeValueAsString(info))
+ .addHeader(HttpHeaders.CONTENT_TYPE,
+ HttpHeaderValues.APPLICATION_JSON.toString()));
+ Mono testRes = lightning.getInfo();
+
+ StepVerifier.create(testRes)
+ .expectNextMatches(r -> r.getVersion()
+ .equals(version))
+ .verifyComplete();
+ }
+
+ @Test
+ @DisplayName("Lookup Invoice Test")
+ public void lookupInvoiceTest() throws JsonProcessingException, IOException,
+ SSLException {
+ InvoiceLookupResponse res = InvoiceLookupResponse.builder()
+ .state(InvoiceState.ACCEPTED).build();
+ mockBackEnd.enqueue(new MockResponse()
+ .setBody(objectMapper.writeValueAsString(res))
+ .addHeader(HttpHeaders.CONTENT_TYPE,
+ HttpHeaderValues.APPLICATION_JSON.toString()));
+ Mono testRes = lightning.lookupInvoice("hash");
+
+ StepVerifier.create(testRes)
+ .expectNextMatches(r -> r.getState()
+ .equals(InvoiceState.ACCEPTED))
+ .verifyComplete();
+ }
+
+ @Test
+ @DisplayName("Generate Invoice Test")
+ public void generateInvoiceTest() throws JsonProcessingException, IOException,
+ SSLException {
+ String expectedPayReq = "lntest";
+ Double amount = 0.1;
+ byte[] hash = new byte[32];
+ AddHoldInvoiceResponse res = AddHoldInvoiceResponse.builder()
+ .payment_request(expectedPayReq).build();
+ mockBackEnd.enqueue(new MockResponse()
+ .setBody(objectMapper.writeValueAsString(res))
+ .addHeader(HttpHeaders.CONTENT_TYPE,
+ HttpHeaderValues.APPLICATION_JSON.toString()));
+ Mono testRes = lightning.generateInvoice(amount, hash);
+
+ StepVerifier.create(testRes)
+ .expectNextMatches(r -> r.getPayment_request()
+ .equals(expectedPayReq))
+ .verifyComplete();
+ }
+
+ @Test
+ @DisplayName("Handle Invoice Test")
+ public void handleInvoiceTest() throws JsonProcessingException, IOException,
+ SSLException {
+ XmrQuoteTable quote = XmrQuoteTable.builder()
+ .amount(0.1)
+ .quote_id("qid")
+ .build();
+ mockBackEnd.enqueue(new MockResponse()
+ .setResponseCode(HttpStatus.OK.value())
+ .addHeader(HttpHeaders.CONTENT_TYPE,
+ HttpHeaderValues.APPLICATION_JSON.toString()));
+ Mono> testRes = lightning.handleInvoice(quote, true);
+
+ StepVerifier.create(testRes)
+ .expectNextMatches(r -> r.getStatusCode()
+ .equals(HttpStatus.OK))
+ .verifyComplete();
+ }
+
+ @Test
+ @DisplayName("Fetch Balance Test")
+ public void fetchBalanceTest() throws JsonProcessingException, IOException,
+ SSLException {
+ Amount amount = Amount.builder().msat("10000").sat("10").build();
+ Liquidity liquidity = Liquidity.builder()
+ .local_balance(amount).remote_balance(amount).build();
+ mockBackEnd.enqueue(new MockResponse()
+ .setBody(objectMapper.writeValueAsString(liquidity))
+ .addHeader(HttpHeaders.CONTENT_TYPE,
+ HttpHeaderValues.APPLICATION_JSON.toString()));
+ Mono testRes = lightning.fetchBalance();
+
+
+ StepVerifier.create(testRes)
+ .expectNextMatches(r -> r.getLocal_balance()
+ .equals(amount))
+ .verifyComplete();
+ }
+
+}
diff --git a/src/test/java/org/hiahatf/mass/service/rpc/MoneroTest.java b/src/test/java/org/hiahatf/mass/service/rpc/MoneroTest.java
new file mode 100644
index 0000000..4d3ef88
--- /dev/null
+++ b/src/test/java/org/hiahatf/mass/service/rpc/MoneroTest.java
@@ -0,0 +1,117 @@
+package org.hiahatf.mass.service.rpc;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.hiahatf.mass.models.monero.proof.GetProofResult;
+import org.hiahatf.mass.models.monero.proof.GetReserveProofResponse;
+import org.hiahatf.mass.models.monero.transfer.TransferResponse;
+import org.hiahatf.mass.models.monero.transfer.TransferResult;
+import org.hiahatf.mass.models.monero.validate.ValidateAddressResponse;
+import org.hiahatf.mass.models.monero.validate.ValidateAddressResult;
+import org.hiahatf.mass.services.rpc.Monero;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.platform.runner.JUnitPlatform;
+import org.junit.runner.RunWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+/**
+ * Tests for Monero RPC Service
+ */
+@ExtendWith(MockitoExtension.class)
+@RunWith(JUnitPlatform.class)
+public class MoneroTest {
+
+ public static MockWebServer mockBackEnd;
+ private ObjectMapper objectMapper = new ObjectMapper();
+ private Monero monero;
+
+ @BeforeAll
+ static void setUp() throws IOException {
+ mockBackEnd = new MockWebServer();
+ mockBackEnd.start();
+ }
+
+ @AfterAll
+ static void tearDown() throws IOException {
+ mockBackEnd.shutdown();
+ }
+
+ @BeforeEach
+ void initialize() {
+ String baseUrl = String.format("http://localhost:%s",
+ mockBackEnd.getPort());
+ monero = new Monero(baseUrl);
+ }
+
+ @Test
+ @DisplayName("Monero Validate Address Test")
+ public void validateAddressTest() throws JsonProcessingException {
+ String address = "54testAddress";
+ ValidateAddressResult result = ValidateAddressResult.builder()
+ .valid(true).build();
+ ValidateAddressResponse res = ValidateAddressResponse.builder()
+ .result(result).build();
+ mockBackEnd.enqueue(new MockResponse()
+ .setBody(objectMapper.writeValueAsString(res))
+ .addHeader("Content-Type", "application/json"));
+ Mono testRes = monero.validateAddress(address);
+
+ StepVerifier.create(testRes)
+ .expectNextMatches(r -> r.getResult()
+ .equals(result))
+ .verifyComplete();
+ }
+
+ @Test
+ @DisplayName("Monero Transfer Test")
+ public void transferTest() throws JsonProcessingException {
+ String address = "54testAddress";
+ Double amount = 0.1;
+ TransferResult result = TransferResult.builder()
+ .tx_hash("hash").build();
+ TransferResponse response = TransferResponse.builder()
+ .result(result).build();
+ mockBackEnd.enqueue(new MockResponse()
+ .setBody(objectMapper.writeValueAsString(response))
+ .addHeader("Content-Type", "application/json"));
+ Mono testRes = monero.transfer(address, amount);
+
+ StepVerifier.create(testRes)
+ .expectNextMatches(r -> r.getResult()
+ .equals(result))
+ .verifyComplete();
+ }
+
+ @Test
+ @DisplayName("Monero Reserve Proof Test")
+ public void reserveProofTest() throws JsonProcessingException {
+ Double amount = 0.1;
+ GetProofResult result = GetProofResult.builder()
+ .signature("reserveProofTest").build();
+ GetReserveProofResponse response = GetReserveProofResponse.builder()
+ .result(result).build();
+ mockBackEnd.enqueue(new MockResponse()
+ .setBody(objectMapper.writeValueAsString(response))
+ .addHeader("Content-Type", "application/json"));
+ Mono testRes = monero.getReserveProof(amount);
+
+ StepVerifier.create(testRes)
+ .expectNextMatches(r -> r.getResult()
+ .equals(result))
+ .verifyComplete();
+ }
+
+}
diff --git a/src/test/java/org/hiahatf/mass/util/MassUtilTest.java b/src/test/java/org/hiahatf/mass/util/MassUtilTest.java
new file mode 100644
index 0000000..84def8d
--- /dev/null
+++ b/src/test/java/org/hiahatf/mass/util/MassUtilTest.java
@@ -0,0 +1,24 @@
+package org.hiahatf.mass.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test class for the utility methods
+ */
+public class MassUtilTest {
+
+ private MassUtil util = new MassUtil(0.01);
+
+ @Test
+ @DisplayName("Parse Rate Test")
+ public void parseRateTest() {
+ String data = "{BTC: 0.0076543}";
+ Double testRate = util.parseMoneroRate(data);
+ Double expectedRate = 0.007730843;
+ assertEquals(expectedRate, testRate);
+ }
+
+}
diff --git a/src/test/resources/test.macroon b/src/test/resources/test.macroon
new file mode 100644
index 0000000..621e8f1
--- /dev/null
+++ b/src/test/resources/test.macroon
@@ -0,0 +1 @@
+used for the Lightning rpc test
\ No newline at end of file