Skip to content

Commit

Permalink
feat: restructure tests and included concurrency tests
Browse files Browse the repository at this point in the history
  • Loading branch information
nc1z committed Jul 15, 2024
1 parent f463c28 commit 73cf637
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.example.settlementsenrichment.entity;

import com.example.settlementsenrichment.util.ValidationConstants;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.*;
Expand Down Expand Up @@ -72,4 +73,8 @@ public class MarketSettlementMessage {
@Column(name = "supporting_information")
@Schema(type = "String", example = "/BNF/FFC-4697132")
private String supportingInformation;

@Version
@JsonIgnore
private Integer version;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.example.settlementsenrichment.exception;

import org.hibernate.exception.LockAcquisitionException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
Expand Down Expand Up @@ -66,6 +67,15 @@ public ResponseEntity<ErrorResponse> handleHttpMessageNotReadableException(HttpM
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
}

@ExceptionHandler(LockAcquisitionException.class)
public ResponseEntity<ErrorResponse> handleLockAcquisitionException(LockAcquisitionException ex) {
Map<String, String> errors = new HashMap<>();
errors.put("detail", "Database lock acquisition failure");

ErrorResponse errorResponse = new ErrorResponse(HttpStatus.CONFLICT.value(), "Database conflict error", errors);
return ResponseEntity.status(HttpStatus.CONFLICT).body(errorResponse);
}

@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGlobalException(Exception ex, WebRequest request) {
ErrorResponse errorResponse = new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), "An unexpected error occurred");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import java.math.RoundingMode;
Expand All @@ -28,7 +29,7 @@ public MarketSettlementMessageService(MarketSettlementMessageRepository marketSe
this.standardSettlementInstructionRepository = standardSettlementInstructionRepository;
}

@Transactional
@Transactional(isolation = Isolation.SERIALIZABLE)
public MarketSettlementMessage createMarketSettlementMessage(TradeRequest tradeRequest) {

if (findOptionalByTradeId(tradeRequest.tradeId()).isPresent()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.example.settlementsenrichment.integration;

import com.example.settlementsenrichment.AbstractIntegrationTest;
import com.example.settlementsenrichment.dto.TradeRequest;
import com.example.settlementsenrichment.entity.MarketSettlementMessage;
import com.example.settlementsenrichment.entity.StandardSettlementInstruction;
import com.example.settlementsenrichment.repository.MarketSettlementMessageRepository;
import com.example.settlementsenrichment.repository.StandardSettlementInstructionRepository;
import com.example.settlementsenrichment.service.MarketSettlementMessageService;
import org.hibernate.exception.LockAcquisitionException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;

import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

import static org.junit.jupiter.api.Assertions.*;

public class MarketSettlementMessageConcurrentWriteTest extends AbstractIntegrationTest {

@Autowired
private StandardSettlementInstructionRepository standardSettlementInstructionRepository;

@Autowired
private MarketSettlementMessageRepository marketSettlementMessageRepository;

@Autowired
private MarketSettlementMessageService marketSettlementMessageService;

@BeforeEach
void setUp() {
standardSettlementInstructionRepository.deleteAll();

StandardSettlementInstruction ssi = StandardSettlementInstruction.builder()
.code("OCBC_DBS_1")
.payerAccountNumber("438421")
.payerBank("OCBCSGSGXXX")
.receiverAccountNumber("05461368")
.receiverBank("DBSSGB2LXXX")
.supportingInformation("BNF:FFC-4697132")
.build();

standardSettlementInstructionRepository.save(ssi);
}

@Test
void testConcurrentWrites() throws ExecutionException {
TradeRequest tradeRequest1 = new TradeRequest("16846548", "OCBC_DBS_1", new BigDecimal("12894.65"), "USD", "20022020");
TradeRequest tradeRequest2 = new TradeRequest("16846548", "OCBC_DBS_1", new BigDecimal("12894.65"), "USD", "20022020");

CompletableFuture<MarketSettlementMessage> future1 = CompletableFuture.supplyAsync(() -> marketSettlementMessageService.createMarketSettlementMessage(tradeRequest1));
CompletableFuture<MarketSettlementMessage> future2 = CompletableFuture.supplyAsync(() -> marketSettlementMessageService.createMarketSettlementMessage(tradeRequest2));

ExecutionException exception = assertThrows(ExecutionException.class, () -> {
CompletableFuture.allOf(future1, future2).get();
});

Throwable cause = exception.getCause();
assertNotNull(cause);
assertNotNull(cause.getCause());
assertEquals(LockAcquisitionException.class, cause.getCause().getClass(), "Expected LockAcquisitionException");

List<MarketSettlementMessage> messages = marketSettlementMessageRepository.findAll();
assertEquals(1, messages.size(), "Only one MarketSettlementMessage should exist with the given tradeId");
assertEquals("16846548", messages.get(0).getTradeId());
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.example.settlementsenrichment.integration;
package com.example.settlementsenrichment.integration.controller;

import com.example.settlementsenrichment.AbstractIntegrationTest;
import com.example.settlementsenrichment.entity.MarketSettlementMessage;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.example.settlementsenrichment.controller;
package com.example.settlementsenrichment.unit.controller;

import com.example.settlementsenrichment.controller.MarketSettlementMessageController;
import com.example.settlementsenrichment.dto.TradeRequest;
import com.example.settlementsenrichment.entity.MarketSettlementMessage;
import com.example.settlementsenrichment.entity.Party;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.example.settlementsenrichment.service;
package com.example.settlementsenrichment.unit.service;

import com.example.settlementsenrichment.dto.TradeRequest;
import com.example.settlementsenrichment.entity.MarketSettlementMessage;
Expand All @@ -8,6 +8,7 @@
import com.example.settlementsenrichment.exception.ResourceNotFoundException;
import com.example.settlementsenrichment.repository.MarketSettlementMessageRepository;
import com.example.settlementsenrichment.repository.StandardSettlementInstructionRepository;
import com.example.settlementsenrichment.service.MarketSettlementMessageService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
Expand Down

0 comments on commit 73cf637

Please sign in to comment.