From ddf91791e50f030a5ebc517c5967cfc0cb4243e9 Mon Sep 17 00:00:00 2001 From: Swordsteel Date: Sat, 13 Sep 2025 11:42:16 +0200 Subject: [PATCH] update TransactionController with transfer --- http/transaction.http | 10 +++ .../lulz/controller/TransactionController.kt | 16 ++++ .../controller/TransactionControllerTest.kt | 90 +++++++++++++++++++ 3 files changed, 116 insertions(+) diff --git a/http/transaction.http b/http/transaction.http index f0777b5..75ffb35 100644 --- a/http/transaction.http +++ b/http/transaction.http @@ -15,3 +15,13 @@ Content-Type: application/json "account": "00000000-0000-0000-0000-000000000000", "amount": 1 } + +### Withdrawal +POST {{url}}/transfer +Content-Type: application/json + +{ + "account": "00000000-0000-0000-0000-000000000000", + "receiver": "00000000-0000-0000-0000-000000000000", + "amount": 1 +} diff --git a/src/main/kotlin/ltd/lulz/controller/TransactionController.kt b/src/main/kotlin/ltd/lulz/controller/TransactionController.kt index 9820440..1d7e649 100644 --- a/src/main/kotlin/ltd/lulz/controller/TransactionController.kt +++ b/src/main/kotlin/ltd/lulz/controller/TransactionController.kt @@ -5,6 +5,7 @@ import jakarta.validation.Valid import ltd.lulz.exception.AccountNotFoundException import ltd.lulz.exception.InsufficientFundsException import ltd.lulz.model.Transaction +import ltd.lulz.model.Transfer import ltd.lulz.service.TransactionService import org.springframework.http.HttpStatus.CREATED import org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR @@ -54,4 +55,19 @@ class TransactionController( } .doOnError { log.warn { "withdrawal account ${request.account}: " + it.localizedMessage } } .then() + + @PostMapping("/transfer") + @ResponseStatus(CREATED) + fun transfer( + @Valid @RequestBody request: Transfer.Request, + ): Mono = transactionService.transfer(request.account, request.receiver, request.amount) + .onErrorResume { + when (it) { + is InsufficientFundsException -> Mono.error(ResponseStatusException(NOT_ACCEPTABLE)) + is AccountNotFoundException -> Mono.error(ResponseStatusException(NOT_FOUND)) + else -> Mono.error(ResponseStatusException(INTERNAL_SERVER_ERROR)) + } + } + .doOnError { log.warn { "transfer account ${request.account}: " + it.localizedMessage } } + .then() } diff --git a/src/test/kotlin/ltd/lulz/controller/TransactionControllerTest.kt b/src/test/kotlin/ltd/lulz/controller/TransactionControllerTest.kt index b2b0ce9..b2762cd 100644 --- a/src/test/kotlin/ltd/lulz/controller/TransactionControllerTest.kt +++ b/src/test/kotlin/ltd/lulz/controller/TransactionControllerTest.kt @@ -9,6 +9,7 @@ import ltd.lulz.exception.AccountNotFoundException import ltd.lulz.exception.InsufficientFundsException import ltd.lulz.model.AccountEntity import ltd.lulz.model.Transaction +import ltd.lulz.model.Transfer import ltd.lulz.service.TransactionService import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -23,6 +24,7 @@ class TransactionControllerTest { companion object { val amount: BigDecimal = BigDecimal.valueOf(0.01) val uuid: UUID = UUID.fromString("00000000-0000-0000-0000-000000000000") + val receiver = UUID.fromString("00000000-0000-0000-0000-000000000001") } private val transactionService: TransactionService = mockk() @@ -194,4 +196,92 @@ class TransactionControllerTest { // then result.expectStatus().is5xxServerError } + + @Test + fun `transfer success`() { + // given + val request = Transfer.Request(uuid, receiver, amount) + + every { transactionService.transfer(any(), any(), any()) } returns Mono.empty() + + // when + val result = webTestClient.post() + .uri("/transfer") + .contentType(APPLICATION_JSON) + .bodyValue(request) + .exchange() + + // then + result.expectStatus().isCreated + } + + @Test + fun `transfer fail amount to small`() { + // given + val request = Transfer.Request(uuid, receiver, BigDecimal.valueOf(0.009)) + + // when + val result = webTestClient.post() + .uri("/transfer") + .contentType(APPLICATION_JSON) + .bodyValue(request) + .exchange() + + // then + result.expectStatus().isBadRequest + } + + @Test + fun `transfer insufficient Funds`() { + // given + val request = Transfer.Request(uuid, receiver, amount) + + every { transactionService.transfer(any(), any(), any()) } returns Mono.error(InsufficientFundsException()) + + // when + val result = webTestClient.post() + .uri("/transfer") + .contentType(APPLICATION_JSON) + .bodyValue(request) + .exchange() + + // then + result.expectStatus().isEqualTo(NOT_ACCEPTABLE) + } + + @Test + fun `transfer account not found`() { + // given + val request = Transfer.Request(uuid, receiver, amount) + + every { transactionService.transfer(any(), any(), any()) } returns Mono.error(AccountNotFoundException()) + + // when + val result = webTestClient.post() + .uri("/transfer") + .contentType(APPLICATION_JSON) + .bodyValue(request) + .exchange() + + // then + result.expectStatus().isNotFound + } + + @Test + fun `transfer default error`() { + // given + val request = Transfer.Request(uuid, receiver, amount) + + every { transactionService.transfer(any(), any(), any()) } returns Mono.error(RuntimeException()) + + // when + val result = webTestClient.post() + .uri("/transfer") + .contentType(APPLICATION_JSON) + .bodyValue(request) + .exchange() + + // then + result.expectStatus().is5xxServerError + } }