Skip to content

Commit

Permalink
Merge pull request #13 from wanniDev/main
Browse files Browse the repository at this point in the history
테스트 환경 개선
  • Loading branch information
wanniDev authored Dec 6, 2023
2 parents 5646d79 + b927ad7 commit eb3ee09
Show file tree
Hide file tree
Showing 46 changed files with 531 additions and 264 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package org.collaborators.paymentslab.account.application
import org.collaborator.paymentlab.common.Role
import org.collaborators.paymentslab.account.application.command.LoginAccount
import org.collaborators.paymentslab.account.application.command.RegisterAccount
import org.collaborators.paymentslab.account.application.command.RegisterAdminAccount
import org.collaborators.paymentslab.account.application.command.RegisterConfirm
import org.collaborators.paymentslab.account.domain.*
import org.slf4j.LoggerFactory
Expand Down
1 change: 0 additions & 1 deletion account-api/account-domain/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
dependencies {
implementation(project(":common"))
implementation("jakarta.persistence:jakarta.persistence-api:3.1.0")
implementation("org.hibernate:hibernate-core:6.1.7.Final")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.2")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.2")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import java.util.*
@Table(name = "ACCOUNTS")
class Account protected constructor(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,
var id: Long? = null,
var accountKey: String = KeyGenerator.generate("act_"),
var email: String,
var username: String,
Expand All @@ -27,7 +27,7 @@ class Account protected constructor(
@ElementCollection(fetch = FetchType.EAGER)
@Enumerated(EnumType.STRING)
var roles: MutableSet<Role> = hashSetOf(Role.USER)
): AbstractAggregateRoot() {
): AbstractAggregateRoot<Long>() {

init {
generateEmailCheckToken()
Expand All @@ -36,6 +36,10 @@ class Account protected constructor(
var password: String? = null
private set

override fun id(): Long? {
return this.id
}

companion object {
fun register(
email: String, encodedPassword: String, username: String, phoneNumber: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,4 @@ import org.springframework.context.annotation.Import
]
)
@Configuration
class AccountModuleConfiguration {
}
class AccountModuleConfiguration
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import org.collaborator.paymentlab.common.error.ResourceNotFoundException
import org.collaborators.paymentslab.account.domain.Account
import org.collaborators.paymentslab.account.domain.AccountRepository


class AccountRepositoryAdapter(
private val jpaAccountRepository: JpaAccountRepository
): AccountRepository {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import org.collaborator.paymentlab.common.result.ApiResult
import org.collaborators.paymentslab.account.application.AccountService
import org.collaborators.paymentslab.account.application.command.LoginAccount
import org.collaborators.paymentslab.account.application.command.RegisterAccount
import org.collaborators.paymentslab.account.application.command.RegisterAdminAccount
import org.collaborators.paymentslab.account.application.command.RegisterConfirm
import org.collaborators.paymentslab.account.presentation.request.LoginAccountRequest
import org.collaborators.paymentslab.account.presentation.request.RegisterAccountRequest
Expand Down
6 changes: 6 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ dependencies {

/** kafka **/
implementation("org.springframework.kafka:spring-kafka")
/** kafka test **/
implementation("org.springframework.kafka:spring-kafka-test")
implementation("org.testcontainers:kafka:1.19.3")

/** resilience4j **/
implementation("io.github.resilience4j:resilience4j-spring-boot3:2.1.0")
}

tasks {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,26 @@ import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer
import org.springframework.boot.web.client.RestTemplateBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
import org.springframework.web.client.RestTemplate
import java.time.Duration
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

@Configuration
class RestTemplateConfig {

@Bean
fun restTemplate(): RestTemplate {
val restTemplate = RestTemplate()
restTemplate.messageConverters.add(0, MappingJackson2HttpMessageConverter(configureObjectMapper().build()))
return restTemplate
return RestTemplateBuilder()
.setConnectTimeout(Duration.ofSeconds(5))
.setReadTimeout(Duration.ofSeconds(5))
.messageConverters(MappingJackson2HttpMessageConverter(configureObjectMapper().build()))
.build()
}

// JSR310
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.collaborators.paymentslab.config.resilience4j

import io.github.resilience4j.common.retry.configuration.RetryConfigCustomizer
import io.github.resilience4j.core.IntervalFunction
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

private const val TOSS_PAYMENTS_APPROVAL = "tossPaymentsApprovalProcessor"
private const val INITIAL_INTERVAL = 1000L
private const val MULTIPLIER = 2.0
private const val RANDOMIZATION_FACTOR = 0.6
private const val MAX_RETRIES = 4

@Configuration
class Resilience4jConfig {
@Bean
fun retryConfigCustomizer(): RetryConfigCustomizer {
val jitterBackoffFunction
= IntervalFunction.ofExponentialRandomBackoff(INITIAL_INTERVAL, MULTIPLIER, RANDOMIZATION_FACTOR)

return RetryConfigCustomizer
.of(TOSS_PAYMENTS_APPROVAL) {
it.maxAttempts(MAX_RETRIES)
.intervalFunction(jitterBackoffFunction)
.build()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class JwtAuthenticationFilter(

if (SecurityContextHolder.getContext().authentication == null) {
val context = UsernamePasswordAuthenticationToken(
AuthenticatedUser(account.id!!, account.accountKey!!, account.roles), null, authorities(account.roles)
AuthenticatedUser(account.id()!!, account.accountKey!!, account.roles), null, authorities(account.roles)
)
SecurityContextHolder.getContext().authentication = context
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import org.springframework.context.annotation.Import
import org.springframework.context.annotation.Profile
import org.springframework.http.HttpMethod.GET
import org.springframework.http.HttpMethod.POST
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ class GlobalExceptionHandler(private val objectMapper: ObjectMapper): ErrorContr
}.collect(Collectors.joining(" "))
}

private fun format(f: FieldError): String? {
private fun format(f: FieldError): String {
return if (f.field == "password") {
"Field : [" + f.field + "] " + "Reason: [" + f.defaultMessage + "]"
} else "Field : [" + f.field + "] " + "Value: [" + f.rejectedValue + "]"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
package org.collaborators.paymentslab

import com.fasterxml.jackson.databind.ObjectMapper
import org.collaborator.paymentlab.common.Role
import org.collaborator.paymentlab.common.URI_HOST
import org.collaborator.paymentlab.common.URI_PORT
import org.collaborator.paymentlab.common.URI_SCHEME
import org.collaborators.paymentslab.account.domain.Account
import org.collaborators.paymentslab.account.domain.AccountRepository
import org.collaborators.paymentslab.account.domain.PasswordEncrypt
import org.collaborators.paymentslab.account.domain.TokenGenerator
import org.collaborators.paymentslab.payment.infrastructure.kafka.StringKafkaTemplateWrapper
import org.junit.jupiter.api.BeforeEach
import org.collaborators.paymentslab.payment.domain.repository.PaymentHistoryRepository
import org.collaborators.paymentslab.payment.domain.repository.PaymentOrderRepository
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.kotlin.any
import org.mockito.kotlin.doNothing
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.kafka.core.KafkaTemplate
import org.springframework.kafka.core.*
import org.springframework.kafka.listener.*
import org.springframework.restdocs.RestDocumentationExtension
import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor
import org.springframework.restdocs.operation.preprocess.Preprocessors
Expand All @@ -37,20 +35,6 @@ import org.springframework.test.web.servlet.MockMvc
@ExtendWith(RestDocumentationExtension::class)
@ActiveProfiles("test")
abstract class AbstractApiTest {
@Autowired
protected lateinit var mockMvc: MockMvc

@Autowired
protected lateinit var objectMapper: ObjectMapper

@Autowired
protected lateinit var tokenGenerator: TokenGenerator

@Autowired lateinit var encrypt: PasswordEncrypt

@MockBean
protected lateinit var stringKafkaTemplateWrapper: StringKafkaTemplateWrapper


@Value("\${uri.scheme}")
protected lateinit var scheme: String
Expand All @@ -64,33 +48,27 @@ abstract class AbstractApiTest {
@Value("\${admin.key}")
protected lateinit var adminKey: String

@BeforeEach
fun setUp() {
doNothing().`when`(stringKafkaTemplateWrapper).send(any(), any())
}
@Autowired
protected lateinit var mockMvc: MockMvc

protected fun testEntityForRegister(email: String): Account {
val account = Account.register(
email,
encrypt.encode("qqqwww123"),
"testName",
"010-1234-1234"
)
account.completeRegister()
return account
}
@Autowired
protected lateinit var tokenGenerator: TokenGenerator

protected fun testEntityForAdminRegister(email: String): Account {
val account = Account.register(
email,
encrypt.encode("qqqwww123"),
"testName",
"010-1234-1234",
hashSetOf(Role.USER, Role.ADMIN)
)
account.completeRegister()
return account
}
@Autowired lateinit var encrypt: PasswordEncrypt

@MockBean
protected lateinit var kafkaTemplate: KafkaTemplate<String, String>

@MockBean
protected lateinit var accountRepository: AccountRepository

@MockBean
protected lateinit var paymentHistoryRepository: PaymentHistoryRepository

@MockBean
protected lateinit var paymentOrderRepository: PaymentOrderRepository

protected val objectMapper: ObjectMapper = ObjectMapper()

protected fun getDocumentRequest(): OperationRequestPreprocessor {
return Preprocessors.preprocessRequest(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package org.collaborators.paymentslab.account.presentation

import org.collaborator.paymentlab.common.*
import org.collaborator.paymentlab.common.error.ErrorCode
import org.collaborator.paymentlab.common.error.ResourceNotFoundException
import org.collaborators.paymentslab.AbstractApiTest
import org.collaborators.paymentslab.account.domain.Account
import org.collaborators.paymentslab.account.domain.AccountRepository
import org.collaborators.paymentslab.account.presentation.request.LoginAccountRequest
import org.collaborators.paymentslab.account.presentation.request.RegisterAccountRequest
import org.collaborators.paymentslab.account.presentation.request.RegisterAdminAccountRequest
import org.collaborators.paymentslab.account.presentation.request.RegisterConfirmRequest
import org.junit.jupiter.api.*
import org.springframework.beans.factory.annotation.Autowired
import org.mockito.kotlin.any
import org.mockito.kotlin.given
import org.springframework.http.MediaType
import org.springframework.restdocs.headers.HeaderDocumentation.headerWithName
import org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders
Expand All @@ -22,15 +23,18 @@ import org.springframework.restdocs.payload.PayloadDocumentation.*
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status

class AuthenticationApiTest @Autowired constructor(
private val accountRepository: AccountRepository
) : AbstractApiTest() {
class AuthenticationApiTest : AbstractApiTest() {
@Test
@DisplayName("회원가입 api 동작")
fun registerTest() {
val requestDto = RegisterAccountRequest("[email protected]", "qwer1234", "helloUsername", "010-1234-5678")
val reqBody = this.objectMapper.writeValueAsString(requestDto)

val mockMember = MockAuthentication.mockUserAccountFrom(requestDto)
given(accountRepository.existByEmail(mockMember.email)).willReturn(false)
given(accountRepository.save(any())).willReturn(mockMember)
given(accountRepository.findByEmail(any())).willReturn(mockMember)

this.mockMvc.perform(
RestDocumentationRequestBuilders
.post("$V1_AUTH/$REGISTER")
Expand Down Expand Up @@ -70,6 +74,11 @@ class AuthenticationApiTest @Autowired constructor(
RegisterAdminAccountRequest("[email protected]", "qwer1234", "helloUsername", "010-1234-5678", adminKey)
val reqBody = this.objectMapper.writeValueAsString(requestDto)

val mockMember = MockAuthentication.mockAdminAccountFrom(requestDto)
given(accountRepository.existByEmail(mockMember.email)).willReturn(false)
given(accountRepository.save(any())).willReturn(mockMember)
given(accountRepository.findByEmail(any())).willReturn(mockMember)

this.mockMvc.perform(
RestDocumentationRequestBuilders
.post("$V1_AUTH/$REGISTER_ADMIN")
Expand Down Expand Up @@ -190,9 +199,9 @@ class AuthenticationApiTest @Autowired constructor(
@Test
@DisplayName("회원가입 검증 api 동작 테스트")
fun confirmTest() {
val registered = accountRepository.save(Account.register("[email protected]",
encrypt.encode("qqqwww123"), "hello2", "010-1234-1234"))
val account = accountRepository.findByEmail(registered.email)
val account = MockAuthentication.mockUserAccount()
given(accountRepository.save(any())).willReturn(account)
given(accountRepository.findByEmail(any())).willReturn(account)

val requestDto = RegisterConfirmRequest(account.emailCheckToken!!, account.email)
val reqBody = this.objectMapper.writeValueAsString(requestDto)
Expand Down Expand Up @@ -224,10 +233,11 @@ class AuthenticationApiTest @Autowired constructor(
@Test
@DisplayName("로그인 api 동작 테스트")
fun loginTest() {
val registered = testEntityForRegister("[email protected]")
val account = accountRepository.save(registered)
val account = MockAuthentication.mockUserAccount()
given(accountRepository.save(any())).willReturn(account)
given(accountRepository.findByEmail(any())).willReturn(account)

val requestDto = LoginAccountRequest(account.email, "qqqwww123")
val requestDto = LoginAccountRequest(account.email, MockAuthentication.testPlainPassword)
val reqBody = this.objectMapper.writeValueAsString(requestDto)

this.mockMvc.perform(
Expand Down Expand Up @@ -270,11 +280,10 @@ class AuthenticationApiTest @Autowired constructor(
@Test
@DisplayName("잘못된 로그인 api 에러 테스트")
fun loginErrorTest() {
val registered = accountRepository.save(Account.register("[email protected]",
encrypt.encode("qqqwww123"), "hello2", "010-1234-1234"))
val account = accountRepository.findByEmail(registered.email)
val account = MockAuthentication.mockUserAccount()
given(accountRepository.findByEmail(any())).willReturn(account)

val requestDto = LoginAccountRequest(account.email, "wrongpassword123")
val requestDto = LoginAccountRequest(account.email, MockAuthentication.testWrongPlainPassword)
val reqBody = this.objectMapper.writeValueAsString(requestDto)

this.mockMvc.perform(
Expand Down Expand Up @@ -305,10 +314,11 @@ class AuthenticationApiTest @Autowired constructor(
@Test
@DisplayName("토큰 재발급 api 테스트")
fun reIssuanceTest() {
val registered = testEntityForRegister("[email protected]")
val account = accountRepository.save(registered)
val account = MockAuthentication.mockUserAccount()
val tokens = tokenGenerator.generate(account.email, account.roles)

given(accountRepository.findByEmail(any())).willReturn(account)

this.mockMvc.perform(
RestDocumentationRequestBuilders
.post("$V1_AUTH/$RE_ISSUANCE")
Expand Down Expand Up @@ -344,8 +354,9 @@ class AuthenticationApiTest @Autowired constructor(
@Test
@DisplayName("회원 등록이 안된 사용자의 토큰으로 재발급 실패 api 테스트")
fun notRegisteredReIssuanceTest() {
val registered = testEntityForRegister("[email protected]")
val tokens = tokenGenerator.generate(registered.email, registered.roles)
val account = MockAuthentication.mockUserAccount()
given(accountRepository.findByEmail(MockAuthentication.testWrongReissuerEmail)).willThrow(ResourceNotFoundException(ErrorCode.ACCOUNT_NOT_FOUND))
val tokens = tokenGenerator.generate(MockAuthentication.testWrongReissuerEmail, account.roles)

this.mockMvc.perform(
RestDocumentationRequestBuilders
Expand Down
Loading

0 comments on commit eb3ee09

Please sign in to comment.