Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[KAN-108] 리뷰 테스트 코드 (테스트 커버리지 100%) #83

Merged
merged 2 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 2 additions & 22 deletions src/main/kotlin/com/restaurant/be/review/domain/entity/Review.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ import com.restaurant.be.common.entity.BaseEntity
import com.restaurant.be.common.exception.InvalidLikeCountException
import com.restaurant.be.review.domain.entity.QReview.review
import com.restaurant.be.review.presentation.dto.UpdateReviewRequest
import com.restaurant.be.review.presentation.dto.common.ReviewResponseDto
import com.restaurant.be.user.domain.entity.QUser.user
import com.restaurant.be.user.domain.entity.User
import kotlinx.serialization.json.JsonNull.content
import javax.persistence.CascadeType
import javax.persistence.Column
import javax.persistence.Entity
Expand Down Expand Up @@ -47,7 +44,6 @@ class Review(
@Column(name = "view_count", nullable = false)
var viewCount: Long = 0,

// 부모 (Review Entity)가 주인이되어 Image참조 가능. 반대는 불가능
@OneToMany(cascade = [CascadeType.ALL], orphanRemoval = true)
@JoinColumn(name = "review_id")
var images: MutableList<ReviewImage> = mutableListOf()
Expand All @@ -74,31 +70,15 @@ class Review(
fun incrementViewCount() {
this.viewCount++
}

fun incrementLikeCount() {
this.likeCount++
}

fun decrementLikeCount() {
if (this.likeCount == 0L) {
throw InvalidLikeCountException()
}
this.likeCount--
}

fun toResponseDTO(doesUserLike: Boolean): ReviewResponseDto {
return ReviewResponseDto(
id = id ?: 0,
userId = user.id ?: 0,
username = user.nickname,
profileImageUrl = user.profileImageUrl,
restaurantId = restaurantId,
rating = rating,
content = content,
imageUrls = images.map { it.imageUrl },
isLike = doesUserLike,
viewCount = viewCount,
likeCount = likeCount,
createdAt = createdAt,
modifiedAt = modifiedAt
)
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.restaurant.be.review.domain.service

import com.restaurant.be.common.exception.NotFoundRestaurantException
import com.restaurant.be.common.exception.NotFoundReviewException
import com.restaurant.be.common.exception.NotFoundUserEmailException
import com.restaurant.be.restaurant.repository.RestaurantRepository
import com.restaurant.be.review.domain.entity.ReviewImage
Expand All @@ -26,8 +25,7 @@ class CreateReviewService(
reviewRequest: ReviewRequestDto,
email: String
): CreateReviewResponse {
val user = userRepository.findByEmail(email)
?: throw NotFoundUserEmailException()
val user = userRepository.findByEmail(email) ?: throw NotFoundUserEmailException()

val review = reviewRequest.toEntity(user, restaurantId)

Expand All @@ -43,8 +41,7 @@ class CreateReviewService(

applyReviewCountAndAvgRating(restaurantId, reviewRequest.rating)

val reviewWithLikes = reviewRepository.findReview(user, review.id ?: 0)
?: throw NotFoundReviewException()
val reviewWithLikes = reviewRepository.findReview(user, review.id ?: 0)!!

val responseDto = ReviewResponseDto.toDto(
reviewWithLikes.review,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@ import org.springframework.transaction.annotation.Transactional
class GetReviewService(
private val userRepository: UserRepository,
private val reviewRepository: ReviewRepository

) {
@Transactional(readOnly = true)
fun getReviews(pageable: Pageable, restaurantId: Long, email: String): GetReviewsResponse {
val user = userRepository.findByEmail(email)
?: throw NotFoundUserEmailException()
val user = userRepository.findByEmail(email) ?: throw NotFoundUserEmailException()

val reviewsWithLikes = reviewRepository.findReviews(user, restaurantId, pageable)

Expand All @@ -37,8 +35,7 @@ class GetReviewService(

@Transactional
fun getReview(reviewId: Long, email: String): GetReviewResponse {
val user = userRepository.findByEmail(email)
?: throw NotFoundUserEmailException()
val user = userRepository.findByEmail(email) ?: throw NotFoundUserEmailException()

val reviewWithLikes = reviewRepository.findReview(user, reviewId)
?: throw NotFoundReviewException()
Expand All @@ -57,8 +54,7 @@ class GetReviewService(

@Transactional(readOnly = true)
fun getMyReviews(pageable: Pageable, email: String): GetMyReviewsResponse {
val user = userRepository.findByEmail(email)
?: throw NotFoundUserEmailException()
val user = userRepository.findByEmail(email) ?: throw NotFoundUserEmailException()

val reviewsWithLikes = reviewRepository.findMyReviews(user, pageable)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,37 @@ import com.restaurant.be.common.exception.DuplicateLikeException
import com.restaurant.be.common.exception.NotFoundLikeException
import com.restaurant.be.common.exception.NotFoundReviewException
import com.restaurant.be.common.exception.NotFoundUserEmailException
import com.restaurant.be.common.exception.NotFoundUserIdException
import com.restaurant.be.review.domain.entity.QReview.review
import com.restaurant.be.review.presentation.dto.LikeReviewRequest
import com.restaurant.be.review.presentation.dto.LikeReviewResponse
import com.restaurant.be.review.presentation.dto.ReviewWithLikesDto
import com.restaurant.be.review.presentation.dto.common.ReviewResponseDto
import com.restaurant.be.review.repository.ReviewLikesRepository
import com.restaurant.be.review.repository.ReviewLikeRepository
import com.restaurant.be.review.repository.ReviewRepository
import com.restaurant.be.user.domain.entity.User
import com.restaurant.be.user.repository.UserRepository
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
class LikeReviewService(
val userRepository: UserRepository,
val reviewLikesRepository: ReviewLikesRepository,
val reviewLikeRepository: ReviewLikeRepository,
val reviewRepository: ReviewRepository
) {
@Transactional
fun likeReview(reviewId: Long, request: LikeReviewRequest, email: String): LikeReviewResponse {
val user = userRepository.findByEmail(email)
?: throw NotFoundUserEmailException()
val user = userRepository.findByEmail(email) ?: throw NotFoundUserEmailException()

val userId = user.id ?: throw NotFoundUserIdException()
val userId = user.id ?: 0L

likeReviewWhetherAlreadyLikeOrNot(request, reviewId, user, userId)

val reviewWithLikes: ReviewWithLikesDto? = reviewRepository.findReview(user, reviewId)
val reviewWithLikes: ReviewWithLikesDto = reviewRepository.findReview(user, reviewId)
?: throw NotFoundReviewException()

val responseDto = ReviewResponseDto.toDto(
reviewWithLikes!!.review,
reviewWithLikes.review,
reviewWithLikes.isLikedByUser
)

Expand All @@ -53,19 +51,19 @@ class LikeReviewService(
if (isAlreadyLike(reviewId, user)) {
throw DuplicateLikeException()
}
reviewLikesRepository.save(request.toEntity(userId, reviewId))
val review = reviewRepository.findById(reviewId)
review.get().incrementLikeCount()
reviewLikeRepository.save(request.toEntity(userId, reviewId))
val review = reviewRepository.findByIdOrNull(reviewId)
review?.incrementLikeCount()
} else {
if (!isAlreadyLike(reviewId, user)) {
throw NotFoundLikeException()
}
reviewLikesRepository.deleteByReviewIdAndUserId(reviewId, userId)
val review = reviewRepository.findById(reviewId)
review.get().decrementLikeCount()
reviewLikeRepository.deleteByReviewIdAndUserId(reviewId, userId)
val review = reviewRepository.findByIdOrNull(reviewId)
review?.decrementLikeCount()
}
}

private fun isAlreadyLike(reviewId: Long, user: User) =
reviewLikesRepository.existsByReviewIdAndUserId(reviewId, user.id)
reviewLikeRepository.existsByReviewIdAndUserId(reviewId, user.id)
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package com.restaurant.be.review.domain.service

import com.restaurant.be.common.exception.NotFoundRestaurantException
import com.restaurant.be.common.exception.NotFoundReviewException
import com.restaurant.be.common.exception.NotFoundUserEmailException
import com.restaurant.be.common.exception.UnAuthorizedUpdateException
import com.restaurant.be.restaurant.repository.RestaurantRepository
import com.restaurant.be.review.domain.entity.QReview.review
import com.restaurant.be.review.presentation.dto.UpdateReviewRequest
import com.restaurant.be.review.presentation.dto.UpdateReviewResponse
import com.restaurant.be.review.presentation.dto.common.ReviewResponseDto
import com.restaurant.be.review.repository.ReviewLikeRepository
import com.restaurant.be.review.repository.ReviewRepository
import com.restaurant.be.user.repository.UserRepository
import org.springframework.stereotype.Service
Expand All @@ -19,37 +18,50 @@ import kotlin.jvm.optionals.getOrNull
class UpdateReviewService(
private val reviewRepository: ReviewRepository,
private val userRepository: UserRepository,
private val restaurantRepository: RestaurantRepository
private val restaurantRepository: RestaurantRepository,
private val reviewLikeRepository: ReviewLikeRepository
) {
@Transactional
fun updateReview(restaurantId: Long, reviewId: Long, reviewRequest: UpdateReviewRequest, email: String): UpdateReviewResponse {
val user = userRepository.findByEmail(email)
?: throw NotFoundUserEmailException()
fun updateReview(
restaurantId: Long,
reviewId: Long,
reviewRequest: UpdateReviewRequest,
email: String
): UpdateReviewResponse {
val user = userRepository.findByEmail(email) ?: throw NotFoundUserEmailException()

val review = reviewRepository.findById(reviewId)
.getOrNull()
?: throw NotFoundReviewException()
.orElseThrow { NotFoundReviewException() }

if (user.id != review.user.id) throw UnAuthorizedUpdateException()

applyReviewCountAndAvgRating(review.restaurantId, review.rating, reviewRequest.review.rating)
applyReviewCountAndAvgRating(
review.restaurantId,
review.rating,
reviewRequest.review.rating
)

review.updateReview(reviewRequest)

val reviewWithLikes = reviewRepository.findReview(user, reviewId)
?: throw NotFoundReviewException()
reviewRepository.save(review)

val responseDto = ReviewResponseDto.toDto(
reviewWithLikes.review,
reviewWithLikes.isLikedByUser
review,
reviewLikeRepository.existsByReviewIdAndUserId(reviewId, user.id)
)

return UpdateReviewResponse(responseDto)
}

private fun applyReviewCountAndAvgRating(restaurantId: Long, rating: Double, updateRating: Double) {
private fun applyReviewCountAndAvgRating(
restaurantId: Long,
rating: Double,
updateRating: Double
) {
val restaurant = restaurantRepository.findById(restaurantId).getOrNull()
?: throw NotFoundRestaurantException()
restaurant.updateReview(rating, updateRating)
if (restaurant != null) {
restaurant.updateReview(rating, updateRating)
restaurantRepository.save(restaurant)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
@file:Suppress("ktlint", "MatchingDeclarationName")

package com.restaurant.be.review.presentation.dto

import com.restaurant.be.review.presentation.dto.common.ReviewRequestDto
import com.restaurant.be.review.presentation.dto.common.ReviewResponseDto
import io.swagger.annotations.ApiModelProperty

data class CreateReviewRequest(
@ApiModelProperty(value = "리뷰 정보", required = true)
val review: ReviewRequestDto
)

data class CreateReviewResponse(
@ApiModelProperty(value = "리뷰 정보", required = true)
val review: ReviewResponseDto
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ data class ReviewResponseDto(
val modifiedAt: LocalDateTime
) {
companion object {
fun toDto(review: Review, isLikedByUser: Boolean? = null): ReviewResponseDto {
fun toDto(review: Review, isLikedByUser: Boolean): ReviewResponseDto {
return ReviewResponseDto(
id = review.id ?: 0,
userId = review.user.id ?: 0,
Expand All @@ -69,7 +69,7 @@ data class ReviewResponseDto(
rating = review.rating,
content = review.content,
imageUrls = review.images.map { it.imageUrl },
isLike = isLikedByUser ?: false,
isLike = isLikedByUser,
likeCount = review.likeCount,
viewCount = review.viewCount,
createdAt = review.createdAt,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository

@Repository
interface ReviewLikesRepository : JpaRepository<ReviewLike, Long> {
interface ReviewLikeRepository : JpaRepository<ReviewLike, Long> {
fun existsByReviewIdAndUserId(reviewId: Long?, userId: Long?): Boolean
fun deleteByReviewIdAndUserId(reviewId: Long?, userId: Long?)
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,16 @@ class ReviewRepositoryCustomImpl(
.and(reviewLike.userId.eq(user.id))
)
.where(review.id.eq(reviewId))
.fetchJoin()
.fetchOne()
}

override fun findReviews(user: User, restaurantId: Long, pageable: Pageable): Page<ReviewWithLikesDto> {
val orderSpecifier = setOrderSpecifier(pageable)
val orderSpecifier = if (!pageable.sort.isEmpty) {
setOrderSpecifier(pageable)
} else {
val reviewPath = PathBuilderFactory().create(Review::class.java)
listOf(reviewPath.getNumber("id", Long::class.java).desc())
}

val reviewsWithLikes = queryFactory
.select(
Expand Down Expand Up @@ -72,7 +76,7 @@ class ReviewRepositoryCustomImpl(
}

override fun findMyReviews(user: User, pageable: Pageable): Page<ReviewWithLikesDto> {
val orderSpecifier = if (pageable.sort.isSorted) {
val orderSpecifier = if (!pageable.sort.isEmpty) {
setOrderSpecifier(pageable)
} else {
val reviewPath = PathBuilderFactory().create(Review::class.java)
Expand Down
29 changes: 29 additions & 0 deletions src/test/kotlin/com/restaurant/be/common/util/ReviewUtil.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.restaurant.be.common.util

import com.restaurant.be.review.domain.entity.Review
import com.restaurant.be.review.domain.entity.ReviewImage
import com.restaurant.be.user.domain.entity.User

object ReviewUtil {
fun generateReviewEntity(
id: Long = 0,
user: User,
restaurantId: Long = 0,
content: String = "default_content",
rating: Double = 0.0,
likeCount: Long = 0,
viewCount: Long = 0,
images: MutableList<ReviewImage> = mutableListOf()
): Review {
return Review(
id = id,
user = user,
restaurantId = restaurantId,
content = content,
rating = rating,
likeCount = likeCount,
viewCount = viewCount,
images = images
)
}
}
Loading
Loading