Skip to content

Commit

Permalink
Merge pull request #10 from Nexters/feature/3-4-snow-maker-api
Browse files Browse the repository at this point in the history
[#3 #4]
  • Loading branch information
jun108059 authored Oct 3, 2024
2 parents e154f07 + 589af14 commit cdc4ab8
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 0 deletions.
20 changes: 20 additions & 0 deletions src/main/kotlin/nexters/weski/snow_maker/SnowMakerController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package nexters.weski.snow_maker

import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping("/api/snow-maker")

class SnowMakerController(
private val snowMakerService: SnowMakerService
) {
@GetMapping("/{resortId}")
fun getSnowMaker(@PathVariable resortId: Long): SnowMakerDto {
return snowMakerService.getSnowMaker(resortId)
}

@PostMapping("/{resortId}/vote")
fun voteSnowMaker(@PathVariable resortId: Long, @RequestParam isPositive: Boolean) {
snowMakerService.voteSnowMaker(resortId, isPositive)
}
}
8 changes: 8 additions & 0 deletions src/main/kotlin/nexters/weski/snow_maker/SnowMakerDto.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package nexters.weski.snow_maker

data class SnowMakerDto(
val resortId: Long,
val totalVotes: Long,
val positiveVotes: Long,
val status: String,
)
42 changes: 42 additions & 0 deletions src/main/kotlin/nexters/weski/snow_maker/SnowMakerService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package nexters.weski.snow_maker

import nexters.weski.ski_resort.SkiResortRepository
import org.springframework.stereotype.Service

@Service
class SnowMakerService(
private val snowMakerVoteRepository: SnowMakerVoteRepository,
private val skiResortRepository: SkiResortRepository
) {
fun getSnowMaker(resortId: Long): SnowMakerDto {
val totalVotes = snowMakerVoteRepository.countBySkiResortResortId(resortId)
val positiveVotes = snowMakerVoteRepository.countBySkiResortResortIdAndIsPositive(resortId, true)
val status = calculateStatus(totalVotes, positiveVotes)

return SnowMakerDto(
resortId = resortId,
totalVotes = totalVotes,
positiveVotes = positiveVotes,
status = status
)
}

fun voteSnowMaker(resortId: Long, isPositive: Boolean) {
val skiResort = skiResortRepository.findById(resortId).orElseThrow { Exception("Resort not found") }
val vote = SnowMakerVote(
isPositive = isPositive,
skiResort = skiResort
)
snowMakerVoteRepository.save(vote)
}

private fun calculateStatus(totalVotes: Long, positiveVotes: Long): String {
if (totalVotes == 0L) return "정보 없음"
val positiveRate = (positiveVotes.toDouble() / totalVotes.toDouble()) * 100
return when {
positiveRate >= 80 -> "좋음"
positiveRate >= 50 -> "나쁘지 않음"
else -> "좋지 않음"
}
}
}
22 changes: 22 additions & 0 deletions src/main/kotlin/nexters/weski/snow_maker/SnowMakerVote.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package nexters.weski.snow_maker


import jakarta.persistence.*
import nexters.weski.common.BaseEntity
import nexters.weski.ski_resort.SkiResort
import java.time.LocalDateTime

@Entity
@Table(name = "snow_quality_votes")
data class SnowMakerVote(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0,

val isPositive: Boolean,
val votedAt: LocalDateTime = LocalDateTime.now(),

@ManyToOne
@JoinColumn(name = "resort_id")
val skiResort: SkiResort
) : BaseEntity()
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package nexters.weski.snow_maker

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

interface SnowMakerVoteRepository : JpaRepository<SnowMakerVote, Long> {
fun countBySkiResortResortId(resortId: Long): Long
fun countBySkiResortResortIdAndIsPositive(resortId: Long, isPositive: Boolean): Long
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package nexters.weski.snow_maker


import com.ninjasquad.springmockk.MockkBean
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
import nexters.weski.common.config.JpaConfig
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.FilterType
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.*

@WebMvcTest(SnowMakerController::class)
@ComponentScan(
excludeFilters = [ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
classes = [JpaConfig::class]
)]
)
class SnowMakerControllerTest @Autowired constructor(
private val mockMvc: MockMvc
) {

@MockkBean
lateinit var snowMakerService: SnowMakerService

@Test
fun `GET api_snow-maker_resortId should return snow Maker data`() {
// Given
val resortId = 1L
val snowMakerDto = SnowMakerDto(resortId, 100, 80, "좋음")
every { snowMakerService.getSnowMaker(resortId) } returns snowMakerDto

// When & Then
mockMvc.perform(get("/api/snow-maker/$resortId"))
.andExpect(status().isOk)
.andExpect(jsonPath("$.totalVotes").value(100))
.andExpect(jsonPath("$.status").value("좋음"))
}

@Test
fun `POST api_snow-maker_resortId_vote should vote successfully`() {
// Given
val resortId = 1L
every { snowMakerService.voteSnowMaker(any(), any()) } just Runs

// When & Then
mockMvc.perform(post("/api/snow-maker/$resortId/vote")
.param("isPositive", "true"))
.andExpect(status().isOk)
}
}
53 changes: 53 additions & 0 deletions src/test/kotlin/nexters/weski/snow_maker/SnowMakerServiceTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package nexters.weski.snow_maker


import io.mockk.*
import nexters.weski.ski_resort.ResortStatus
import nexters.weski.ski_resort.SkiResort
import nexters.weski.ski_resort.SkiResortRepository
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
import java.util.Optional

class SnowMakerServiceTest {

private val snowMakerVoteRepository: SnowMakerVoteRepository = mockk(relaxed = true)
private val skiResortRepository: SkiResortRepository = mockk()
private val snowMakerService = SnowMakerService(snowMakerVoteRepository, skiResortRepository)

@Test
fun `getSnowMaker should return SnowMakerDto`() {
// Given
val resortId = 1L
every { snowMakerVoteRepository.countBySkiResortResortId(resortId) } returns 100
every { snowMakerVoteRepository.countBySkiResortResortIdAndIsPositive(resortId, true) } returns 80

// When
val result = snowMakerService.getSnowMaker(resortId)

// Then
assertEquals(100, result.totalVotes)
assertEquals(80, result.positiveVotes)
assertEquals("좋음", result.status)
}

@Test
fun `voteSnowMaker should save vote`() {
// Given
val resortId = 1L
val isPositive = true
val skiResort = SkiResort(resortId, "스키장 A", ResortStatus.운영중, null, null, 5, 10)
val snowMakerVote = SnowMakerVote(
isPositive = isPositive,
skiResort = skiResort
)
every { skiResortRepository.findById(resortId) } returns Optional.of(skiResort)
every { snowMakerVoteRepository.save(any()) } returns snowMakerVote

// When
snowMakerService.voteSnowMaker(resortId, isPositive)

// Then
verify { snowMakerVoteRepository.save(any()) }
}
}

0 comments on commit cdc4ab8

Please sign in to comment.