Skip to content

Commit

Permalink
#64 - 비속어를 검증한다. (#83)
Browse files Browse the repository at this point in the history
* feat: BadWords Checker 구현

* feat: VoteOption Content VO 구현

* refactor: 텍스트 필드에 욕설 검증 적용

* test: VoteOptionContent Test 추가

* test: MessageContentTest 추가

* test: Introduction Test 추가

* feat: 비속어 판별 API 추가

* test: validate test 추가

* test: ServiceTest 추가

* test: Pagination Test 보강

* test: Test 컴파일 에러 수정

* test: SpringBootTest 제거

* refactor: lastCursorId 추가

* refactor: Transactional 어노테이션 설정 변경

* test: Test 수정

* refactor: CreatedVote 수정

* test: Event Test 추가
  • Loading branch information
kpeel5839 authored Aug 9, 2024
1 parent dda6919 commit 71ea9e6
Show file tree
Hide file tree
Showing 83 changed files with 1,635 additions and 125 deletions.
25 changes: 25 additions & 0 deletions app/src/main/kotlin/com/wespot/common/ProfanityController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.wespot.common

import com.wespot.common.dto.CheckProfanityRequest
import com.wespot.common.`in`.CheckProfanityUseCase
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping("/api/v1/check-profanity")
class ProfanityController(
private val checkProfanityUseCase: CheckProfanityUseCase
) {

@PostMapping
fun checkProfanity(@RequestBody message: CheckProfanityRequest): ResponseEntity<Unit> {
checkProfanityUseCase.checkProfanity(message)

return ResponseEntity.noContent()
.build()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ class NotificationController(
@GetMapping
fun getNotifications(
@RequestParam(required = false) cursorId: Long?,
@RequestParam limit: Long
): ResponseEntity<NotificationResponses> {
val notifications = inquiryNotificationUseCase.getNotifications(cursorId, limit)
val notifications = inquiryNotificationUseCase.getNotifications(cursorId, 10)

return ResponseEntity.ok(notifications)
}
Expand Down
8 changes: 3 additions & 5 deletions app/src/main/kotlin/com/wespot/vote/VoteController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,9 @@ class VoteController(

@GetMapping("/received")
fun getReceivedVotes(
@RequestParam(required = false) cursorId: Long?,
@RequestParam limit: Long
@RequestParam(required = false) cursorId: Long?
): ResponseEntity<ReceivedVotesResponses> {
val responses = receivedVoteUseCase.getReceivedVotes(cursorId, limit)
val responses = receivedVoteUseCase.getReceivedVotes(cursorId, 10)

return ResponseEntity.ok(responses)
}
Expand All @@ -88,9 +87,8 @@ class VoteController(
@GetMapping("/sent")
fun getSentVotes(
@RequestParam(required = false) cursorId: Long?,
@RequestParam limit: Long
): ResponseEntity<SentVotesResponses> {
val responses = sentVoteUseCase.getSentVotes(cursorId, limit)
val responses = sentVoteUseCase.getSentVotes(cursorId, 10)

return ResponseEntity.ok(responses)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import com.wespot.auth.port.out.RefreshTokenPort
import com.wespot.auth.service.jwt.JwtTokenProvider
import com.wespot.school.port.out.SchoolPort
import com.wespot.user.SocialType
import com.wespot.user.event.CreatedVoteEvent
import com.wespot.user.event.SignUpUserEvent
import com.wespot.user.fixture.UserFixture
import com.wespot.user.port.out.ProfilePort
import com.wespot.user.port.out.UserConsentPort
Expand All @@ -24,6 +26,7 @@ import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.spyk
import org.springframework.context.ApplicationEventPublisher
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.core.Authentication
import org.springframework.security.crypto.password.PasswordEncoder
Expand All @@ -42,6 +45,7 @@ class AuthServiceTest : BehaviorSpec({
val authenticationManager = mockk<AuthenticationManager>()
val passwordEncoder = mockk<PasswordEncoder>()
val refreshTokenService = mockk<RefreshTokenService>()
val eventPublisher = mockk<ApplicationEventPublisher>()

val secretKey = "testSecretKey"

Expand All @@ -59,7 +63,7 @@ class AuthServiceTest : BehaviorSpec({
authenticationManager = authenticationManager,
passwordEncoder = passwordEncoder,
refreshTokenService = refreshTokenService,
eventPublisher = mockk(),
eventPublisher = eventPublisher,
secretKey = secretKey
)
)
Expand Down Expand Up @@ -190,6 +194,8 @@ class AuthServiceTest : BehaviorSpec({
every { userPort.save(any()) } returns user
every { authService.saveRelatedEntities(user, signUpRequest) } just Runs
every { authService.signIn(any()) } returns tokenAndUserDetailResponse
every { eventPublisher.publishEvent(SignUpUserEvent(user)) } returns Unit
every { eventPublisher.publishEvent(CreatedVoteEvent(user)) } returns Unit

`when`("사용자가 signUp을 호출할 때") {
val response = authService.signUp(signUpRequest)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.wespot.common.domain

import com.wespot.common.ProfanityChecker
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.throwable.shouldHaveMessage

class ProfanityCheckerTest : BehaviorSpec({

given("사용자가 내용을 입력할 때") {
val badWords = listOf(
"ㅅ_ㅂ",
"ㅅ________________ㅂ",
"너는 ㅅ!ㅂ",
"너는 f@uck"
)
`when`("욕설이 존재하면") {
val allMatch = badWords.stream()
.allMatch { ProfanityChecker.checkProfanity(it) }
then("true를 반환한다.") {
allMatch shouldBe true
}
}
`when`("욕설이 존재하면") {
val shouldThrow1 = shouldThrow<IllegalArgumentException> { ProfanityChecker.validateContent(badWords[0]) }
val shouldThrow2 = shouldThrow<IllegalArgumentException> { ProfanityChecker.validateContent(badWords[1]) }
val shouldThrow3 = shouldThrow<IllegalArgumentException> { ProfanityChecker.validateContent(badWords[2]) }
val shouldThrow4 = shouldThrow<IllegalArgumentException> { ProfanityChecker.validateContent(badWords[3]) }
then("에외를 발생시킨다.") {
shouldThrow1 shouldHaveMessage "비속어가 포함되어 있습니다."
shouldThrow2 shouldHaveMessage "비속어가 포함되어 있습니다."
shouldThrow3 shouldHaveMessage "비속어가 포함되어 있습니다."
shouldThrow4 shouldHaveMessage "비속어가 포함되어 있습니다."
}
}
}

})
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.wespot.common.service

import com.wespot.common.dto.CheckProfanityRequest
import io.kotest.assertions.throwables.shouldNotThrow
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.matchers.throwable.shouldHaveMessage
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired

class CheckProfanityServiceTest @Autowired constructor(
private val checkProfanityService: CheckProfanityService
) : ServiceTest() {

@Test
fun `욕설이 아닌 값이 입력되면 예외를 반환하지 않는다`() {
// given
val validWord = "안녕"
val checkProfanityRequest = CheckProfanityRequest(validWord)

// when then
shouldNotThrow<IllegalArgumentException> { checkProfanityService.checkProfanity(checkProfanityRequest) }
}

@Test
fun `욕설이 입력되면 예외를 반환한다`() {
// given
val badWord = "너는 ㅅ_____________112231ㅂ"
val checkProfanityRequest = CheckProfanityRequest(badWord)

// when
val shouldThrow =
shouldThrow<IllegalArgumentException> { checkProfanityService.checkProfanity(checkProfanityRequest) }

// then
shouldThrow shouldHaveMessage "비속어가 포함되어 있습니다."
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.wespot.message.domain

import com.wespot.message.MessageContent
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.throwable.shouldHaveMessage

class MessageContentTest : BehaviorSpec({

given("메시지 컨텐츠에") {
val badWordsContent = "ㅂㅁㄴ이;라ㅓ 싮ㅂㅅㅂㅅㅂㅅㅂ시ㅂ 메시지"
val emptyContent = ""
val validContent = "헬로우"
`when`("욕설이 포함되어 있는 경우") {
val shouldThrow = shouldThrow<IllegalArgumentException> { MessageContent.from(badWordsContent) }
then("예외가 발생한다.") {
shouldThrow shouldHaveMessage "메시지의 내용에 비속어가 포함되어 있습니다."
}
}
`when`("아무런 내용이 없는 경우") {
val shouldThrow = shouldThrow<IllegalArgumentException> { MessageContent.from(emptyContent) }
then("예외가 발생한다.") {
shouldThrow shouldHaveMessage "메시지의 내용은 필수로 존재해야합니다."
}
}
`when`("정상적인 값이 입력되는 경우") {
val messageContent = MessageContent.from(validContent)
then("예외가 발생하지 않는다.") {
messageContent.content shouldBe validContent
}
}
}

})
53 changes: 53 additions & 0 deletions app/src/test/kotlin/com/wespot/message/domain/MessageTest.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,33 @@
package com.wespot.message.domain

import com.wespot.message.Message
import com.wespot.message.MessageTimeValidator
import com.wespot.message.fixture.MessageFixture
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.throwable.shouldHaveMessage
import io.mockk.clearAllMocks
import io.mockk.every
import io.mockk.mockkStatic
import io.mockk.unmockkStatic
import java.time.Clock
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneId

class MessageTest : BehaviorSpec({

beforeContainer {
val fixedClock = Clock.fixed(Instant.parse("2023-03-18T18:00:00Z"), ZoneId.of("UTC"))
MessageTimeValidator.setClock(fixedClock)
}

afterContainer {
clearAllMocks()
MessageTimeValidator.resetClock()
}

given("메시지를") {
val message = MessageFixture.createWithIdAndSenderIdAndReceiverId(1, 1, 2)

Expand All @@ -36,4 +53,40 @@ class MessageTest : BehaviorSpec({
}
}

given("메시지 컨텐츠에") {
mockkStatic(LocalDateTime::class)
every { LocalDateTime.now() } returns LocalDateTime.of(2024, 8, 9, 19, 0)
val badWordsContent = "ㅂㅁㄴ이;라ㅓ 싮ㅂㅅㅂㅅㅂㅅㅂ시ㅂ 메시지"
val emptyContent = ""
`when`("욕설이 포함되어 있는 경우") {
val shouldThrow = shouldThrow<IllegalArgumentException> {
Message.sendMessage(
badWordsContent,
1,
2,
"senderName",
false
)
}
then("예외가 발생한다.") {
shouldThrow shouldHaveMessage "메시지의 내용에 비속어가 포함되어 있습니다."
}
}
`when`("아무런 내용이 없는 경우") {
val shouldThrow = shouldThrow<IllegalArgumentException> {
Message.sendMessage(
emptyContent,
1,
2,
"senderName",
false
)
}
then("예외가 발생한다.") {
shouldThrow shouldHaveMessage "메시지의 내용은 필수로 존재해야합니다."
}
}
unmockkStatic(LocalDateTime::class)
}

})
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.wespot.message.fixture

import com.wespot.message.Message
import com.wespot.message.MessageContent
import com.wespot.message.MessageType
import java.time.LocalDateTime

Expand All @@ -9,7 +10,7 @@ object MessageFixture {

fun createWithId(id: Long) = Message(
id = id,
content = "content",
content = MessageContent.from("content"),
senderId = 1,
senderName = "senderName",
receiverId = 2,
Expand All @@ -31,7 +32,7 @@ object MessageFixture {

fun createWithIdAndSenderIdAndReceiverId(id: Long, senderId: Long, receiverId: Long) = Message(
id = id,
content = "content",
content = MessageContent.from("content"),
senderId = senderId,
senderName = "senderName",
receiverId = receiverId,
Expand Down Expand Up @@ -59,7 +60,7 @@ object MessageFixture {
): Message {
return Message(
id = 0,
content = content,
content = MessageContent.from(content),
senderId = senderId,
senderName = senderName,
receiverId = receiverId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.wespot.message.service

import com.wespot.auth.service.SecurityUtils
import com.wespot.message.Message
import com.wespot.message.MessageContent
import com.wespot.message.MessageTimeValidator
import com.wespot.message.dto.request.UpdateMessageRequest
import com.wespot.message.dto.response.UpdateMessageResponse
Expand Down Expand Up @@ -68,7 +69,7 @@ class ModifyMessageServiceTest : BehaviorSpec({
every { userPort.findById(receiver.id) } returns receiver
every { messagePort.findById(message.id) } returns message
every { SecurityUtils.getLoginUser(userPort) } returns sender
every { messagePort.save(any()) } returns message.copy(content = updateMessageRequest.content)
every { messagePort.save(any()) } returns message.copy(content = MessageContent.from(updateMessageRequest.content))

then("메시지가 올바르게 업데이트되어야 한다") {
val response = modifyMessageService.updateMessage(message.id, updateMessageRequest)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class SendMessageServiceTest : BehaviorSpec({
every { messagePort.save(any()) } returns message
every { messagePort.sendMessageCount(sender.id) } returns 0
every { messagePort.hasSentMessageToday(sender.id, receiver.id) } returns false
every { eventPublisher.publishEvent(MessageLimitEvent(0, 0)) } returns Unit
every { eventPublisher.publishEvent(MessageLimitEvent(sender.id, 0)) } returns Unit
every { eventPublisher.publishEvent(ReceivedMessageEvent(receiver, 0)) } returns Unit
every { blockedUserPort.existsByBlockerIdAndBlockedId(1, 2) } returns false
every { blockedUserPort.existsByBlockerIdAndBlockedId(2, 1) } returns false
Expand Down
Loading

0 comments on commit 71ea9e6

Please sign in to comment.