Merge branch 'develop' into feat/oauth2-user-service-26
This commit is contained in:
@@ -64,7 +64,7 @@ class FilmRepository(
|
|||||||
override fun findByTitle(title: String): Film? {
|
override fun findByTitle(title: String): Film? {
|
||||||
val films =
|
val films =
|
||||||
jdbc.query(
|
jdbc.query(
|
||||||
"SELECT id, title, description FROM films WHERE title = ?",
|
"SELECT id, title, description FROM films WHERE title = ? ORDER BY id LIMIT 1",
|
||||||
filmRowMapper,
|
filmRowMapper,
|
||||||
title,
|
title,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import com.project.movienight.application.ports.input.GetAllFilmsUseCase
|
|||||||
import com.project.movienight.application.ports.input.GetFilmByIdUseCase
|
import com.project.movienight.application.ports.input.GetFilmByIdUseCase
|
||||||
import com.project.movienight.application.ports.input.SearchFilmByTitleUseCase
|
import com.project.movienight.application.ports.input.SearchFilmByTitleUseCase
|
||||||
import org.springframework.http.HttpStatus
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.http.ResponseEntity
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping
|
import org.springframework.web.bind.annotation.DeleteMapping
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
import org.springframework.web.bind.annotation.PatchMapping
|
import org.springframework.web.bind.annotation.PatchMapping
|
||||||
@@ -75,11 +76,18 @@ class FilmController(
|
|||||||
@PathVariable id: UUID,
|
@PathVariable id: UUID,
|
||||||
): FilmResponse = FilmResponse.fromDomain(getFilmByIdUseCase.getById(id))
|
): FilmResponse = FilmResponse.fromDomain(getFilmByIdUseCase.getById(id))
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
fun getAll(): List<FilmResponse> = getAllFilmsUseCase.getAll().map { FilmResponse.fromDomain(it) }
|
||||||
|
|
||||||
@GetMapping("/search")
|
@GetMapping("/search")
|
||||||
fun searchByTitle(
|
fun searchByTitle(
|
||||||
@RequestParam title: String,
|
@RequestParam title: String,
|
||||||
): FilmResponse? = searchFilmByTitleUseCase.searchByTitle(title)?.let { FilmResponse.fromDomain(it) }
|
): ResponseEntity<FilmResponse> {
|
||||||
|
val film = searchFilmByTitleUseCase.searchByTitle(title)
|
||||||
@GetMapping
|
return if (film != null) {
|
||||||
fun getAll(): List<FilmResponse> = getAllFilmsUseCase.getAll().map { FilmResponse.fromDomain(it) }
|
ResponseEntity.ok(FilmResponse.fromDomain(film))
|
||||||
|
} else {
|
||||||
|
ResponseEntity.notFound().build()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import com.project.movienight.application.ports.input.GetFilmLibraryQuery
|
|||||||
import com.project.movienight.application.ports.input.GetFilmLibraryUseCase
|
import com.project.movienight.application.ports.input.GetFilmLibraryUseCase
|
||||||
import com.project.movienight.application.ports.input.RemoveFilmFromLibraryCommand
|
import com.project.movienight.application.ports.input.RemoveFilmFromLibraryCommand
|
||||||
import com.project.movienight.application.ports.input.RemoveFilmFromLibraryUseCase
|
import com.project.movienight.application.ports.input.RemoveFilmFromLibraryUseCase
|
||||||
|
import com.project.movienight.domain.exception.EntityNotFoundException
|
||||||
import org.springframework.http.HttpStatus
|
import org.springframework.http.HttpStatus
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping
|
import org.springframework.web.bind.annotation.DeleteMapping
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
@@ -67,9 +68,7 @@ class FilmLibraryController(
|
|||||||
getFilmLibraryUseCase.getLibrary(
|
getFilmLibraryUseCase.getLibrary(
|
||||||
GetFilmLibraryQuery(userId = userId),
|
GetFilmLibraryQuery(userId = userId),
|
||||||
)
|
)
|
||||||
|
|
||||||
val film = getFilmByIdUseCase.getById(library.filmId)
|
val film = getFilmByIdUseCase.getById(library.filmId)
|
||||||
|
|
||||||
return listOf(FilmResponse.fromDomain(film))
|
return listOf(FilmResponse.fromDomain(film))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,25 +92,38 @@ class FilmLibraryController(
|
|||||||
fun removeFilm(
|
fun removeFilm(
|
||||||
@PathVariable userId: UUID,
|
@PathVariable userId: UUID,
|
||||||
@PathVariable filmId: UUID,
|
@PathVariable filmId: UUID,
|
||||||
) = removeFilmFromLibraryUseCase.removeFilm(
|
) {
|
||||||
RemoveFilmFromLibraryCommand(
|
removeFilmFromLibraryUseCase.removeFilm(
|
||||||
userId = userId,
|
RemoveFilmFromLibraryCommand(
|
||||||
filmId = filmId,
|
userId = userId,
|
||||||
),
|
filmId = filmId,
|
||||||
)
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/available-films")
|
@GetMapping("/available-films")
|
||||||
fun getAvailableFilms(
|
fun getAvailableFilms(
|
||||||
@PathVariable userId: UUID,
|
@PathVariable userId: UUID,
|
||||||
): List<FilmResponse> {
|
): List<FilmResponse> {
|
||||||
val userLibrary =
|
val userLibrary =
|
||||||
getFilmLibraryUseCase.getLibrary(
|
runCatching {
|
||||||
GetFilmLibraryQuery(userId = userId),
|
getFilmLibraryUseCase.getLibrary(
|
||||||
)
|
GetFilmLibraryQuery(userId = userId),
|
||||||
|
)
|
||||||
|
}.onFailure { exception ->
|
||||||
|
if (exception !is EntityNotFoundException) {
|
||||||
|
throw exception
|
||||||
|
}
|
||||||
|
}.getOrNull()
|
||||||
|
|
||||||
val allFilms = getAllFilmsUseCase.getAll()
|
val allFilms = getAllFilmsUseCase.getAll()
|
||||||
|
|
||||||
val availableFilms = allFilms.filter { it.id != userLibrary.filmId }
|
val availableFilms =
|
||||||
|
if (userLibrary != null) {
|
||||||
|
allFilms.filter { it.id != userLibrary.filmId }
|
||||||
|
} else {
|
||||||
|
allFilms
|
||||||
|
}
|
||||||
|
|
||||||
return availableFilms.map { FilmResponse.fromDomain(it) }
|
return availableFilms.map { FilmResponse.fromDomain(it) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package com.project.movienight.adapters.web
|
||||||
|
|
||||||
|
import com.project.movienight.application.ports.input.CreateFilmUseCase
|
||||||
|
import com.project.movienight.application.ports.input.DeleteFilmUseCase
|
||||||
|
import com.project.movienight.application.ports.input.EditFilmUseCase
|
||||||
|
import com.project.movienight.application.ports.input.GetAllFilmsUseCase
|
||||||
|
import com.project.movienight.application.ports.input.GetFilmByIdUseCase
|
||||||
|
import com.project.movienight.application.ports.input.SearchFilmByTitleUseCase
|
||||||
|
import com.project.movienight.domain.model.Film
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.verify
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.springframework.test.web.servlet.MockMvc
|
||||||
|
import org.springframework.test.web.servlet.get
|
||||||
|
import org.springframework.test.web.servlet.setup.MockMvcBuilders
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
class FilmControllerSearchTest {
|
||||||
|
private lateinit var mockMvc: MockMvc
|
||||||
|
private lateinit var searchFilmByTitleUseCase: SearchFilmByTitleUseCase
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun setup() {
|
||||||
|
searchFilmByTitleUseCase = mockk()
|
||||||
|
|
||||||
|
val controller =
|
||||||
|
FilmController(
|
||||||
|
createFilmUseCase = mockk<CreateFilmUseCase>(),
|
||||||
|
editFilmUseCase = mockk<EditFilmUseCase>(),
|
||||||
|
deleteFilmUseCase = mockk<DeleteFilmUseCase>(),
|
||||||
|
getFilmByIdUseCase = mockk<GetFilmByIdUseCase>(),
|
||||||
|
getAllFilmsUseCase = mockk<GetAllFilmsUseCase>(),
|
||||||
|
searchFilmByTitleUseCase = searchFilmByTitleUseCase,
|
||||||
|
)
|
||||||
|
|
||||||
|
mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `search returns film when title exists`() {
|
||||||
|
val title = "Inception"
|
||||||
|
val film = Film(id = UUID.randomUUID(), title = title, description = "A dream heist")
|
||||||
|
|
||||||
|
every { searchFilmByTitleUseCase.searchByTitle(title) } returns film
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.get("/api/films/search") {
|
||||||
|
param("title", title)
|
||||||
|
}.andExpect {
|
||||||
|
status { isOk() }
|
||||||
|
jsonPath("$.id") { value(film.id.toString()) }
|
||||||
|
jsonPath("$.title") { value(title) }
|
||||||
|
jsonPath("$.description") { value("A dream heist") }
|
||||||
|
}
|
||||||
|
|
||||||
|
verify(exactly = 1) { searchFilmByTitleUseCase.searchByTitle(title) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `search returns 404 when title is not found`() {
|
||||||
|
val title = "Unknown Title"
|
||||||
|
|
||||||
|
every { searchFilmByTitleUseCase.searchByTitle(title) } returns null
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.get("/api/films/search") {
|
||||||
|
param("title", title)
|
||||||
|
}.andExpect {
|
||||||
|
status { isNotFound() }
|
||||||
|
content { string("") }
|
||||||
|
}
|
||||||
|
|
||||||
|
verify(exactly = 1) { searchFilmByTitleUseCase.searchByTitle(title) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
package com.project.movienight.controllers
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.project.movienight.adapters.web.dto.request.CreateFilmRequest
|
||||||
|
import com.project.movienight.adapters.web.dto.request.EditFilmRequest
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest
|
||||||
|
import org.springframework.http.MediaType
|
||||||
|
import org.springframework.test.web.servlet.MockMvc
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
|
||||||
|
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath
|
||||||
|
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
@AutoConfigureMockMvc
|
||||||
|
class FilmControllerTest {
|
||||||
|
@Autowired
|
||||||
|
private lateinit var mockMvc: MockMvc
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private lateinit var objectMapper: ObjectMapper
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `create film should return 201 CREATED`() {
|
||||||
|
val request =
|
||||||
|
CreateFilmRequest(
|
||||||
|
title = "The Matrix",
|
||||||
|
description = "A computer hacker learns about the true nature of reality",
|
||||||
|
)
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/api/films")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(request)),
|
||||||
|
).andExpect(status().isCreated)
|
||||||
|
.andExpect(jsonPath("$.title").value("The Matrix"))
|
||||||
|
.andExpect(jsonPath("$.description").value("A computer hacker learns about the true nature of reality"))
|
||||||
|
.andExpect(jsonPath("$.id").exists())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `edit film should return updated film`() {
|
||||||
|
val createRequest =
|
||||||
|
CreateFilmRequest(
|
||||||
|
title = "Old Title",
|
||||||
|
description = "Old Description",
|
||||||
|
)
|
||||||
|
|
||||||
|
val response =
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/api/films")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(createRequest)),
|
||||||
|
).andReturn()
|
||||||
|
|
||||||
|
val filmId = objectMapper.readTree(response.response.contentAsString).get("id").asText()
|
||||||
|
|
||||||
|
val editRequest =
|
||||||
|
EditFilmRequest(
|
||||||
|
title = "New Title",
|
||||||
|
description = "New Description",
|
||||||
|
)
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
patch("/api/films/$filmId")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(editRequest)),
|
||||||
|
).andExpect(status().isOk)
|
||||||
|
.andExpect(jsonPath("$.title").value("New Title"))
|
||||||
|
.andExpect(jsonPath("$.description").value("New Description"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `search film by title should return film`() {
|
||||||
|
val request =
|
||||||
|
CreateFilmRequest(
|
||||||
|
title = "Inception",
|
||||||
|
description = "Dream within a dream",
|
||||||
|
)
|
||||||
|
|
||||||
|
mockMvc.perform(
|
||||||
|
post("/api/films")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(request)),
|
||||||
|
)
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
get("/api/films/search")
|
||||||
|
.param("title", "Inception"),
|
||||||
|
).andExpect(status().isOk)
|
||||||
|
.andExpect(jsonPath("$.title").value("Inception"))
|
||||||
|
.andExpect(jsonPath("$.description").value("Dream within a dream"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `search film by non-existent title should return 404`() {
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
get("/api/films/search")
|
||||||
|
.param("title", "NonExistentFilm12345"),
|
||||||
|
).andExpect(status().isNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `delete film should return 204 NO CONTENT`() {
|
||||||
|
val request =
|
||||||
|
CreateFilmRequest(
|
||||||
|
title = "Film To Delete",
|
||||||
|
description = "This film will be deleted",
|
||||||
|
)
|
||||||
|
|
||||||
|
val response =
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/api/films")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(request)),
|
||||||
|
).andReturn()
|
||||||
|
|
||||||
|
val filmId = objectMapper.readTree(response.response.contentAsString).get("id").asText()
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(delete("/api/films/$filmId"))
|
||||||
|
.andExpect(status().isNoContent())
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
get("/api/films/search").param("title", "Film To Delete"),
|
||||||
|
).andExpect(status().isNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
package com.project.movienight.controllers
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.project.movienight.adapters.web.dto.request.CreateFilmRequest
|
||||||
|
import com.project.movienight.adapters.web.dto.request.CreateUserRequest
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest
|
||||||
|
import org.springframework.http.MediaType
|
||||||
|
import org.springframework.test.web.servlet.MockMvc
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
|
||||||
|
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath
|
||||||
|
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
|
||||||
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
@AutoConfigureMockMvc
|
||||||
|
@Transactional
|
||||||
|
class FilmLibraryControllerTest {
|
||||||
|
@Autowired
|
||||||
|
private lateinit var mockMvc: MockMvc
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private lateinit var objectMapper: ObjectMapper
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `add film to library should work`() {
|
||||||
|
val userRequest =
|
||||||
|
CreateUserRequest(
|
||||||
|
name = "Film Adder",
|
||||||
|
email = "adder@example.com",
|
||||||
|
)
|
||||||
|
val userResponse =
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/api/users")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(userRequest)),
|
||||||
|
).andReturn()
|
||||||
|
val userId = objectMapper.readTree(userResponse.response.contentAsString).get("id").asText()
|
||||||
|
|
||||||
|
val filmRequest =
|
||||||
|
CreateFilmRequest(
|
||||||
|
title = "Library Film",
|
||||||
|
description = "Film description",
|
||||||
|
)
|
||||||
|
val filmResponse =
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/api/films")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(filmRequest)),
|
||||||
|
).andReturn()
|
||||||
|
val filmId = objectMapper.readTree(filmResponse.response.contentAsString).get("id").asText()
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/api/users/$userId/library/films/$filmId"),
|
||||||
|
).andExpect(status().isCreated())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `remove film from library should return 204`() {
|
||||||
|
val userRequest =
|
||||||
|
CreateUserRequest(
|
||||||
|
name = "Remove Film",
|
||||||
|
email = "remove@example.com",
|
||||||
|
)
|
||||||
|
val userResponse =
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/api/users")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(userRequest)),
|
||||||
|
).andReturn()
|
||||||
|
val userId = objectMapper.readTree(userResponse.response.contentAsString).get("id").asText()
|
||||||
|
|
||||||
|
val filmRequest =
|
||||||
|
CreateFilmRequest(
|
||||||
|
title = "Film To Remove",
|
||||||
|
description = "Will be removed",
|
||||||
|
)
|
||||||
|
val filmResponse =
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/api/films")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(filmRequest)),
|
||||||
|
).andReturn()
|
||||||
|
val filmId = objectMapper.readTree(filmResponse.response.contentAsString).get("id").asText()
|
||||||
|
|
||||||
|
mockMvc.perform(post("/api/users/$userId/library/films/$filmId"))
|
||||||
|
mockMvc
|
||||||
|
.perform(delete("/api/users/$userId/library/films/$filmId"))
|
||||||
|
.andExpect(status().isNoContent())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `get available films should exclude film in user's library`() {
|
||||||
|
val userRequest =
|
||||||
|
CreateUserRequest(
|
||||||
|
name = "Available Films User",
|
||||||
|
email = "availablefilms@example.com",
|
||||||
|
)
|
||||||
|
val userResponse =
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/api/users")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(userRequest)),
|
||||||
|
).andReturn()
|
||||||
|
val userId = objectMapper.readTree(userResponse.response.contentAsString).get("id").asText()
|
||||||
|
|
||||||
|
val film1Request =
|
||||||
|
CreateFilmRequest(
|
||||||
|
title = "Film In Library",
|
||||||
|
description = "This will be in the library",
|
||||||
|
)
|
||||||
|
val film1Response =
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/api/films")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(film1Request)),
|
||||||
|
).andReturn()
|
||||||
|
val film1Id = objectMapper.readTree(film1Response.response.contentAsString).get("id").asText()
|
||||||
|
|
||||||
|
val film2Request =
|
||||||
|
CreateFilmRequest(
|
||||||
|
title = "Film Not In Library",
|
||||||
|
description = "This will not be in the library",
|
||||||
|
)
|
||||||
|
val film2Response =
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/api/films")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(film2Request)),
|
||||||
|
).andReturn()
|
||||||
|
val film2Id = objectMapper.readTree(film2Response.response.contentAsString).get("id").asText()
|
||||||
|
|
||||||
|
mockMvc.perform(post("/api/users/$userId/library/films/$film1Id"))
|
||||||
|
|
||||||
|
val result =
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
get("/api/users/$userId/library/available-films"),
|
||||||
|
).andExpect(status().isOk)
|
||||||
|
.andReturn()
|
||||||
|
|
||||||
|
val responseBody = result.response.contentAsString
|
||||||
|
val films = objectMapper.readTree(responseBody)
|
||||||
|
val returnedIds = films.toList().map { it.get("id").asText() }
|
||||||
|
assert(!returnedIds.contains(film1Id)) { "Film in library should not appear in available films" }
|
||||||
|
assert(returnedIds.contains(film2Id)) { "Film not in library should appear in available films" }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `get available films for user without library returns all films`() {
|
||||||
|
val userRequest =
|
||||||
|
CreateUserRequest(
|
||||||
|
name = "No Library User",
|
||||||
|
email = "nolibrary@example.com",
|
||||||
|
)
|
||||||
|
val userResponse =
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/api/users")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(userRequest)),
|
||||||
|
).andReturn()
|
||||||
|
val userId = objectMapper.readTree(userResponse.response.contentAsString).get("id").asText()
|
||||||
|
|
||||||
|
val filmRequest =
|
||||||
|
CreateFilmRequest(
|
||||||
|
title = "Available Film",
|
||||||
|
description = "Should appear in available films",
|
||||||
|
)
|
||||||
|
val filmResponse =
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/api/films")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(filmRequest)),
|
||||||
|
).andReturn()
|
||||||
|
val filmId = objectMapper.readTree(filmResponse.response.contentAsString).get("id").asText()
|
||||||
|
|
||||||
|
val result =
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
get("/api/users/$userId/library/available-films"),
|
||||||
|
).andExpect(status().isOk)
|
||||||
|
.andExpect(jsonPath("$[*].id").isArray)
|
||||||
|
.andReturn()
|
||||||
|
|
||||||
|
val responseBody = result.response.contentAsString
|
||||||
|
val films = objectMapper.readTree(responseBody)
|
||||||
|
val returnedIds = films.toList().map { it.get("id").asText() }
|
||||||
|
assert(returnedIds.contains(filmId)) { "Film should appear in available films when user has no library" }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package com.project.movienight.controllers
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.project.movienight.adapters.web.dto.request.CreateUserRequest
|
||||||
|
import com.project.movienight.adapters.web.dto.request.EditUserRequest
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest
|
||||||
|
import org.springframework.http.MediaType
|
||||||
|
import org.springframework.test.web.servlet.MockMvc
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
|
||||||
|
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath
|
||||||
|
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
|
||||||
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
@AutoConfigureMockMvc
|
||||||
|
@Transactional
|
||||||
|
class UserControllerTest {
|
||||||
|
@Autowired
|
||||||
|
private lateinit var mockMvc: MockMvc
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private lateinit var objectMapper: ObjectMapper
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `create user should return 201 CREATED`() {
|
||||||
|
val request =
|
||||||
|
CreateUserRequest(
|
||||||
|
name = "John Doe",
|
||||||
|
email = "john@example.com",
|
||||||
|
)
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/api/users")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(request)),
|
||||||
|
).andExpect(status().isCreated)
|
||||||
|
.andExpect(jsonPath("$.name").value("John Doe"))
|
||||||
|
.andExpect(jsonPath("$.email").value("john@example.com"))
|
||||||
|
.andExpect(jsonPath("$.id").exists())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `edit user should return updated user`() {
|
||||||
|
val createRequest =
|
||||||
|
CreateUserRequest(
|
||||||
|
name = "Old Name",
|
||||||
|
email = "edit@example.com",
|
||||||
|
)
|
||||||
|
|
||||||
|
val response =
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/api/users")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(createRequest)),
|
||||||
|
).andReturn()
|
||||||
|
|
||||||
|
val userId = objectMapper.readTree(response.response.contentAsString).get("id").asText()
|
||||||
|
|
||||||
|
val editRequest = EditUserRequest(name = "New Name")
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
patch("/api/users/$userId")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(editRequest)),
|
||||||
|
).andExpect(status().isOk)
|
||||||
|
.andExpect(jsonPath("$.name").value("New Name"))
|
||||||
|
.andExpect(jsonPath("$.email").value("edit@example.com"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `delete user should return 204 NO CONTENT`() {
|
||||||
|
val request =
|
||||||
|
CreateUserRequest(
|
||||||
|
name = "User To Delete",
|
||||||
|
email = "delete@example.com",
|
||||||
|
)
|
||||||
|
|
||||||
|
val response =
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
post("/api/users")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(request)),
|
||||||
|
).andReturn()
|
||||||
|
|
||||||
|
val userId = objectMapper.readTree(response.response.contentAsString).get("id").asText()
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(delete("/api/users/$userId"))
|
||||||
|
.andExpect(status().isNoContent())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `delete non-existent user should return 404`() {
|
||||||
|
val nonExistentId = "123e4567-e89b-12d3-a456-426614174000"
|
||||||
|
mockMvc
|
||||||
|
.perform(delete("/api/users/$nonExistentId"))
|
||||||
|
.andExpect(status().isNotFound())
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user