Skip to content

Commit

Permalink
Extract exception controller tests (!1201)
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcelKonrad committed Feb 7, 2025
1 parent 8b72464 commit cb70c64
Show file tree
Hide file tree
Showing 26 changed files with 1,728 additions and 1,312 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import org.springframework.boot.test.context.TestComponent
import org.springframework.data.mapping.PropertyReferenceException
import org.springframework.data.util.TypeInformation
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath
import org.springframework.restdocs.payload.PayloadDocumentation.responseFields
import org.springframework.test.context.ContextConfiguration
Expand All @@ -25,8 +24,8 @@ import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController

@WebMvcTest
@ContextConfiguration(classes = [ExceptionHandler::class, ExceptionHandlerUnitTest.TestController::class, CommonSpringConfig::class, FixedClockConfig::class])
internal class ExceptionHandlerUnitTest : MockMvcBaseTest("errors") {
@ContextConfiguration(classes = [ExceptionHandler::class, ExceptionUnitTest.TestController::class, CommonSpringConfig::class, FixedClockConfig::class])
internal class ExceptionUnitTest : MockMvcBaseTest("errors") {
@Test
fun simpleMessageException() {
documentedGetRequestTo("/errors/simple")
Expand All @@ -50,104 +49,6 @@ internal class ExceptionHandlerUnitTest : MockMvcBaseTest("errors") {
)
}

@Test
fun jsonMissingFieldException() {
post("/errors/json")
.content("""{"field": "abc", "nested": {}}""")
.contentType(MediaType.APPLICATION_JSON)
.perform()
.andExpect(status().isBadRequest)
.andExpect(jsonPath("$.status", `is`(400)))
.andExpect(jsonPath("$.error", `is`("Bad Request")))
.andExpect(jsonPath("$.message", `is`("""Field "$.nested.field" is either missing, "null", of invalid type, or contains "null" values.""")))
.andExpect(jsonPath("$.path", `is`("/errors/json")))
.andExpect(jsonPath("$.timestamp", `is`(notNullValue())))
}

@Test
fun jsonUnknownFieldException() {
post("/errors/json")
.content("""{"field": "abc", "nested": {"unknown": 1, "field": "def", "list": []}}""")
.contentType(MediaType.APPLICATION_JSON)
.perform()
.andExpect(status().isBadRequest)
.andExpect(jsonPath("$.status", `is`(400)))
.andExpect(jsonPath("$.error", `is`("Bad Request")))
.andExpect(jsonPath("$.message", `is`("""Unknown field "$.nested.unknown".""")))
.andExpect(jsonPath("$.path", `is`("/errors/json")))
.andExpect(jsonPath("$.timestamp", `is`(notNullValue())))
}

@Test
fun jsonTypeMismatchException() {
post("/errors/json")
.content("""{"field": "abc", "nested": {"field": [], "list": []}}""")
.contentType(MediaType.APPLICATION_JSON)
.perform()
.andExpect(status().isBadRequest)
.andExpect(jsonPath("$.status", `is`(400)))
.andExpect(jsonPath("$.error", `is`("Bad Request")))
.andExpect(jsonPath("$.message", `is`("""Field "$.nested.field" is either missing, "null", of invalid type, or contains "null" values.""")))
.andExpect(jsonPath("$.path", `is`("/errors/json")))
.andExpect(jsonPath("$.timestamp", `is`(notNullValue())))
}

@Test
fun jsonTypeMismatchArrayException() {
post("/errors/json")
.content("""{"field": "abc", "array": [{"unknown": 1}] "nested": {"field": "def", "list": []}}""")
.contentType(MediaType.APPLICATION_JSON)
.perform()
.andExpect(status().isBadRequest)
.andExpect(jsonPath("$.status", `is`(400)))
.andExpect(jsonPath("$.error", `is`("Bad Request")))
.andExpect(jsonPath("$.message", `is`("""Field "$.array[0].field" is either missing, "null", of invalid type, or contains "null" values.""")))
.andExpect(jsonPath("$.path", `is`("/errors/json")))
.andExpect(jsonPath("$.timestamp", `is`(notNullValue())))
}

@Test
fun jsonNullValueInCollectionException() {
post("/errors/json")
.content("""{"field": "abc", "nested": {"field": "def", "list": [null]}}""")
.contentType(MediaType.APPLICATION_JSON)
.perform()
.andExpect(status().isBadRequest)
.andExpect(jsonPath("$.status", `is`(400)))
.andExpect(jsonPath("$.error", `is`("Bad Request")))
.andExpect(jsonPath("$.message", `is`("""Field "$.nested.list" is either missing, "null", of invalid type, or contains "null" values.""")))
.andExpect(jsonPath("$.path", `is`("/errors/json")))
.andExpect(jsonPath("$.timestamp", `is`(notNullValue())))
}

@Test
fun jsonNullValueException() {
post("/errors/json")
.content("""{"field": null, "nested": {"field": "def", "list": []}}""")
.contentType(MediaType.APPLICATION_JSON)
.perform()
.andExpect(status().isBadRequest)
.andExpect(jsonPath("$.status", `is`(400)))
.andExpect(jsonPath("$.error", `is`("Bad Request")))
.andExpect(jsonPath("$.message", `is`("""Field "$.field" is either missing, "null", of invalid type, or contains "null" values.""")))
.andExpect(jsonPath("$.path", `is`("/errors/json")))
.andExpect(jsonPath("$.timestamp", `is`(notNullValue())))
}

@Test
fun jsonMalformedException() {
post("/errors/json")
.content("""{"field": "abc" "nested": {"field": "def", "list": []}}""")
.contentType(MediaType.APPLICATION_JSON)
.perform()
.andExpect(status().isBadRequest)
.andExpect(jsonPath("$.status", `is`(400)))
.andExpect(jsonPath("$.error", `is`("Bad Request")))
.andExpect(jsonPath("$.message", `is`("""Unexpected character ('"' (code 34)): was expecting comma to separate Object entries""")))
.andExpect(jsonPath("$.path", `is`("/errors/json")))
.andExpect(jsonPath("$.timestamp", `is`(notNullValue())))
}

@Test
fun serviceUnavailable() {
get("/errors/service-unavailable")
Expand Down Expand Up @@ -242,9 +143,6 @@ internal class ExceptionHandlerUnitTest : MockMvcBaseTest("errors") {
@GetMapping("/errors/simple")
fun simpleMessageWithCause(): Nothing = throw FakeSimpleExceptionWithoutCause()

@PostMapping("/errors/json")
fun json(@RequestBody request: JsonRequest): Nothing = throw NotImplementedError()

@GetMapping("/errors/service-unavailable")
fun serviceUnavailable(): Nothing = throw ServiceUnavailable.create("TEST", 500, "irrelevant")

Expand All @@ -265,18 +163,6 @@ internal class ExceptionHandlerUnitTest : MockMvcBaseTest("errors") {
@GetMapping("/errors/unknown-parameter")
fun unknownParameter(@RequestParam parameter: String): Nothing =
throw UnknownParameter(parameter)

@Suppress("ArrayInDataClass")
data class JsonRequest(
val field: String,
val nested: Nested,
val array: Array<Nested>
) {
data class Nested(
val field: String,
val list: List<String>
)
}
}

internal class FakeSimpleExceptionWithoutCause : SimpleMessageException(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package org.orkg.common.exceptions

import org.hamcrest.Matchers.`is`
import org.hamcrest.Matchers.notNullValue
import org.junit.jupiter.api.Test
import org.orkg.common.configuration.CommonSpringConfig
import org.orkg.common.exceptions.JsonExceptionUnitTest.TestController
import org.orkg.testing.configuration.FixedClockConfig
import org.orkg.testing.spring.MockMvcBaseTest
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.boot.test.context.TestComponent
import org.springframework.http.MediaType
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController

@WebMvcTest
@ContextConfiguration(classes = [ExceptionHandler::class, TestController::class, CommonSpringConfig::class, FixedClockConfig::class])
internal class JsonExceptionUnitTest : MockMvcBaseTest("errors") {

@Test
fun jsonMissingFieldException() {
post("/errors/json")
.content("""{"field": "abc", "nested": {}}""")
.contentType(MediaType.APPLICATION_JSON)
.perform()
.andExpect(status().isBadRequest)
.andExpect(jsonPath("$.status", `is`(400)))
.andExpect(jsonPath("$.error", `is`("Bad Request")))
.andExpect(jsonPath("$.message", `is`("""Field "$.nested.field" is either missing, "null", of invalid type, or contains "null" values.""")))
.andExpect(jsonPath("$.path", `is`("/errors/json")))
.andExpect(jsonPath("$.timestamp", `is`(notNullValue())))
}

@Test
fun jsonUnknownFieldException() {
post("/errors/json")
.content("""{"field": "abc", "nested": {"unknown": 1, "field": "def", "list": []}}""")
.contentType(MediaType.APPLICATION_JSON)
.perform()
.andExpect(status().isBadRequest)
.andExpect(jsonPath("$.status", `is`(400)))
.andExpect(jsonPath("$.error", `is`("Bad Request")))
.andExpect(jsonPath("$.message", `is`("""Unknown field "$.nested.unknown".""")))
.andExpect(jsonPath("$.path", `is`("/errors/json")))
.andExpect(jsonPath("$.timestamp", `is`(notNullValue())))
}

@Test
fun jsonTypeMismatchException() {
post("/errors/json")
.content("""{"field": "abc", "nested": {"field": [], "list": []}}""")
.contentType(MediaType.APPLICATION_JSON)
.perform()
.andExpect(status().isBadRequest)
.andExpect(jsonPath("$.status", `is`(400)))
.andExpect(jsonPath("$.error", `is`("Bad Request")))
.andExpect(jsonPath("$.message", `is`("""Field "$.nested.field" is either missing, "null", of invalid type, or contains "null" values.""")))
.andExpect(jsonPath("$.path", `is`("/errors/json")))
.andExpect(jsonPath("$.timestamp", `is`(notNullValue())))
}

@Test
fun jsonTypeMismatchArrayException() {
post("/errors/json")
.content("""{"field": "abc", "array": [{"unknown": 1}] "nested": {"field": "def", "list": []}}""")
.contentType(MediaType.APPLICATION_JSON)
.perform()
.andExpect(status().isBadRequest)
.andExpect(jsonPath("$.status", `is`(400)))
.andExpect(jsonPath("$.error", `is`("Bad Request")))
.andExpect(jsonPath("$.message", `is`("""Field "$.array[0].field" is either missing, "null", of invalid type, or contains "null" values.""")))
.andExpect(jsonPath("$.path", `is`("/errors/json")))
.andExpect(jsonPath("$.timestamp", `is`(notNullValue())))
}

@Test
fun jsonNullValueInCollectionException() {
post("/errors/json")
.content("""{"field": "abc", "nested": {"field": "def", "list": [null]}}""")
.contentType(MediaType.APPLICATION_JSON)
.perform()
.andExpect(status().isBadRequest)
.andExpect(jsonPath("$.status", `is`(400)))
.andExpect(jsonPath("$.error", `is`("Bad Request")))
.andExpect(jsonPath("$.message", `is`("""Field "$.nested.list" is either missing, "null", of invalid type, or contains "null" values.""")))
.andExpect(jsonPath("$.path", `is`("/errors/json")))
.andExpect(jsonPath("$.timestamp", `is`(notNullValue())))
}

@Test
fun jsonNullValueException() {
post("/errors/json")
.content("""{"field": null, "nested": {"field": "def", "list": []}}""")
.contentType(MediaType.APPLICATION_JSON)
.perform()
.andExpect(status().isBadRequest)
.andExpect(jsonPath("$.status", `is`(400)))
.andExpect(jsonPath("$.error", `is`("Bad Request")))
.andExpect(jsonPath("$.message", `is`("""Field "$.field" is either missing, "null", of invalid type, or contains "null" values.""")))
.andExpect(jsonPath("$.path", `is`("/errors/json")))
.andExpect(jsonPath("$.timestamp", `is`(notNullValue())))
}

@Test
fun jsonMalformedException() {
post("/errors/json")
.content("""{"field": "abc" "nested": {"field": "def", "list": []}}""")
.contentType(MediaType.APPLICATION_JSON)
.perform()
.andExpect(status().isBadRequest)
.andExpect(jsonPath("$.status", `is`(400)))
.andExpect(jsonPath("$.error", `is`("Bad Request")))
.andExpect(jsonPath("$.message", `is`("""Unexpected character ('"' (code 34)): was expecting comma to separate Object entries""")))
.andExpect(jsonPath("$.path", `is`("/errors/json")))
.andExpect(jsonPath("$.timestamp", `is`(notNullValue())))
}

@TestComponent
@RestController
internal class TestController {
@PostMapping("/errors/json")
fun json(@RequestBody request: JsonRequest): Nothing = throw NotImplementedError()

@Suppress("ArrayInDataClass")
data class JsonRequest(
val field: String,
val nested: Nested,
val array: Array<Nested>
) {
data class Nested(
val field: String,
val list: List<String>
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package org.orkg.common.exceptions

import org.hamcrest.Matchers.`is`
import org.hamcrest.Matchers.notNullValue
import org.junit.jupiter.api.Test
import org.orkg.common.configuration.CommonSpringConfig
import org.orkg.common.exceptions.MediaTypeCapabilitiesExceptionUnitTest.TestController
import org.orkg.testing.configuration.FixedClockConfig
import org.orkg.testing.spring.MockMvcBaseTest
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.boot.test.context.TestComponent
import org.springframework.http.HttpStatus
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.header
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController

@WebMvcTest
@ContextConfiguration(classes = [ExceptionHandler::class, TestController::class, CommonSpringConfig::class, FixedClockConfig::class])
internal class MediaTypeCapabilitiesExceptionUnitTest : MockMvcBaseTest("errors") {

@Test
fun malformedMediaTypeCapability() {
val name = "formatted-label"
val value = "true"

get("/errors/malformed-media-type-capability")
.param("name", name)
.param("value", value)
.perform()
.andExpect(status().isNotAcceptable)
.andExpect(jsonPath("$.status").value(HttpStatus.NOT_ACCEPTABLE.value()))
.andExpect(jsonPath("$.error", `is`("Not Acceptable")))
.andExpect(jsonPath("$.path").value("/errors/malformed-media-type-capability"))
.andExpect(jsonPath("$.message").value("""Malformed value "$value" for media type capability "$name"."""))
.andExpect(jsonPath("$.timestamp", `is`(notNullValue())))
}

@Test
fun handleHttpMediaTypeNotAcceptable() {
get("/errors/handle-http-media-type-not-acceptable")
.accept("application/unsupported+json")
.perform()
.andExpect(status().isNotAcceptable)
.andExpect(jsonPath("$.status").value(HttpStatus.NOT_ACCEPTABLE.value()))
.andExpect(jsonPath("$.error", `is`("Not Acceptable")))
.andExpect(jsonPath("$.path").value("/errors/handle-http-media-type-not-acceptable"))
.andExpect(jsonPath("$.message").value("""Unsupported response media type. Please check the 'Accept' header for a list of supported media types."""))
.andExpect(jsonPath("$.timestamp", `is`(notNullValue())))
.andExpect(header().string("Accept", "application/json, application/xml"))
}

@Test
fun httpMediaTypeNotSupported() {
post("/errors/http-media-type-not-supported")
.content("request")
.contentType("text/plain")
.perform()
.andExpect(status().isUnsupportedMediaType)
.andExpect(jsonPath("$.status").value(HttpStatus.UNSUPPORTED_MEDIA_TYPE.value()))
.andExpect(jsonPath("$.error", `is`("Unsupported Media Type")))
.andExpect(jsonPath("$.path").value("/errors/http-media-type-not-supported"))
.andExpect(jsonPath("$.message").value("""Unsupported request media type. Please check the 'Accept' header for a list of supported media types."""))
.andExpect(jsonPath("$.timestamp", `is`(notNullValue())))
.andExpect(header().string("Accept", "application/json, application/xml"))
}

@TestComponent
@RestController
internal class TestController {
@GetMapping("/errors/malformed-media-type-capability")
fun malformedMediaTypeCapability(@RequestParam name: String, @RequestParam value: String): Nothing =
throw MalformedMediaTypeCapability(name, value)

@GetMapping("/errors/handle-http-media-type-not-acceptable", produces = ["application/json", "application/xml"])
fun handleHttpMediaTypeNotAcceptable() = "response"

@PostMapping("/errors/http-media-type-not-supported", consumes = ["application/json", "application/xml"])
fun httpMediaTypeNotSupported(@RequestBody body: String): Nothing = throw NotImplementedError()
}
}
Loading

0 comments on commit cb70c64

Please sign in to comment.