Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import io.github.tyostokarry.eventshuffle.dto.EventCreateResponse
import io.github.tyostokarry.eventshuffle.dto.EventDetailsResponse
import io.github.tyostokarry.eventshuffle.dto.EventListItemDto
import io.github.tyostokarry.eventshuffle.dto.EventListResponse
import io.github.tyostokarry.eventshuffle.dto.EventResultResponse
import io.github.tyostokarry.eventshuffle.dto.VoteCreateRequest
import io.github.tyostokarry.eventshuffle.entity.Event
import io.github.tyostokarry.eventshuffle.service.EventService
Expand Down Expand Up @@ -57,4 +58,13 @@ class EventController(
val response = EventDetailsResponse.fromEvent(event)
return ResponseEntity.ok(response)
}

@GetMapping("/{id}/results")
fun getEventResults(
@PathVariable id: Long,
): ResponseEntity<EventResultResponse> {
val event = eventService.getEventById(id)
val response = EventResultResponse.fromEvent(event)
return ResponseEntity.ok(response)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package io.github.tyostokarry.eventshuffle.dto

import io.github.tyostokarry.eventshuffle.entity.Event

data class EventResultResponse(
val id: Long,
val name: String,
val suitableDates: List<VotesListItemDto>,
) {
companion object {
fun fromEvent(event: Event): EventResultResponse {
if (event.votes.isEmpty()) {
return EventResultResponse(
id = event.id,
name = event.name,
suitableDates = emptyList(),
)
}

val allVoters = event.votes.map { it.voterName }.toSet()

val suitableDates =
event.dates
.filter { date ->
event.votes.all {
date in it.votedDates
}
}.map { date ->
VotesListItemDto(
date = date,
people = allVoters.toList(),
)
}

return EventResultResponse(
id = event.id,
name = event.name,
suitableDates = suitableDates,
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,15 @@ class EventService(
// If participant has voted previously, remove old vote
event.votes.removeIf { it.voterName.equals(request.name, ignoreCase = true) }

val newVote =
EventVote(
voterName = request.name,
votedDates = request.votes,
event = event,
)
event.votes.add(newVote)
if (request.votes.isNotEmpty()) {
val newVote =
EventVote(
voterName = request.name,
votedDates = request.votes,
event = event,
)
event.votes.add(newVote)
}

val saved = eventRepository.save(event)
return saved
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import io.github.tyostokarry.eventshuffle.dto.EventCreateRequest
import io.github.tyostokarry.eventshuffle.dto.EventCreateResponse
import io.github.tyostokarry.eventshuffle.dto.EventDetailsResponse
import io.github.tyostokarry.eventshuffle.dto.EventListResponse
import io.github.tyostokarry.eventshuffle.dto.EventResultResponse
import io.github.tyostokarry.eventshuffle.dto.VoteCreateRequest
import io.github.tyostokarry.eventshuffle.entity.Event
import io.github.tyostokarry.eventshuffle.entity.EventVote
Expand Down Expand Up @@ -389,4 +390,164 @@ class EventControllerTest(
)
}
}

@Nested
inner class GetResult {
@Test
fun `get event results returns aggregated vote summary`() {
val event =
Event(
id = 1L,
name = "Test Event",
dates =
listOf(
LocalDate.of(2025, 10, 5),
LocalDate.of(2025, 10, 6),
),
)

event.votes.addAll(
listOf(
EventVote(
id = 1L,
voterName = "Test Name 1",
votedDates = listOf(LocalDate.of(2025, 10, 5)),
event = event,
),
EventVote(
id = 2L,
voterName = "Test Name 2",
votedDates =
listOf(
LocalDate.of(2025, 10, 5),
LocalDate.of(2025, 10, 6),
),
event = event,
),
),
)

given(eventService.getEventById(1L)).willReturn(event)

val responseBody =
mockMvc
.get("/api/v1/event/1/results")
.andExpect { status { isOk() } }
.andReturn()
.response
.contentAsString

val response = objectMapper.readValue(responseBody, EventResultResponse::class.java)

assertEquals(1, response.id, "Response event id should be 1")
assertEquals("Test Event", response.name, "Response event name should be 'Test Event'")
assertEquals(1, response.suitableDates.size, "Response event should have exactly 1 suitable date")
assertEquals(
LocalDate.of(2025, 10, 5),
response.suitableDates.first().date,
"Response events suitable date should be '2025-10-05'",
)
assertEquals(
listOf("Test Name 1", "Test Name 2"),
response.suitableDates.first().people,
"Response events suitable date voters should be 'Test Name 1', 'Test Name 2'",
)
}

@Test
fun `get event results returns empty suitable dates list when no votes are recorded`() {
val event =
Event(
id = 1L,
name = "Test Event",
dates =
listOf(
LocalDate.of(2025, 10, 5),
LocalDate.of(2025, 10, 6),
),
)

given(eventService.getEventById(1L)).willReturn(event)

val responseBody =
mockMvc
.get("/api/v1/event/1/results")
.andExpect { status { isOk() } }
.andReturn()
.response
.contentAsString

val response = objectMapper.readValue(responseBody, EventResultResponse::class.java)

assertEquals(1, response.id, "Response event id should be 1")
assertEquals("Test Event", response.name, "Response event name should be 'Test Event'")
assertTrue(response.suitableDates.isEmpty(), "Response event should have no suitable dates")
}

@Test
fun `get event results returns empty suitable dates list when no date has everyone's vote`() {
val event =
Event(
id = 1L,
name = "Test Event",
dates =
listOf(
LocalDate.of(2025, 10, 5),
LocalDate.of(2025, 10, 6),
),
)

event.votes.addAll(
listOf(
EventVote(
id = 1L,
voterName = "Test Name 1",
votedDates = listOf(LocalDate.of(2025, 10, 5)),
event = event,
),
EventVote(
id = 2L,
voterName = "Test Name 2",
votedDates = listOf(LocalDate.of(2025, 10, 6)),
event = event,
),
),
)

given(eventService.getEventById(1L)).willReturn(event)

val responseBody =
mockMvc
.get("/api/v1/event/1/results")
.andExpect { status { isOk() } }
.andReturn()
.response
.contentAsString

val response = objectMapper.readValue(responseBody, EventResultResponse::class.java)

assertEquals(1, response.id, "Response event id should be 1")
assertEquals("Test Event", response.name, "Response event name should be 'Test Event'")
assertTrue(response.suitableDates.isEmpty(), "Response event should have no suitable dates")
}

@Test
fun `get event results returns 404 when event not found`() {
given(eventService.getEventById(999L)).willThrow(EventNotFoundException(999L))

val responseBody =
mockMvc
.get("/api/v1/event/999/results")
.andExpect { status { isNotFound() } }
.andReturn()
.response
.contentAsString

assertEquals(
"Event with ID 999 not found",
responseBody,
"Error body should contain an indication of invalid event id.",
)
}
}
}