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

feature: 공지사항 조회 #20

Merged
merged 9 commits into from
Jan 27, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.petqua.application.announcement

import com.petqua.domain.announcement.Announcement

data class AnnouncementResponse(
val id: Long,
val title: String,
val linkUrl: String,
) {
companion object {
fun from(announcement: Announcement): AnnouncementResponse {
return AnnouncementResponse(
id = announcement.id,
title = announcement.title,
linkUrl = announcement.linkUrl,
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.petqua.application.announcement

import com.petqua.domain.announcement.AnnouncementRepository
import org.springframework.cache.annotation.Cacheable
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Transactional
@Service
class AnnouncementService(
private val announcementRepository: AnnouncementRepository,
) {

@Cacheable("announcements")
@Transactional(readOnly = true)
fun readAll(): List<AnnouncementResponse> {
val announcements = announcementRepository.findAll()
return announcements.map { AnnouncementResponse.from(it) }
}
}
Comment on lines +8 to +20
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

깔끔하네요~~

Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class CacheConfiguration {
@Bean
fun cacheManager(): CacheManager {
val cacheManager = ConcurrentMapCacheManager()
cacheManager.setCacheNames(listOf("banners"))
cacheManager.setCacheNames(listOf("banners", "announcements"))
return cacheManager
}
}
20 changes: 20 additions & 0 deletions src/main/kotlin/com/petqua/domain/announcement/Announcement.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.petqua.domain.announcement

import com.petqua.common.domain.BaseEntity
import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id

@Entity
class Announcement(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0L,

@Column(nullable = false)
val title: String,

@Column(nullable = false)
val linkUrl: String,
) : BaseEntity()
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.petqua.domain.announcement

import org.springframework.data.jpa.repository.JpaRepository

interface AnnouncementRepository : JpaRepository<Announcement, Long>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.petqua.presentation.announcement

import com.petqua.application.announcement.AnnouncementResponse
import com.petqua.application.announcement.AnnouncementService
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RequestMapping("/announcements")
@RestController
class AnnouncementController(
private val announcementService: AnnouncementService
) {

@GetMapping
fun readAll(): ResponseEntity<List<AnnouncementResponse>> {
val response = announcementService.readAll()
return ResponseEntity.ok(response)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.petqua.application.announcement

import com.petqua.domain.announcement.Announcement
import com.petqua.domain.announcement.AnnouncementRepository
import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.matchers.shouldBe
import org.mockito.Mockito.atMost
import org.mockito.Mockito.verify
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment.NONE
import org.springframework.boot.test.mock.mockito.SpyBean
import org.springframework.test.context.TestConstructor
import org.springframework.test.context.TestConstructor.AutowireMode.ALL

@TestConstructor(autowireMode = ALL)
@SpringBootTest(webEnvironment = NONE)
class AnnouncementServiceTest(
private var announcementService: AnnouncementService,
@SpyBean private var announcementRepository: AnnouncementRepository,
) : BehaviorSpec({

Given("공지사항 조회 테스트") {
announcementRepository.saveAll(
listOf(
Announcement(title = "titleA", linkUrl = "linkUrlA"),
Announcement(title = "titleB", linkUrl = "linkUrlB"),
Announcement(title = "titleC", linkUrl = "linkUrlC"),
)
)

When("공지사항을 전체 조회 하면") {
val results = announcementService.readAll()

Then("모든 공지사항이 조회 된다") {
results.size shouldBe 3
}
}

When("공지사항이 캐싱 되어 있으면") {
repeat(5) { announcementService.readAll() }

Then("퀴리가 발생 하지 않는다") {
verify(announcementRepository, atMost(1)).findAll()
}
}
}
})
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.petqua.application
package com.petqua.application.product

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

헉쓰 놓쳤네요 이동 감사합니다

import com.petqua.application.product.ProductService
import com.petqua.application.product.dto.ProductDetailResponse
import com.petqua.application.product.dto.ProductReadRequest
import com.petqua.application.product.dto.ProductsResponse
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.petqua.presentation.announcement

import com.petqua.application.announcement.AnnouncementResponse
import com.petqua.domain.announcement.Announcement
import com.petqua.domain.announcement.AnnouncementRepository
import com.petqua.test.ApiTestConfig
import io.restassured.module.kotlin.extensions.Extract
import io.restassured.module.kotlin.extensions.Given
import io.restassured.module.kotlin.extensions.Then
import io.restassured.module.kotlin.extensions.When
import org.assertj.core.api.SoftAssertions.assertSoftly
import org.springframework.http.HttpStatus

class AnnouncementControllerTest(
private val announcementRepository: AnnouncementRepository
) : ApiTestConfig() {

init {
Given("공지 사항이 존재할 때") {
val announcements = announcementRepository.saveAll(
listOf(
Announcement(title = "announcementsA", linkUrl = "linkUrlA"),
Announcement(title = "announcementsB", linkUrl = "linkUrlB")
)
)

When("공지 사항 목록을 조회하면") {
val response = Given {
log().all()
} When {
get("/announcements")
} Then {
log().all()
} Extract {
response()
}

Then("모든 공지 사항 목록이 반환된다.") {
val responseData = response.`as`(Array<AnnouncementResponse>::class.java)

assertSoftly {
it.assertThat(response.statusCode).isEqualTo(HttpStatus.OK.value())
it.assertThat(responseData.size).isEqualTo(2)
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.petqua.presentation
package com.petqua.presentation.product

import com.petqua.application.product.dto.ProductDetailResponse
import com.petqua.application.product.dto.ProductsResponse
Expand All @@ -20,15 +20,15 @@ import io.restassured.module.kotlin.extensions.Extract
import io.restassured.module.kotlin.extensions.Given
import io.restassured.module.kotlin.extensions.Then
import io.restassured.module.kotlin.extensions.When
import java.math.BigDecimal
import kotlin.Long.Companion.MIN_VALUE
import org.assertj.core.api.SoftAssertions.assertSoftly
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT
import org.springframework.boot.test.web.server.LocalServerPort
import org.springframework.http.HttpStatus
import org.springframework.http.HttpStatus.BAD_REQUEST
import org.springframework.http.HttpStatus.NOT_FOUND
import java.math.BigDecimal
import kotlin.Long.Companion.MIN_VALUE

@SpringBootTest(webEnvironment = RANDOM_PORT)
class ProductControllerTest(
Expand Down
6 changes: 5 additions & 1 deletion src/test/kotlin/com/petqua/test/ApiTestConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@ import org.springframework.boot.test.web.server.LocalServerPort
abstract class ApiTestConfig : BehaviorSpec() {

@LocalServerPort
protected val port: Int = RestAssured.port
protected var port: Int = 0

@Autowired
private lateinit var dataCleaner: DataCleaner

init {
this.beforeTest {
RestAssured.port = this.port
}

Comment on lines +20 to +23
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오호 이전이랑 무슨 차이가 있나요??

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이전에는 port라는 변수에 값만 설정하고, port를 사용하진 않았었는데요
protected val port: Int = RestAssured.port

@SpringBooTest에서 RandomPort로 하니까 RestAssured.port 값이 이전과 동일해서 connection 안되더라고요.

@LocalServerPort로 SpringContext마다 할당된 randomPort를 RestAssured.port로 지정하였습니다!
Int 타입이라 Lateinit이 안되어서 기본값을 하나 두었어요!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

너모 멋져요...!

afterContainer {
dataCleaner.clean()
}
Expand Down
Loading