Skip to content

Commit

Permalink
The wallet service is 100% covered by tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sabexzero committed Apr 24, 2024
1 parent 426ce14 commit 7ec25a5
Show file tree
Hide file tree
Showing 10 changed files with 228 additions and 28 deletions.
8 changes: 7 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,15 @@ dependencies {
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.0'
implementation 'org.springframework.boot:spring-boot-starter-web'
//implementation 'ch.qos.logback:logback-classic:1.5.3'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.2'
testImplementation 'org.mockito:mockito-core:5.11.0'
implementation 'org.springframework.boot:spring-boot-starter-test:3.2.5'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframe work.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
}

test {
useJUnitPlatform()
}
2 changes: 0 additions & 2 deletions src/main/java/com/example/TradeHub/TradeHubApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ public class TradeHubApplication {

//TODO: Implement logging

//TODO: Create constant error messages

//TODO: Security Design

//TODO: Fix errors in service with incorrect cryptocurrencies values
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ public void afterCryptoTransaction(
return;
}



//Determine transaction type
for(TransactionType transactionType : TransactionType.values()){
if(methodName.toUpperCase().contains(transactionType.name())){
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.example.TradeHub.repository.specified.user;

import com.example.TradeHub.domain.user.User;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends CrudRepository<User, Long> {

}
package com.example.TradeHub.repository.user;

import com.example.TradeHub.domain.user.User;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends CrudRepository<User, Long> {

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.security.web.firewall.RequestRejectedException;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.math.BigDecimal;
import java.net.URI;
import java.time.LocalDateTime;
Expand Down Expand Up @@ -76,7 +78,7 @@ private void setHeaders(){
public BigDecimal getCryptocurrencyPrice(
String baseAsset,
String quoteAsset
) throws RuntimeException {
) throws IOException {
BigDecimal result = null;

ResponseEntity<String> response = restTemplate.exchange(
Expand All @@ -93,7 +95,7 @@ public BigDecimal getCryptocurrencyPrice(

result = exchange.rate();
} catch (Exception e) {
throw new RuntimeException("Failed to get the price of the cryptocurrency");
throw new IOException("Failed to get the price of the cryptocurrency");
}
return result;
}
Expand Down
11 changes: 6 additions & 5 deletions src/main/java/com/example/TradeHub/service/CommerceService.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Random;

Expand All @@ -35,7 +37,7 @@ public class CommerceService {
*/
public CompletedCryptoBuyRequest handleCryptoBuyRequest(
CryptoUserRequest request
){
) throws IOException {
BigDecimal buyPrice = coinApiService.getCryptocurrencyPrice(request.baseAsset(), request.quoteAsset());

return buyCryptoRequestRepository.save(
Expand All @@ -52,12 +54,11 @@ public CompletedCryptoBuyRequest handleCryptoBuyRequest(
@CryptoTransaction
public CryptoUserResponse confirmCryptoBuyRequest(
Long requestId
) {

) throws NoSuchElementException {
//Allegedly received money from the user
boolean userHaveMoney = new Random().nextBoolean();
CompletedCryptoBuyRequest request = buyCryptoRequestRepository.findById(requestId)
.orElseThrow(() -> new RuntimeException("Request not found"));
.orElseThrow(() -> new NoSuchElementException("Request not found"));

if(userHaveMoney){
try{
Expand All @@ -74,7 +75,7 @@ public CryptoUserResponse confirmCryptoBuyRequest(
@CryptoTransaction
public CryptoUserResponse handleCryptoSellRequest(
CryptoUserRequest request
) {
) throws NoSuchElementException {

try {
walletsService.decreaseUserBalance(request.userId(), request.baseAsset(), request.amount());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.Optional;

Expand All @@ -24,7 +25,7 @@ public class ConvertCryptoService {
@CryptoTransaction
public CryptoUserResponse handleConvertRequest(
CryptoUserRequest request
){
) throws IOException {
//We receive a wallet from which the cryptocurrency will be debited
CryptoWallet walletToGive = cryptoWalletRepository.findByUserAndCryptocurrency(
request.userId(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ public CryptoTransferResponse handleTransferRequest(
){
try{
walletsService.decreaseUserBalance(request.senderId(),request.sentAsset(),request.sentAmount());

walletsService.increaseUserBalance(request.recipientId(),request.sentAsset(),request.sentAmount());

return new CryptoTransferResponse(request, true);
Expand Down
18 changes: 14 additions & 4 deletions src/main/java/com/example/TradeHub/service/WalletsService.java
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
package com.example.TradeHub.service;

import com.example.TradeHub.domain.wallet.CryptoWallet;
import com.example.TradeHub.repository.user.UserRepository;
import com.example.TradeHub.repository.wallet.CryptoWalletRepository;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.NoSuchElementException;
import java.util.Optional;

@Service
@RequiredArgsConstructor
public class WalletsService {
private final CryptoWalletRepository cryptoWalletRepository;
private final UserRepository userRepository;
private final Logger logger = LoggerFactory.getLogger(WalletsService.class);
private static final BigDecimal commissionCoefficient = new BigDecimal("1.03");

public void increaseUserBalance(
Long userId,
String asset,
BigDecimal amount
){
) throws NoSuchElementException{

userRepository.findById(userId)
.orElseThrow(() -> new NoSuchElementException("User not found"));

Optional<CryptoWallet> wallet = cryptoWalletRepository.findByUserAndCryptocurrency(userId, asset);

if(wallet.isEmpty()){
Expand All @@ -44,14 +51,17 @@ public void decreaseUserBalance(
Long userId,
String asset,
BigDecimal amount
) throws RuntimeException{
) throws IllegalStateException{
userRepository.findById(userId)
.orElseThrow(() -> new NoSuchElementException("User not found"));

CryptoWallet wallet = cryptoWalletRepository.findByUserAndCryptocurrency(userId, asset)
.orElseThrow(() -> new RuntimeException("The user did not find the required crypto wallet"));
.orElseThrow(() -> new NoSuchElementException("The user did not find the required crypto wallet"));

int compareResult = wallet.DecreaseBalance(amount);

if (compareResult < 0) {
throw new RuntimeException("The sender does not have enough funds " +
throw new IllegalStateException("The sender does not have enough funds " +
"to carry out the transfer of cryptocurrency");
} else {
if(compareResult == 0){
Expand Down
185 changes: 185 additions & 0 deletions src/test/java/com/example/TradeHub/WalletsServiceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package com.example.TradeHub;

import com.example.TradeHub.domain.user.User;
import com.example.TradeHub.domain.wallet.CryptoWallet;
import com.example.TradeHub.repository.user.UserRepository;
import com.example.TradeHub.repository.wallet.CryptoWalletRepository;
import com.example.TradeHub.service.WalletsService;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.boot.test.context.SpringBootTest;

import java.math.BigDecimal;
import java.util.NoSuchElementException;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;


@SpringBootTest
public class WalletsServiceTest {

@Mock
private CryptoWalletRepository cryptoWalletRepository;

@Mock
private UserRepository userRepository;

@InjectMocks
private WalletsService walletsService;


@Test
public void testIncreaseUserBalance_NewWallet() {
// Arrange
Long userId = 1L;
String asset = "BTC";
BigDecimal amount = new BigDecimal("0.1");

when(cryptoWalletRepository.findByUserAndCryptocurrency(userId, asset))
.thenReturn(Optional.empty());

when(userRepository.findById(userId))
.thenReturn(Optional.of(new User()));

// Act
walletsService.increaseUserBalance(userId, asset, amount);

// Assert
verify(cryptoWalletRepository).save(any());
}

@Test
public void testIncreaseUserBalance_ExistingWallet() {
// Arrange
Long userId = 1L;
String asset = "BTC";
BigDecimal amount = new BigDecimal("0.1");

CryptoWallet existingWallet = CryptoWallet.builder()
.userId(userId)
.baseAsset(asset)
.balance(new BigDecimal("1.0"))
.build();

when(userRepository.findById(userId))
.thenReturn(Optional.of(new User()));

when(cryptoWalletRepository.findByUserAndCryptocurrency(userId, asset))
.thenReturn(Optional.of(existingWallet));

// Act
walletsService.increaseUserBalance(userId, asset, amount);


// Assert
verify(cryptoWalletRepository).save(existingWallet);
}

@Test
public void testIncreaseUserBalance_UserNotExist() {
// Arrange
Long userId = 1L;
String asset = "BTC";
BigDecimal amount = new BigDecimal("0.1");

when(userRepository.findById(userId))
.thenReturn(Optional.empty());

// Act & Assert
assertThrows(NoSuchElementException.class, () -> {
walletsService.increaseUserBalance(userId, asset, amount);
});
}

@Test
public void testDecreaseUserBalance_WalletNotExist() {
// Arrange
Long userId = 1L;
String asset = "BTC";
BigDecimal amount = new BigDecimal("0.1");

when(cryptoWalletRepository.findByUserAndCryptocurrency(userId, asset))
.thenReturn(Optional.empty());

when(userRepository.findById(userId))
.thenReturn(Optional.of(new User()));

// Act && Assert
assertThrows(NoSuchElementException.class, () -> {
walletsService.decreaseUserBalance(userId, asset, amount);
});
}

@Test
public void testDecreaseUserBalance_ExistingWalletAndEnoughFunds() {
// Arrange
Long userId = 1L;
String asset = "BTC";
BigDecimal amount = new BigDecimal("0.1");

CryptoWallet existingWallet = CryptoWallet.builder()
.userId(userId)
.baseAsset(asset)
.balance(new BigDecimal("1.0"))
.build();

when(userRepository.findById(userId))
.thenReturn(Optional.of(new User()));

when(cryptoWalletRepository.findByUserAndCryptocurrency(userId, asset))
.thenReturn(Optional.of(existingWallet));

// Act
walletsService.decreaseUserBalance(userId, asset, amount);


// Assert
verify(cryptoWalletRepository).save(existingWallet);
}

@Test
public void testDecreaseUserBalance_ExistingWalletAndNotEnoughFunds() {
// Arrange
Long userId = 1L;
String asset = "BTC";

CryptoWallet existingWallet = CryptoWallet.builder()
.userId(userId)
.baseAsset(asset)
.balance(new BigDecimal("1.0"))
.build();

BigDecimal amount = existingWallet.getBalance().add(new BigDecimal(1L));

when(userRepository.findById(userId))
.thenReturn(Optional.of(new User()));

when(cryptoWalletRepository.findByUserAndCryptocurrency(userId, asset))
.thenReturn(Optional.of(existingWallet));

// Act & Assert
assertThrows(IllegalStateException.class, () -> {
walletsService.decreaseUserBalance(userId, asset, amount);
});
}

@Test
public void testDecreaseUserBalance_UserNotExist() {
// Arrange
Long userId = 1L;
String asset = "BTC";
BigDecimal amount = new BigDecimal("0.1");

when(userRepository.findById(userId))
.thenReturn(Optional.empty());

// Act & Assert
assertThrows(NoSuchElementException.class, () -> {
walletsService.decreaseUserBalance(userId, asset, amount);
});
}
}

0 comments on commit 7ec25a5

Please sign in to comment.