From 72ac37e6033c27269ccfc6a1f21b06bb8e65093e Mon Sep 17 00:00:00 2001 From: Swordsteel Date: Tue, 21 Jan 2025 11:08:20 +0100 Subject: [PATCH] add get accounts - add get accounts to account.http - add getAccounts to AccountController - add missing test in AccountServiceTest - add getAccounts to AccountService --- http/account.http | 9 ++ .../hlaeja/controller/AccountController.kt | 25 +++ .../ltd/hlaeja/service/AccountService.kt | 11 ++ .../ltd/hlaeja/service/AccountServiceTest.kt | 153 ++++++++++++++++-- 4 files changed, 188 insertions(+), 10 deletions(-) diff --git a/http/account.http b/http/account.http index 1bbe2e4..e8dced4 100644 --- a/http/account.http +++ b/http/account.http @@ -14,3 +14,12 @@ Content-Type: application/json "ROLE_TEST" ] } + +### Get accounts +GET {{hostname}}/accounts + +### Get accounts +GET {{hostname}}/accounts/page-1 + +### Get accounts +GET {{hostname}}/accounts/page-1/show-5 diff --git a/src/main/kotlin/ltd/hlaeja/controller/AccountController.kt b/src/main/kotlin/ltd/hlaeja/controller/AccountController.kt index 405d0d4..587c6a6 100644 --- a/src/main/kotlin/ltd/hlaeja/controller/AccountController.kt +++ b/src/main/kotlin/ltd/hlaeja/controller/AccountController.kt @@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RestController +import reactor.core.publisher.Flux import reactor.core.publisher.Mono @RestController @@ -18,6 +19,10 @@ class AccountController( private val accountService: AccountService, private val passwordEncoder: PasswordEncoder, ) { + companion object { + const val DEFAULT_PAGE: Int = 1 + const val DEFAULT_SIZE: Int = 25 + } @GetMapping("/account-{uuid}") fun getAccount( @@ -30,4 +35,24 @@ class AccountController( @RequestBody request: Account.Request, ): Mono = accountService.addAccount(request.toAccountEntity(passwordEncoder)) .map { it.toAccountResponse() } + + @GetMapping("/accounts") + fun getDefaultAccounts(): Flux = getAccounts(DEFAULT_PAGE, DEFAULT_SIZE) + + @GetMapping("/accounts/page-{page}") + fun getAccountsPage( + @PathVariable page: Int, + ): Flux = getAccounts(page, DEFAULT_SIZE) + + @GetMapping("/accounts/page-{page}/show-{size}") + fun getAccountsPageSize( + @PathVariable page: Int, + @PathVariable size: Int, + ): Flux = getAccounts(page, size) + + private fun getAccounts( + page: Int, + size: Int, + ): Flux = accountService.getAccounts(page, size) + .map { it.toAccountResponse() } } diff --git a/src/main/kotlin/ltd/hlaeja/service/AccountService.kt b/src/main/kotlin/ltd/hlaeja/service/AccountService.kt index c83ddbe..e67266c 100644 --- a/src/main/kotlin/ltd/hlaeja/service/AccountService.kt +++ b/src/main/kotlin/ltd/hlaeja/service/AccountService.kt @@ -1,6 +1,7 @@ package ltd.hlaeja.service import io.github.oshai.kotlinlogging.KotlinLogging +import java.lang.IllegalArgumentException import java.util.UUID import ltd.hlaeja.entity.AccountEntity import ltd.hlaeja.repository.AccountRepository @@ -8,6 +9,7 @@ import org.springframework.dao.DuplicateKeyException import org.springframework.http.HttpStatus import org.springframework.stereotype.Service import org.springframework.web.server.ResponseStatusException +import reactor.core.publisher.Flux import reactor.core.publisher.Mono private val log = KotlinLogging.logger {} @@ -40,4 +42,13 @@ class AccountService( else -> Mono.error(ResponseStatusException(HttpStatus.BAD_REQUEST)) } } + + fun getAccounts(page: Int, size: Int): Flux = try { + accountRepository.findAll() + .skip((page - 1).toLong() * size) + .take(size.toLong()) + .doOnNext { log.debug { "Retrieved accounts $page with size $size" } } + } catch (e: IllegalArgumentException) { + Flux.error(ResponseStatusException(HttpStatus.BAD_REQUEST)) + } } diff --git a/src/test/kotlin/ltd/hlaeja/service/AccountServiceTest.kt b/src/test/kotlin/ltd/hlaeja/service/AccountServiceTest.kt index 70e4abf..797ac0d 100644 --- a/src/test/kotlin/ltd/hlaeja/service/AccountServiceTest.kt +++ b/src/test/kotlin/ltd/hlaeja/service/AccountServiceTest.kt @@ -9,13 +9,31 @@ import ltd.hlaeja.entity.AccountEntity import ltd.hlaeja.repository.AccountRepository import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.springframework.dao.DuplicateKeyException +import org.springframework.http.HttpStatus.BAD_REQUEST +import org.springframework.http.HttpStatus.CONFLICT import org.springframework.web.server.ResponseStatusException +import reactor.core.publisher.Flux import reactor.core.publisher.Mono import reactor.test.StepVerifier class AccountServiceTest { companion object { val account = UUID.fromString("00000000-0000-0000-0000-000000000002") + val accountEntity = AccountEntity( + account, + ZonedDateTime.now(), + ZonedDateTime.now(), + true, + "username", + "password", + "ROLE_TEST", + ) + val accounts = Flux.just( + accountEntity.copy(username = "username1"), + accountEntity.copy(username = "username2"), + accountEntity.copy(username = "username3"), + ) } private lateinit var accountRepository: AccountRepository @@ -30,16 +48,6 @@ class AccountServiceTest { @Test fun `get account by id - success`() { // given - val accountEntity = AccountEntity( - account, - ZonedDateTime.now(), - ZonedDateTime.now(), - true, - "username", - "password", - "ROLE_TEST", - ) - every { accountRepository.findById(any(UUID::class)) } returns Mono.just(accountEntity) // when @@ -64,4 +72,129 @@ class AccountServiceTest { // then verify { accountRepository.findById(any(UUID::class)) } } + + @Test + fun `get account by username - success`() { + // given + every { accountRepository.findByUsername(any()) } returns Mono.just(accountEntity) + + // when + StepVerifier.create(accountService.getUserByUsername("username")) + .expectNext(accountEntity) + .verifyComplete() + + // then + verify { accountRepository.findByUsername(any()) } + } + + @Test + fun `get account by username - fail does not exist`() { + // given + every { accountRepository.findByUsername(any()) } returns Mono.empty() + + // when + StepVerifier.create(accountService.getUserByUsername("username")) + .expectError(ResponseStatusException::class.java) + .verify() + + // then + verify { accountRepository.findByUsername(any()) } + } + + @Test + fun `add account - success`() { + // given + every { accountRepository.save(any()) } returns Mono.just(accountEntity) + + // when + StepVerifier.create(accountService.addAccount(accountEntity)) + .expectNext(accountEntity) + .verifyComplete() + + // then + verify { accountRepository.save(any()) } + } + + @Test + fun `add account - fail duplicated user`() { + // given + every { accountRepository.save(any()) } returns Mono.error(DuplicateKeyException("Test")) + + // when + StepVerifier.create(accountService.addAccount(accountEntity)) + .expectErrorMatches { error -> + error is ResponseStatusException && error.statusCode == CONFLICT + } + .verify() + + // then + verify { accountRepository.save(any()) } + } + + @Test + fun `add account - fail`() { + // given + every { accountRepository.save(any()) } returns Mono.error(RuntimeException()) + + // when + StepVerifier.create(accountService.addAccount(accountEntity)) + .expectErrorMatches { error -> + error is ResponseStatusException && error.statusCode == BAD_REQUEST + } + .verify() + + // then + verify { accountRepository.save(any()) } + } + + @Test + fun `get accounts - limit size success`() { + // given + every { accountRepository.findAll() } returns accounts + + // when + StepVerifier.create(accountService.getAccounts(1,2)) + .expectNextMatches { accountEntity -> + accountEntity.username == "username1" + } + .expectNextMatches {accountEntity -> + accountEntity.username == "username2" + } + .verifyComplete() + + // then + verify { accountRepository.findAll() } + } + + @Test + fun `get accounts - negative page fail`() { + // given + every { accountRepository.findAll() } returns accounts + + // when + StepVerifier.create(accountService.getAccounts(-1,10)) + .expectErrorMatches { error -> + error is ResponseStatusException && error.statusCode == BAD_REQUEST + } + .verify() + + // then + verify { accountRepository.findAll() } + } + + @Test + fun `get accounts - negative size fail`() { + // given + every { accountRepository.findAll() } returns accounts + + // when + StepVerifier.create(accountService.getAccounts(1,-10)) + .expectErrorMatches { error -> + error is ResponseStatusException && error.statusCode == BAD_REQUEST + } + .verify() + + // then + verify { accountRepository.findAll() } + } }