Skip to content

Commit

Permalink
✨ 인증 코드 도메인 정의 (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
waterfogSW authored Sep 9, 2024
1 parent d9c25f4 commit cd7e057
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.threedays.domain.auth.entity

import com.threedays.domain.auth.exception.AuthException
import com.threedays.domain.auth.vo.AuthCodeId
import com.threedays.support.common.base.domain.AggregateRoot
import com.threedays.support.common.base.domain.UUIDTypeId
import java.time.LocalDateTime

data class AuthCode(
override val id: AuthCodeId,
val code: Code,
val expireAt: LocalDateTime,
) : AggregateRoot<AuthCode, AuthCodeId>() {

@JvmInline
value class Code(val value: String) {

init {
require(value.length == 6) { "인증 코드는 6자리 숫자로 구성되어야 합니다." }
require(value.all { it.isDigit() }) { "인증 코드는 숫자로만 구성되어야 합니다." }
}

companion object {

fun generate(): Code {
val random: Int = (0..999999).random()
val code: String = random.toString().padStart(6, '0')
return Code(code)
}

}
}

companion object {

fun create(expireAt: LocalDateTime): AuthCode {
return AuthCode(UUIDTypeId.random(), Code.generate(), expireAt)
}

}

fun verify(code: Code) {
if (this.code != code) {
throw AuthException.InvalidAuthCodeException()
}

if (LocalDateTime.now() > expireAt) {
throw AuthException.AuthCodeExpiredException()
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.threedays.domain.auth.exception

import com.threedays.support.common.base.exception.CustomException

sealed class AuthException(
codeNumber: Int,
override val message: String = DEFAULT_MESSAGE,
) : CustomException("Auth", codeNumber, message) {

data class AuthCodeExpiredException(
override val message: String = "인증 코드가 만료되었습니다.",
) : AuthException(1001, message)

data class InvalidAuthCodeException(
override val message: String = "유효하지 않은 인증 코드입니다.",
) : AuthException(1002, message)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.threedays.domain.auth.repository

import com.threedays.domain.auth.entity.AuthCode
import com.threedays.domain.auth.vo.AuthCodeId
import com.threedays.support.common.base.domain.Repository

interface AuthCodeRepository : Repository<AuthCode, AuthCodeId>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.threedays.domain.auth.vo

import com.threedays.support.common.base.domain.UUIDTypeId
import java.util.*

data class AuthCodeId(override val value: UUID) : UUIDTypeId(value)
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package com.threedays.domain.auth.entity

import com.threedays.domain.auth.exception.AuthException
import io.kotest.assertions.throwables.shouldNotThrow
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.annotation.DisplayName
import io.kotest.core.spec.style.DescribeSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import java.time.LocalDateTime

@DisplayName("[도메인] - 인증 코드")
class AuthCodeTest : DescribeSpec({

describe("인증 코드 생성") {
it("새로운 인증 코드를 생성한다") {
// arrange
val expireAt = LocalDateTime.now().plusMinutes(5)

// act
val authCode = AuthCode.create(expireAt)

// assert
authCode.id shouldNotBe null
authCode.code.value.length shouldBe 6
authCode.code.value.all { it.isDigit() } shouldBe true
authCode.expireAt shouldBe expireAt
}
}

describe("인증 코드 검증") {
context("유효한 인증 코드로") {
it("검증에 성공한다") {
// arrange
val expireAt: LocalDateTime = LocalDateTime.now().plusMinutes(5)
val authCode = AuthCode.create(expireAt)
val validCode = authCode.code

// act & assert
shouldNotThrow<AuthException> {
authCode.verify(validCode)
}
}
}

context("잘못된 인증 코드로") {
it("InvalidAuthCodeException을 발생시킨다") {
// arrange
val expireAt = LocalDateTime.now().plusMinutes(5)
val authCode = AuthCode.create(expireAt)
val invalidCode = AuthCode.Code("000000")

// act & assert
shouldThrow<AuthException.InvalidAuthCodeException> {
authCode.verify(invalidCode)
}
}
}

context("만료된 인증 코드로") {
it("AuthCodeExpiredException을 발생시킨다") {
// arrange
val expireAt = LocalDateTime.now().minusMinutes(1)
val authCode = AuthCode.create(expireAt)

// act & assert
shouldThrow<AuthException.AuthCodeExpiredException> {
authCode.verify(authCode.code)
}
}
}
}

describe("인증 코드 값 객체 (Code)") {
context("유효한 코드로") {
it("Code 객체를 생성한다") {
// act
val code = AuthCode.Code("123456")

// assert
code.value shouldBe "123456"
}
}

context("6자리가 아닌 코드로") {
it("예외를 발생시킨다") {
// act & assert
shouldThrow<IllegalArgumentException> {
AuthCode.Code("12345")
}
shouldThrow<IllegalArgumentException> {
AuthCode.Code("1234567")
}
}
}

context("숫자가 아닌 문자가 포함된 코드로") {
it("예외를 발생시킨다") {
// act & assert
shouldThrow<IllegalArgumentException> {
AuthCode.Code("12345a")
}
}
}

context("코드 생성 시") {
it("6자리 숫자로 구성된 코드를 생성한다") {
// act
val generatedCode = AuthCode.Code.generate()

// assert
generatedCode.value.length shouldBe 6
generatedCode.value.all { it.isDigit() } shouldBe true
}
}
}
})
2 changes: 1 addition & 1 deletion openapi
Submodule openapi updated 2 files
+1 −0 .github/codeowners
+39 −1 openapi.yaml

0 comments on commit cd7e057

Please sign in to comment.