diff --git a/http/transaction.http b/http/transaction.http index afb8972..f0777b5 100644 --- a/http/transaction.http +++ b/http/transaction.http @@ -6,3 +6,12 @@ Content-Type: application/json "account": "00000000-0000-0000-0000-000000000000", "amount": 1.23 } + +### Withdrawal +POST {{url}}/withdrawal +Content-Type: application/json + +{ + "account": "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 4936234..9820440 100644 --- a/src/main/kotlin/ltd/lulz/controller/TransactionController.kt +++ b/src/main/kotlin/ltd/lulz/controller/TransactionController.kt @@ -3,10 +3,12 @@ package ltd.lulz.controller import io.github.oshai.kotlinlogging.KotlinLogging import jakarta.validation.Valid import ltd.lulz.exception.AccountNotFoundException +import ltd.lulz.exception.InsufficientFundsException import ltd.lulz.model.Transaction import ltd.lulz.service.TransactionService import org.springframework.http.HttpStatus.CREATED import org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR +import org.springframework.http.HttpStatus.NOT_ACCEPTABLE import org.springframework.http.HttpStatus.NOT_FOUND import org.springframework.validation.annotation.Validated import org.springframework.web.bind.annotation.PostMapping @@ -37,4 +39,19 @@ class TransactionController( } .doOnError { log.warn { "deposit account ${request.account}: " + it.localizedMessage } } .then() + + @PostMapping("/withdrawal") + @ResponseStatus(CREATED) + fun withdrawal( + @Valid @RequestBody request: Transaction.Request, + ): Mono = transactionService.withdrawal(request.account, 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 { "withdrawal 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 c1ea8b6..b2b0ce9 100644 --- a/src/test/kotlin/ltd/lulz/controller/TransactionControllerTest.kt +++ b/src/test/kotlin/ltd/lulz/controller/TransactionControllerTest.kt @@ -6,11 +6,13 @@ import java.math.BigDecimal import java.util.UUID import ltd.lulz.controller.AccountControllerTest.Companion.name import ltd.lulz.exception.AccountNotFoundException +import ltd.lulz.exception.InsufficientFundsException import ltd.lulz.model.AccountEntity import ltd.lulz.model.Transaction import ltd.lulz.service.TransactionService import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.springframework.http.HttpStatus.NOT_ACCEPTABLE import org.springframework.http.MediaType.APPLICATION_JSON import org.springframework.test.web.reactive.server.WebTestClient import reactor.core.publisher.Mono @@ -102,4 +104,94 @@ class TransactionControllerTest { // then result.expectStatus().isNotFound } + + @Test + fun `withdrawal success`() { + // given + val request = Transaction.Request(uuid, amount) + + every { transactionService.withdrawal(any(), any()) } returns Mono.just( + AccountEntity(id = uuid, name = name, amount = amount), + ) + + // when + val result = webTestClient.post() + .uri("/withdrawal") + .contentType(APPLICATION_JSON) + .bodyValue(request) + .exchange() + + // then + result.expectStatus().isCreated + } + + @Test + fun `withdrawal fail amount to small`() { + // given + val request = Transaction.Request(uuid, BigDecimal.valueOf(0.009)) + + // when + val result = webTestClient.post() + .uri("/withdrawal") + .contentType(APPLICATION_JSON) + .bodyValue(request) + .exchange() + + // then + result.expectStatus().isBadRequest + } + + @Test + fun `withdrawal insufficient Funds`() { + // given + val request = Transaction.Request(uuid, amount) + + every { transactionService.withdrawal(any(), any()) } returns Mono.error(InsufficientFundsException()) + + // when + val result = webTestClient.post() + .uri("/withdrawal") + .contentType(APPLICATION_JSON) + .bodyValue(request) + .exchange() + + // then + result.expectStatus().isEqualTo(NOT_ACCEPTABLE) + } + + @Test + fun `withdrawal account not found`() { + // given + val request = Transaction.Request(uuid, amount) + + every { transactionService.withdrawal(any(), any()) } returns Mono.error(AccountNotFoundException()) + + // when + val result = webTestClient.post() + .uri("/withdrawal") + .contentType(APPLICATION_JSON) + .bodyValue(request) + .exchange() + + // then + result.expectStatus().isNotFound + } + + @Test + fun `withdrawal default error`() { + // given + val request = Transaction.Request(uuid, amount) + + every { transactionService.withdrawal(any(), any()) } returns Mono.error(RuntimeException()) + + // when + val result = webTestClient.post() + .uri("/withdrawal") + .contentType(APPLICATION_JSON) + .bodyValue(request) + .exchange() + + // then + result.expectStatus().is5xxServerError + } }