test: write tests for controllers #24

Merged
devitq merged 11 commits from feat/spring-tests into develop 2026-05-08 20:32:36 +00:00
8 changed files with 485 additions and 14 deletions
1
@@ -64,7 +64,7 @@ class FilmRepository(
override fun findByTitle(title: String): Film? {
val films =
jdbc.query(
"SELECT id, title, description FROM films WHERE title = ?",
"SELECT id, title, description FROM films WHERE title = ? ORDER BY id LIMIT 1",
filmRowMapper,
title,
)
@@ -86,7 +86,8 @@ class UserRepository(
val entities =
jdbc.query(
"""
SELECT id, name, email, provider, provider_id, created_at FROM users
SELECT id, name, email, provider, provider_id, created_at
FROM users
WHERE provider = ? AND provider_id = ?
""".trimIndent(),
userEntityRowMapper,
1
@@ -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.SearchFilmByTitleUseCase
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PatchMapping
2
@@ -75,11 +76,18 @@ class FilmController(
@PathVariable id: UUID,
): FilmResponse = FilmResponse.fromDomain(getFilmByIdUseCase.getById(id))
@GetMapping
fun getAll(): List<FilmResponse> = getAllFilmsUseCase.getAll().map { FilmResponse.fromDomain(it) }
@GetMapping("/search")
fun searchByTitle(
@RequestParam title: String,
): FilmResponse? = searchFilmByTitleUseCase.searchByTitle(title)?.let { FilmResponse.fromDomain(it) }
@GetMapping
fun getAll(): List<FilmResponse> = getAllFilmsUseCase.getAll().map { FilmResponse.fromDomain(it) }
): ResponseEntity<FilmResponse> {
val film = searchFilmByTitleUseCase.searchByTitle(title)
return if (film != null) {
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.RemoveFilmFromLibraryCommand
import com.project.movienight.application.ports.input.RemoveFilmFromLibraryUseCase
import com.project.movienight.domain.exception.EntityNotFoundException
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
1
@@ -67,9 +68,7 @@ class FilmLibraryController(
getFilmLibraryUseCase.getLibrary(
GetFilmLibraryQuery(userId = userId),
)
val film = getFilmByIdUseCase.getById(library.filmId)
return listOf(FilmResponse.fromDomain(film))
}
3
@@ -107,13 +106,24 @@ class FilmLibraryController(
@PathVariable userId: UUID,
): List<FilmResponse> {
val userLibrary =
getFilmLibraryUseCase.getLibrary(
GetFilmLibraryQuery(userId = userId),
)
runCatching {
getFilmLibraryUseCase.getLibrary(
GetFilmLibraryQuery(userId = userId),
)
}.onFailure { exception ->
if (exception !is EntityNotFoundException) {
throw exception
}
}.getOrNull()
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) }
}
@@ -59,7 +59,7 @@ class FilmControllerSearchTest {
}
@Test
fun `search returns empty body when title is missing`() {
fun `search returns 404 when title is not found`() {
val title = "Unknown Title"
every { searchFilmByTitleUseCase.searchByTitle(title) } returns null
@@ -68,7 +68,7 @@ class FilmControllerSearchTest {
.get("/api/films/search") {
param("title", title)
}.andExpect {
status { isOk() }
status { isNotFound() }
content { string("") }
}
@@ -0,0 +1,140 @@
package com.project.movienight.controllers
copilot-pull-request-reviewer[bot] commented 2026-04-21 18:32:34 +00:00 (Migrated from github.com)
Review

This multiline EditFilmRequest(...) call is missing a trailing comma after the last argument; with ktlint experimental rules enabled in this repo, that often fails formatting checks. Add the trailing comma to match the style used throughout the codebase.

            description = "New Description",
This multiline `EditFilmRequest(...)` call is missing a trailing comma after the last argument; with ktlint experimental rules enabled in this repo, that often fails formatting checks. Add the trailing comma to match the style used throughout the codebase. ```suggestion description = "New Description", ```
copilot-pull-request-reviewer[bot] commented 2026-04-21 18:32:35 +00:00 (Migrated from github.com)
Review

These wildcard imports (MockMvcRequestBuilders.*, MockMvcResultMatchers.*) will fail ktlint's no-wildcard-imports rule (ktlintCheck is part of check). Replace them with explicit imports for only the methods/matchers used in this test file.

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
These wildcard imports (`MockMvcRequestBuilders.*`, `MockMvcResultMatchers.*`) will fail ktlint's `no-wildcard-imports` rule (ktlintCheck is part of `check`). Replace them with explicit imports for only the methods/matchers used in this test file. ```suggestion 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 ```
copilot-pull-request-reviewer[bot] commented 2026-04-21 18:32:38 +00:00 (Migrated from github.com)
Review

There are consecutive blank lines between tests (e.g., around here), which will fail ktlint's standard formatting rules. Remove the extra blank line(s) so there's at most one blank line between members.


There are consecutive blank lines between tests (e.g., around here), which will fail ktlint's standard formatting rules. Remove the extra blank line(s) so there's at most one blank line between members. ```suggestion ```
copilot-pull-request-reviewer[bot] commented 2026-04-21 18:32:38 +00:00 (Migrated from github.com)
Review

This multiline CreateFilmRequest(...) call is missing a trailing comma after the last argument; with ktlint experimental rules enabled in this repo, that often fails formatting checks. Add the trailing comma to match the style used throughout the main source files.

            description = "A computer hacker learns about the true nature of reality",
This multiline `CreateFilmRequest(...)` call is missing a trailing comma after the last argument; with ktlint experimental rules enabled in this repo, that often fails formatting checks. Add the trailing comma to match the style used throughout the main source files. ```suggestion description = "A computer hacker learns about the true nature of reality", ```
copilot-pull-request-reviewer[bot] commented 2026-04-21 18:32:38 +00:00 (Migrated from github.com)
Review

This mockMvc.perform(...) is formatted as a multiline call-site but is missing the trailing comma after the argument. The main codebase uses trailing commas for multiline call-sites, and with ktlint experimental rules enabled this can fail formatting checks; update this (and similar call-sites in the file) to include the trailing comma.

This `mockMvc.perform(...)` is formatted as a multiline call-site but is missing the trailing comma after the argument. The main codebase uses trailing commas for multiline call-sites, and with ktlint experimental rules enabled this can fail formatting checks; update this (and similar call-sites in the file) to include the trailing comma.
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
copilot-pull-request-reviewer[bot] commented 2026-04-21 18:32:34 +00:00 (Migrated from github.com)
Review

These wildcard imports (MockMvcRequestBuilders.*, MockMvcResultMatchers.*) will fail ktlint's no-wildcard-imports rule (ktlintCheck is part of check). Replace them with explicit imports for only the methods/matchers used in this test file.

import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
These wildcard imports (`MockMvcRequestBuilders.*`, `MockMvcResultMatchers.*`) will fail ktlint's `no-wildcard-imports` rule (ktlintCheck is part of `check`). Replace them with explicit imports for only the methods/matchers used in this test file. ```suggestion import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status ```
copilot-pull-request-reviewer[bot] commented 2026-04-21 18:32:37 +00:00 (Migrated from github.com)
Review

There are consecutive blank lines in this file (e.g., here before the first test), which will fail ktlint formatting checks. Remove extra blank lines so there's at most one blank line between members.


There are consecutive blank lines in this file (e.g., here before the first test), which will fail ktlint formatting checks. Remove extra blank lines so there's at most one blank line between members. ```suggestion ```
copilot-pull-request-reviewer[bot] commented 2026-04-21 18:32:37 +00:00 (Migrated from github.com)
Review

This mockMvc.perform(...) call is formatted as a multiline call-site but is missing the trailing comma after the single argument. The main source files consistently use trailing commas in multiline call-sites, and with ktlint experimental rules enabled this can fail formatting checks; update this (and similar call-sites in the file) to include the trailing comma.

This `mockMvc.perform(...)` call is formatted as a multiline call-site but is missing the trailing comma after the single argument. The main source files consistently use trailing commas in multiline call-sites, and with ktlint experimental rules enabled this can fail formatting checks; update this (and similar call-sites in the file) to include the trailing comma.
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
copilot-pull-request-reviewer[bot] commented 2026-04-21 18:32:35 +00:00 (Migrated from github.com)
Review

This multiline CreateUserRequest(...) call is missing a trailing comma after the last argument. The main codebase consistently uses trailing commas in multiline argument lists, and with ktlint experimental rules enabled this can fail formatting checks; update this (and other multiline calls in the file) to include trailing commas.

This multiline `CreateUserRequest(...)` call is missing a trailing comma after the last argument. The main codebase consistently uses trailing commas in multiline argument lists, and with ktlint experimental rules enabled this can fail formatting checks; update this (and other multiline calls in the file) to include trailing commas.
copilot-pull-request-reviewer[bot] commented 2026-04-21 18:32:37 +00:00 (Migrated from github.com)
Review

These wildcard imports (MockMvcRequestBuilders.*, MockMvcResultMatchers.*) will fail ktlint's no-wildcard-imports rule (ktlintCheck is part of check). Replace them with explicit imports for only the methods/matchers used in this test file.

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
These wildcard imports (`MockMvcRequestBuilders.*`, `MockMvcResultMatchers.*`) will fail ktlint's `no-wildcard-imports` rule (ktlintCheck is part of `check`). Replace them with explicit imports for only the methods/matchers used in this test file. ```suggestion 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 ```
copilot-pull-request-reviewer[bot] commented 2026-04-21 18:32:38 +00:00 (Migrated from github.com)
Review

This file has consecutive blank lines (e.g., between test cases), which will fail ktlint's standard formatting rules. Remove extra blank lines so there's at most one blank line between members.


This file has consecutive blank lines (e.g., between test cases), which will fail ktlint's standard formatting rules. Remove extra blank lines so there's at most one blank line between members. ```suggestion ```
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())
}
}