diff --git a/common/spring-webmvc/src/test/kotlin/org/orkg/common/exceptions/ExceptionHandlerUnitTest.kt b/common/spring-webmvc/src/test/kotlin/org/orkg/common/exceptions/ExceptionUnitTest.kt similarity index 61% rename from common/spring-webmvc/src/test/kotlin/org/orkg/common/exceptions/ExceptionHandlerUnitTest.kt rename to common/spring-webmvc/src/test/kotlin/org/orkg/common/exceptions/ExceptionUnitTest.kt index daa06dd51..9143dd76e 100644 --- a/common/spring-webmvc/src/test/kotlin/org/orkg/common/exceptions/ExceptionHandlerUnitTest.kt +++ b/common/spring-webmvc/src/test/kotlin/org/orkg/common/exceptions/ExceptionUnitTest.kt @@ -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 @@ -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") @@ -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") @@ -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") @@ -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 - ) { - data class Nested( - val field: String, - val list: List - ) - } } internal class FakeSimpleExceptionWithoutCause : SimpleMessageException( diff --git a/common/spring-webmvc/src/test/kotlin/org/orkg/common/exceptions/JsonExceptionUnitTest.kt b/common/spring-webmvc/src/test/kotlin/org/orkg/common/exceptions/JsonExceptionUnitTest.kt new file mode 100644 index 000000000..0afed5ac9 --- /dev/null +++ b/common/spring-webmvc/src/test/kotlin/org/orkg/common/exceptions/JsonExceptionUnitTest.kt @@ -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 + ) { + data class Nested( + val field: String, + val list: List + ) + } + } +} diff --git a/common/spring-webmvc/src/test/kotlin/org/orkg/common/exceptions/MediaTypeCapabilitiesExceptionUnitTest.kt b/common/spring-webmvc/src/test/kotlin/org/orkg/common/exceptions/MediaTypeCapabilitiesExceptionUnitTest.kt new file mode 100644 index 000000000..f10336338 --- /dev/null +++ b/common/spring-webmvc/src/test/kotlin/org/orkg/common/exceptions/MediaTypeCapabilitiesExceptionUnitTest.kt @@ -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() + } +} diff --git a/community/community-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/community/adapter/input/rest/exceptions/ContributorExceptionUnitTest.kt b/community/community-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/community/adapter/input/rest/exceptions/ContributorExceptionUnitTest.kt new file mode 100644 index 000000000..113f247cd --- /dev/null +++ b/community/community-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/community/adapter/input/rest/exceptions/ContributorExceptionUnitTest.kt @@ -0,0 +1,49 @@ +package org.orkg.community.adapter.input.rest.exceptions + +import org.hamcrest.Matchers.`is` +import org.hamcrest.Matchers.notNullValue +import org.junit.jupiter.api.Test +import org.orkg.common.ContributorId +import org.orkg.common.exceptions.ExceptionHandler +import org.orkg.community.adapter.input.rest.exceptions.ContributorExceptionUnitTest.TestController +import org.orkg.community.domain.ContributorAlreadyExists +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.jsonPath +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController + +@WebMvcTest +@ContextConfiguration(classes = [TestController::class, ExceptionHandler::class, FixedClockConfig::class]) +internal class ContributorExceptionUnitTest : MockMvcBaseTest("exceptions") { + + @Test + fun contributorAlreadyExists() { + val id = ContributorId("f9965b2a-5222-45e1-8ef8-dbd8ce1f57bc") + + get("/contributor-already-exists") + .param("id", id.value.toString()) + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.path").value("/contributor-already-exists")) + .andExpect(jsonPath("$.message").value("""Contributor "$id" already exists.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @TestComponent + @RestController + internal class TestController { + @GetMapping("/contributor-already-exists") + fun contributorAlreadyExists(@RequestParam id: ContributorId) { + throw ContributorAlreadyExists(id) + } + } +} diff --git a/community/community-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/community/adapter/input/rest/ExceptionControllerUnitTest.kt b/community/community-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/community/adapter/input/rest/exceptions/ObservatoryExceptionUnitTest.kt similarity index 62% rename from community/community-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/community/adapter/input/rest/ExceptionControllerUnitTest.kt rename to community/community-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/community/adapter/input/rest/exceptions/ObservatoryExceptionUnitTest.kt index 1b72b2429..940ff65f6 100644 --- a/community/community-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/community/adapter/input/rest/ExceptionControllerUnitTest.kt +++ b/community/community-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/community/adapter/input/rest/exceptions/ObservatoryExceptionUnitTest.kt @@ -1,16 +1,12 @@ -package org.orkg.community.adapter.input.rest +package org.orkg.community.adapter.input.rest.exceptions import org.hamcrest.Matchers.`is` import org.hamcrest.Matchers.notNullValue import org.junit.jupiter.api.Test -import org.orkg.common.ContributorId import org.orkg.common.ObservatoryId -import org.orkg.common.OrganizationId import org.orkg.common.exceptions.ExceptionHandler -import org.orkg.community.adapter.input.rest.ExceptionControllerUnitTest.FakeExceptionController -import org.orkg.community.domain.ContributorAlreadyExists +import org.orkg.community.adapter.input.rest.exceptions.ObservatoryExceptionUnitTest.TestController import org.orkg.community.domain.ObservatoryAlreadyExists -import org.orkg.community.domain.OrganizationNotFound import org.orkg.testing.configuration.FixedClockConfig import org.orkg.testing.spring.MockMvcBaseTest import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest @@ -24,8 +20,8 @@ import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController @WebMvcTest -@ContextConfiguration(classes = [FakeExceptionController::class, ExceptionHandler::class, FixedClockConfig::class]) -internal class ExceptionControllerUnitTest : MockMvcBaseTest("exceptions") { +@ContextConfiguration(classes = [TestController::class, ExceptionHandler::class, FixedClockConfig::class]) +internal class ObservatoryExceptionUnitTest : MockMvcBaseTest("exceptions") { @Test fun observatoryAlreadyExistsWithId() { @@ -72,39 +68,9 @@ internal class ExceptionControllerUnitTest : MockMvcBaseTest("exceptions") { .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) } - @Test - fun organizationNotFound() { - val id = OrganizationId("f9965b2a-5222-45e1-8ef8-dbd8ce1f57bc") - - get("/organization-not-found") - .param("id", id.value.toString()) - .perform() - .andExpect(status().isNotFound) - .andExpect(jsonPath("$.status").value(HttpStatus.NOT_FOUND.value())) - .andExpect(jsonPath("$.error", `is`("Not Found"))) - .andExpect(jsonPath("$.path").value("/organization-not-found")) - .andExpect(jsonPath("$.message").value("""Organization "$id" not found.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun contributorAlreadyExists() { - val id = ContributorId("f9965b2a-5222-45e1-8ef8-dbd8ce1f57bc") - - get("/contributor-already-exists") - .param("id", id.value.toString()) - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.path").value("/contributor-already-exists")) - .andExpect(jsonPath("$.message").value("""Contributor "$id" already exists.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - @TestComponent @RestController - internal class FakeExceptionController { + internal class TestController { @GetMapping("/observatory-already-exists-with-id") fun observatoryAlreadyExistsWithId(@RequestParam id: ObservatoryId) { throw ObservatoryAlreadyExists.withId(id) @@ -119,15 +85,5 @@ internal class ExceptionControllerUnitTest : MockMvcBaseTest("exceptions") { fun observatoryAlreadyExistsWithDisplayId(@RequestParam displayId: String) { throw ObservatoryAlreadyExists.withDisplayId(displayId) } - - @GetMapping("/organization-not-found") - fun organizationNotFound(@RequestParam id: OrganizationId) { - throw OrganizationNotFound(id) - } - - @GetMapping("/contributor-already-exists") - fun contributorAlreadyExists(@RequestParam id: ContributorId) { - throw ContributorAlreadyExists(id) - } } } diff --git a/community/community-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/community/adapter/input/rest/exceptions/OrganizationExceptionUnitTest.kt b/community/community-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/community/adapter/input/rest/exceptions/OrganizationExceptionUnitTest.kt new file mode 100644 index 000000000..f99ba2714 --- /dev/null +++ b/community/community-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/community/adapter/input/rest/exceptions/OrganizationExceptionUnitTest.kt @@ -0,0 +1,49 @@ +package org.orkg.community.adapter.input.rest.exceptions + +import org.hamcrest.Matchers.`is` +import org.hamcrest.Matchers.notNullValue +import org.junit.jupiter.api.Test +import org.orkg.common.OrganizationId +import org.orkg.common.exceptions.ExceptionHandler +import org.orkg.community.adapter.input.rest.exceptions.OrganizationExceptionUnitTest.TestController +import org.orkg.community.domain.OrganizationNotFound +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.jsonPath +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController + +@WebMvcTest +@ContextConfiguration(classes = [TestController::class, ExceptionHandler::class, FixedClockConfig::class]) +internal class OrganizationExceptionUnitTest : MockMvcBaseTest("exceptions") { + + @Test + fun organizationNotFound() { + val id = OrganizationId("f9965b2a-5222-45e1-8ef8-dbd8ce1f57bc") + + get("/organization-not-found") + .param("id", id.value.toString()) + .perform() + .andExpect(status().isNotFound) + .andExpect(jsonPath("$.status").value(HttpStatus.NOT_FOUND.value())) + .andExpect(jsonPath("$.error", `is`("Not Found"))) + .andExpect(jsonPath("$.path").value("/organization-not-found")) + .andExpect(jsonPath("$.message").value("""Organization "$id" not found.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @TestComponent + @RestController + internal class TestController { + @GetMapping("/organization-not-found") + fun organizationNotFound(@RequestParam id: OrganizationId) { + throw OrganizationNotFound(id) + } + } +} diff --git a/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/LiteratureListControllerExceptionUnitTest.kt b/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/LiteratureListControllerExceptionUnitTest.kt deleted file mode 100644 index 946042a11..000000000 --- a/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/LiteratureListControllerExceptionUnitTest.kt +++ /dev/null @@ -1,72 +0,0 @@ -package org.orkg.contenttypes.adapter.input.rest - -import org.hamcrest.Matchers.`is` -import org.hamcrest.Matchers.notNullValue -import org.junit.jupiter.api.Test -import org.orkg.common.ThingId -import org.orkg.common.exceptions.ExceptionHandler -import org.orkg.contenttypes.adapter.input.rest.LiteratureListControllerExceptionUnitTest.FakeExceptionController -import org.orkg.contenttypes.domain.LiteratureListAlreadyPublished -import org.orkg.contenttypes.domain.PublishedLiteratureListContentNotFound -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.jsonPath -import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.RequestParam -import org.springframework.web.bind.annotation.RestController - -@WebMvcTest -@ContextConfiguration(classes = [FakeExceptionController::class, ExceptionHandler::class, FixedClockConfig::class]) -internal class LiteratureListControllerExceptionUnitTest : MockMvcBaseTest("literature-lists") { - - @Test - fun publishedLiteratureListContentNotFound() { - val literatureListId = "R123" - val contentId = "R456" - - get("/published-literature-list-content-not-found") - .param("literatureListId", literatureListId) - .param("contentId", contentId) - .perform() - .andExpect(status().isNotFound) - .andExpect(jsonPath("$.status").value(HttpStatus.NOT_FOUND.value())) - .andExpect(jsonPath("$.error", `is`("Not Found"))) - .andExpect(jsonPath("$.path").value("/published-literature-list-content-not-found")) - .andExpect(jsonPath("$.message").value("""Literature list content "$contentId" not found for literature list "$literatureListId".""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun literatureListAlreadyPublished() { - val id = "R123" - - get("/literature-list-already-published") - .param("id", id) - .perform() - .andExpect(status().isForbidden) - .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) - .andExpect(jsonPath("$.error", `is`("Forbidden"))) - .andExpect(jsonPath("$.path").value("/literature-list-already-published")) - .andExpect(jsonPath("$.message").value("""Literature list "$id" is already published.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @TestComponent - @RestController - internal class FakeExceptionController { - @GetMapping("/published-literature-list-content-not-found") - fun publishedLiteratureListContentNotFound(@RequestParam literatureListId: ThingId, @RequestParam contentId: ThingId) { - throw PublishedLiteratureListContentNotFound(literatureListId, contentId) - } - - @GetMapping("/literature-list-already-published") - fun literatureListAlreadyPublished(@RequestParam id: ThingId) { - throw LiteratureListAlreadyPublished(id) - } - } -} diff --git a/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/CommonExceptionUnitTest.kt b/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/CommonExceptionUnitTest.kt new file mode 100644 index 000000000..9fae8bf6c --- /dev/null +++ b/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/CommonExceptionUnitTest.kt @@ -0,0 +1,87 @@ +package org.orkg.contenttypes.adapter.input.rest.exceptions + +import org.hamcrest.Matchers.`is` +import org.hamcrest.Matchers.notNullValue +import org.junit.jupiter.api.Test +import org.orkg.common.ThingId +import org.orkg.common.exceptions.ExceptionHandler +import org.orkg.contenttypes.adapter.input.rest.exceptions.CommonExceptionUnitTest.TestController +import org.orkg.contenttypes.domain.InvalidBibTeXReference +import org.orkg.contenttypes.domain.InvalidMonth +import org.orkg.contenttypes.domain.SustainableDevelopmentGoalNotFound +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.jsonPath +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController + +@WebMvcTest +@ContextConfiguration(classes = [TestController::class, ExceptionHandler::class, FixedClockConfig::class]) +internal class CommonExceptionUnitTest : MockMvcBaseTest("exceptions") { + + @Test + fun invalidMonth() { + get("/invalid-month") + .param("month", "0") + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.path").value("/invalid-month")) + .andExpect(jsonPath("$.message").value("""Invalid month "0". Must be in range [1..12].""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun sustainableDevelopmentGoalNotFound() { + get("/sustainable-development-goal-not-found") + .param("sdgId", "SDG1") + .perform() + .andExpect(status().isNotFound) + .andExpect(jsonPath("$.status").value(HttpStatus.NOT_FOUND.value())) + .andExpect(jsonPath("$.error", `is`("Not Found"))) + .andExpect(jsonPath("$.path").value("/sustainable-development-goal-not-found")) + .andExpect(jsonPath("$.message").value("""Sustainable Development Goal "SDG1" not found.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun invalidBibTeXReference() { + val reference = "not bibtex" + + get("/invalid-bibtex-reference") + .param("reference", reference) + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.path").value("/invalid-bibtex-reference")) + .andExpect(jsonPath("$.message").value("""Invalid BibTeX reference "$reference".""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @TestComponent + @RestController + internal class TestController { + @GetMapping("/invalid-month") + fun invalidMonth(@RequestParam month: Int) { + throw InvalidMonth(month) + } + + @GetMapping("/sustainable-development-goal-not-found") + fun sustainableDevelopmentGoalNotFound(@RequestParam sdgId: ThingId) { + throw SustainableDevelopmentGoalNotFound(sdgId) + } + + @GetMapping("/invalid-bibtex-reference") + fun invalidBibTeXReference(@RequestParam reference: String) { + throw InvalidBibTeXReference(reference) + } + } +} diff --git a/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/ComparisonExceptionUnitTest.kt b/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/ComparisonExceptionUnitTest.kt new file mode 100644 index 000000000..d5462a6fa --- /dev/null +++ b/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/ComparisonExceptionUnitTest.kt @@ -0,0 +1,112 @@ +package org.orkg.contenttypes.adapter.input.rest.exceptions + +import org.hamcrest.Matchers.`is` +import org.hamcrest.Matchers.notNullValue +import org.junit.jupiter.api.Test +import org.orkg.common.ThingId +import org.orkg.common.exceptions.ExceptionHandler +import org.orkg.contenttypes.adapter.input.rest.exceptions.ComparisonExceptionUnitTest.TestController +import org.orkg.contenttypes.domain.ComparisonAlreadyPublished +import org.orkg.contenttypes.domain.ComparisonNotModifiable +import org.orkg.contenttypes.domain.ComparisonRelatedFigureNotModifiable +import org.orkg.contenttypes.domain.ComparisonRelatedResourceNotModifiable +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.jsonPath +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController + +@WebMvcTest +@ContextConfiguration(classes = [TestController::class, ExceptionHandler::class, FixedClockConfig::class]) +internal class ComparisonExceptionUnitTest : MockMvcBaseTest("comparisons") { + + @Test + fun comparisonAlreadyPublished() { + val id = "R123" + + get("/comparison-already-published") + .param("id", id) + .perform() + .andExpect(status().isForbidden) + .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) + .andExpect(jsonPath("$.error", `is`("Forbidden"))) + .andExpect(jsonPath("$.path").value("/comparison-already-published")) + .andExpect(jsonPath("$.message").value("""Comparison "$id" is already published.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun comparisonNotModifiable() { + val id = "R123" + + get("/comparison-not-modifiable") + .param("id", id) + .perform() + .andExpect(status().isForbidden) + .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) + .andExpect(jsonPath("$.error", `is`("Forbidden"))) + .andExpect(jsonPath("$.path").value("/comparison-not-modifiable")) + .andExpect(jsonPath("$.message").value("""Comparison "$id" is not modifiable.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun comparisonRelatedResourceNotModifiable() { + val id = "R123" + + get("/comparison-related-resource-not-modifiable") + .param("id", id) + .perform() + .andExpect(status().isForbidden) + .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) + .andExpect(jsonPath("$.error", `is`("Forbidden"))) + .andExpect(jsonPath("$.path").value("/comparison-related-resource-not-modifiable")) + .andExpect(jsonPath("$.message").value("""Comparison related resource "$id" is not modifiable.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun comparisonRelatedFigureNotModifiable() { + val id = "R123" + + get("/comparison-related-figure-not-modifiable") + .param("id", id) + .perform() + .andExpect(status().isForbidden) + .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) + .andExpect(jsonPath("$.error", `is`("Forbidden"))) + .andExpect(jsonPath("$.path").value("/comparison-related-figure-not-modifiable")) + .andExpect(jsonPath("$.message").value("""Comparison related figure "$id" is not modifiable.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @TestComponent + @RestController + internal class TestController { + @GetMapping("/comparison-already-published") + fun comparisonAlreadyPublished(@RequestParam id: ThingId) { + throw ComparisonAlreadyPublished(id) + } + + @GetMapping("/comparison-not-modifiable") + fun comparisonNotModifiable(@RequestParam id: ThingId) { + throw ComparisonNotModifiable(id) + } + + @GetMapping("/comparison-related-resource-not-modifiable") + fun comparisonRelatedResourceNotModifiable(@RequestParam id: ThingId) { + throw ComparisonRelatedResourceNotModifiable(id) + } + + @GetMapping("/comparison-related-figure-not-modifiable") + fun comparisonRelatedFigureNotModifiable(@RequestParam id: ThingId) { + throw ComparisonRelatedFigureNotModifiable(id) + } + } +} diff --git a/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/LiteratureListExceptionUnitTest.kt b/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/LiteratureListExceptionUnitTest.kt new file mode 100644 index 000000000..7b7447eb3 --- /dev/null +++ b/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/LiteratureListExceptionUnitTest.kt @@ -0,0 +1,219 @@ +package org.orkg.contenttypes.adapter.input.rest.exceptions + +import org.hamcrest.Matchers.`is` +import org.hamcrest.Matchers.notNullValue +import org.junit.jupiter.api.Test +import org.orkg.common.ThingId +import org.orkg.common.exceptions.ExceptionHandler +import org.orkg.contenttypes.adapter.input.rest.exceptions.LiteratureListExceptionUnitTest.TestController +import org.orkg.contenttypes.domain.InvalidHeadingSize +import org.orkg.contenttypes.domain.InvalidListSectionEntry +import org.orkg.contenttypes.domain.LiteratureListAlreadyPublished +import org.orkg.contenttypes.domain.LiteratureListNotFound +import org.orkg.contenttypes.domain.LiteratureListNotModifiable +import org.orkg.contenttypes.domain.LiteratureListSectionTypeMismatch +import org.orkg.contenttypes.domain.PublishedLiteratureListContentNotFound +import org.orkg.contenttypes.domain.UnrelatedLiteratureListSection +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.jsonPath +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController + +@WebMvcTest +@ContextConfiguration(classes = [TestController::class, ExceptionHandler::class, FixedClockConfig::class]) +internal class LiteratureListExceptionUnitTest : MockMvcBaseTest("literature-lists") { + + @Test + fun publishedLiteratureListContentNotFound() { + val literatureListId = "R123" + val contentId = "R456" + + get("/published-literature-list-content-not-found") + .param("literatureListId", literatureListId) + .param("contentId", contentId) + .perform() + .andExpect(status().isNotFound) + .andExpect(jsonPath("$.status").value(HttpStatus.NOT_FOUND.value())) + .andExpect(jsonPath("$.error", `is`("Not Found"))) + .andExpect(jsonPath("$.path").value("/published-literature-list-content-not-found")) + .andExpect(jsonPath("$.message").value("""Literature list content "$contentId" not found for literature list "$literatureListId".""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun literatureListAlreadyPublished() { + val id = "R123" + + get("/literature-list-already-published") + .param("id", id) + .perform() + .andExpect(status().isForbidden) + .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) + .andExpect(jsonPath("$.error", `is`("Forbidden"))) + .andExpect(jsonPath("$.path").value("/literature-list-already-published")) + .andExpect(jsonPath("$.message").value("""Literature list "$id" is already published.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun literatureListNotFound() { + val id = ThingId("R123") + + get("/errors/literature-list-not-found") + .param("id", id.value) + .perform() + .andExpect(status().isNotFound) + .andExpect(jsonPath("$.status").value(HttpStatus.NOT_FOUND.value())) + .andExpect(jsonPath("$.error").value("Not Found")) + .andExpect(jsonPath("$.path").value("/errors/literature-list-not-found")) + .andExpect(jsonPath("$.message").value("""Literature list "$id" not found.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun invalidListSectionEntry() { + val id = "R123" + val expectedAnyInstanceOf = arrayOf("C1", "C2") + + get("/invalid-list-section-entry") + .param("id", id) + .param("expectedAnyInstanceOf", *expectedAnyInstanceOf) + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.path").value("/invalid-list-section-entry")) + .andExpect(jsonPath("$.message").value("""Invalid list section entry "$id". Must be an instance of either "C1", "C2".""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun invalidHeadingSize() { + val headingSize = "5" + + get("/invalid-heading-size") + .param("headingSize", headingSize) + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.path").value("/invalid-heading-size")) + .andExpect(jsonPath("$.message").value("""Invalid heading size "$headingSize". Must be at least 1.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun unrelatedLiteratureListSection() { + val literatureListId = "R123" + val literatureListSectionId = "R456" + + get("/unrelated-literature-list-section") + .param("literatureListId", literatureListId) + .param("literatureListSectionId", literatureListSectionId) + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.path").value("/unrelated-literature-list-section")) + .andExpect(jsonPath("$.message").value("""Literature list section "$literatureListSectionId" does not belong to literature list "$literatureListId".""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun literatureListSectionTypeMismatchMustBeTextSection() { + get("/literature-list-section-type-mismatch-must-be-text-section") + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.path").value("/literature-list-section-type-mismatch-must-be-text-section")) + .andExpect(jsonPath("$.message").value("""Invalid literature list section type. Must be a text section.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun literatureListSectionTypeMismatchMustBeListSection() { + get("/literature-list-section-type-mismatch-must-be-list-section") + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.path").value("/literature-list-section-type-mismatch-must-be-list-section")) + .andExpect(jsonPath("$.message").value("""Invalid literature list section type. Must be a list section.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun literatureListNotModifiable() { + val id = "R123" + + get("/literature-list-not-modifiable") + .param("id", id) + .perform() + .andExpect(status().isForbidden) + .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) + .andExpect(jsonPath("$.error", `is`("Forbidden"))) + .andExpect(jsonPath("$.path").value("/literature-list-not-modifiable")) + .andExpect(jsonPath("$.message").value("""Literature list "$id" is not modifiable.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @TestComponent + @RestController + internal class TestController { + @GetMapping("/published-literature-list-content-not-found") + fun publishedLiteratureListContentNotFound(@RequestParam literatureListId: ThingId, @RequestParam contentId: ThingId) { + throw PublishedLiteratureListContentNotFound(literatureListId, contentId) + } + + @GetMapping("/literature-list-already-published") + fun literatureListAlreadyPublished(@RequestParam id: ThingId) { + throw LiteratureListAlreadyPublished(id) + } + + @GetMapping("/errors/literature-list-not-found") + fun literatureListNotFound(@RequestParam id: ThingId) { + throw LiteratureListNotFound(id) + } + + @GetMapping("/invalid-list-section-entry") + fun invalidListSectionEntry(@RequestParam id: ThingId, @RequestParam expectedAnyInstanceOf: Set) { + throw InvalidListSectionEntry(id, expectedAnyInstanceOf) + } + + @GetMapping("/invalid-heading-size") + fun invalidHeadingSize(@RequestParam headingSize: Int) { + throw InvalidHeadingSize(headingSize) + } + + @GetMapping("/unrelated-literature-list-section") + fun unrelatedLiteratureListSection( + @RequestParam literatureListId: ThingId, + @RequestParam literatureListSectionId: ThingId + ) { + throw UnrelatedLiteratureListSection(literatureListId, literatureListSectionId) + } + + @GetMapping("/literature-list-section-type-mismatch-must-be-text-section") + fun literatureListSectionTypeMismatchMustBeTextSection() { + throw LiteratureListSectionTypeMismatch.mustBeTextSection() + } + + @GetMapping("/literature-list-section-type-mismatch-must-be-list-section") + fun literatureListSectionTypeMismatchMustBeListSection() { + throw LiteratureListSectionTypeMismatch.mustBeListSection() + } + + @GetMapping("/literature-list-not-modifiable") + fun literatureListNotModifiable(@RequestParam id: ThingId) { + throw LiteratureListNotModifiable(id) + } + } +} diff --git a/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/ComparisonControllerExceptionUnitTest.kt b/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/PaperExceptionUnitTest.kt similarity index 58% rename from content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/ComparisonControllerExceptionUnitTest.kt rename to content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/PaperExceptionUnitTest.kt index b04f47b57..14a0a464a 100644 --- a/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/ComparisonControllerExceptionUnitTest.kt +++ b/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/PaperExceptionUnitTest.kt @@ -1,12 +1,12 @@ -package org.orkg.contenttypes.adapter.input.rest +package org.orkg.contenttypes.adapter.input.rest.exceptions import org.hamcrest.Matchers.`is` import org.hamcrest.Matchers.notNullValue import org.junit.jupiter.api.Test import org.orkg.common.ThingId import org.orkg.common.exceptions.ExceptionHandler -import org.orkg.contenttypes.adapter.input.rest.ComparisonControllerExceptionUnitTest.FakeExceptionController -import org.orkg.contenttypes.domain.ComparisonAlreadyPublished +import org.orkg.contenttypes.adapter.input.rest.exceptions.PaperExceptionUnitTest.TestController +import org.orkg.contenttypes.domain.PaperNotModifiable import org.orkg.testing.configuration.FixedClockConfig import org.orkg.testing.spring.MockMvcBaseTest import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest @@ -20,30 +20,30 @@ import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController @WebMvcTest -@ContextConfiguration(classes = [FakeExceptionController::class, ExceptionHandler::class, FixedClockConfig::class]) -internal class ComparisonControllerExceptionUnitTest : MockMvcBaseTest("comparisons") { +@ContextConfiguration(classes = [TestController::class, ExceptionHandler::class, FixedClockConfig::class]) +internal class PaperExceptionUnitTest : MockMvcBaseTest("literature-lists") { @Test - fun comparisonAlreadyPublished() { + fun paperNotModifiable() { val id = "R123" - get("/comparison-already-published") + get("/paper-not-modifiable") .param("id", id) .perform() .andExpect(status().isForbidden) .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) .andExpect(jsonPath("$.error", `is`("Forbidden"))) - .andExpect(jsonPath("$.path").value("/comparison-already-published")) - .andExpect(jsonPath("$.message").value("""Comparison "$id" is already published.""")) + .andExpect(jsonPath("$.path").value("/paper-not-modifiable")) + .andExpect(jsonPath("$.message").value("""Paper "$id" is not modifiable.""")) .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) } @TestComponent @RestController - internal class FakeExceptionController { - @GetMapping("/comparison-already-published") - fun comparisonAlreadyPublished(@RequestParam id: ThingId) { - throw ComparisonAlreadyPublished(id) + internal class TestController { + @GetMapping("/paper-not-modifiable") + fun paperNotModifiable(@RequestParam id: ThingId) { + throw PaperNotModifiable(id) } } } diff --git a/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/RosettaStoneStatementControllerExceptionUnitTest.kt b/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/RosettaStoneStatementExceptionUnitTest.kt similarity index 97% rename from content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/RosettaStoneStatementControllerExceptionUnitTest.kt rename to content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/RosettaStoneStatementExceptionUnitTest.kt index f42c6ee59..8606d8976 100644 --- a/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/RosettaStoneStatementControllerExceptionUnitTest.kt +++ b/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/RosettaStoneStatementExceptionUnitTest.kt @@ -1,11 +1,11 @@ -package org.orkg.contenttypes.adapter.input.rest +package org.orkg.contenttypes.adapter.input.rest.exceptions import org.hamcrest.Matchers.`is` import org.hamcrest.Matchers.notNullValue import org.junit.jupiter.api.Test import org.orkg.common.ThingId import org.orkg.common.exceptions.ExceptionHandler -import org.orkg.contenttypes.adapter.input.rest.RosettaStoneStatementControllerExceptionUnitTest.FakeExceptionController +import org.orkg.contenttypes.adapter.input.rest.exceptions.RosettaStoneStatementExceptionUnitTest.TestController import org.orkg.contenttypes.domain.CannotDeleteIndividualRosettaStoneStatementVersion import org.orkg.contenttypes.domain.MissingInputPositions import org.orkg.contenttypes.domain.MissingObjectPositionValue @@ -32,8 +32,8 @@ import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController @WebMvcTest -@ContextConfiguration(classes = [FakeExceptionController::class, ExceptionHandler::class, FixedClockConfig::class]) -internal class RosettaStoneStatementControllerExceptionUnitTest : MockMvcBaseTest("rosetta-stone-statements") { +@ContextConfiguration(classes = [TestController::class, ExceptionHandler::class, FixedClockConfig::class]) +internal class RosettaStoneStatementExceptionUnitTest : MockMvcBaseTest("rosetta-stone-statements") { @Test fun tooManyInputPositions() { @@ -257,7 +257,7 @@ internal class RosettaStoneStatementControllerExceptionUnitTest : MockMvcBaseTes @TestComponent @RestController - internal class FakeExceptionController { + internal class TestController { @GetMapping("/too-many-input-positions") fun tooManyInputPositions(@RequestParam exceptedCount: Int, @RequestParam templateId: ThingId) { throw TooManyInputPositions(exceptedCount, templateId) diff --git a/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/RosettaStoneTemplateControllerExceptionUnitTest.kt b/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/RosettaStoneTemplateExceptionUnitTest.kt similarity index 80% rename from content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/RosettaStoneTemplateControllerExceptionUnitTest.kt rename to content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/RosettaStoneTemplateExceptionUnitTest.kt index 522ce5154..d3e2a923a 100644 --- a/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/RosettaStoneTemplateControllerExceptionUnitTest.kt +++ b/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/RosettaStoneTemplateExceptionUnitTest.kt @@ -1,14 +1,18 @@ -package org.orkg.contenttypes.adapter.input.rest +package org.orkg.contenttypes.adapter.input.rest.exceptions import org.hamcrest.Matchers.`is` import org.hamcrest.Matchers.notNullValue import org.junit.jupiter.api.Test import org.orkg.common.ThingId import org.orkg.common.exceptions.ExceptionHandler -import org.orkg.contenttypes.adapter.input.rest.RosettaStoneTemplateControllerExceptionUnitTest.FakeExceptionController +import org.orkg.contenttypes.adapter.input.rest.exceptions.RosettaStoneTemplateExceptionUnitTest.TestController import org.orkg.contenttypes.domain.InvalidObjectPositionPath +import org.orkg.contenttypes.domain.InvalidSubjectPositionCardinality import org.orkg.contenttypes.domain.InvalidSubjectPositionPath +import org.orkg.contenttypes.domain.InvalidSubjectPositionType import org.orkg.contenttypes.domain.MissingFormattedLabelPlaceholder +import org.orkg.contenttypes.domain.MissingPropertyPlaceholder +import org.orkg.contenttypes.domain.MissingSubjectPosition import org.orkg.contenttypes.domain.NewRosettaStoneTemplateExampleUsageMustStartWithPreviousExampleUsage import org.orkg.contenttypes.domain.NewRosettaStoneTemplateLabelSectionsMustBeOptional import org.orkg.contenttypes.domain.NewRosettaStoneTemplatePropertyMustBeOptional @@ -33,8 +37,8 @@ import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController @WebMvcTest -@ContextConfiguration(classes = [FakeExceptionController::class, ExceptionHandler::class, FixedClockConfig::class]) -internal class RosettaStoneTemplateControllerExceptionUnitTest : MockMvcBaseTest("rosetta-stone-templates") { +@ContextConfiguration(classes = [TestController::class, ExceptionHandler::class, FixedClockConfig::class]) +internal class RosettaStoneTemplateExceptionUnitTest : MockMvcBaseTest("rosetta-stone-templates") { @Test fun invalidSubjectPositionPath() { @@ -242,9 +246,60 @@ internal class RosettaStoneTemplateControllerExceptionUnitTest : MockMvcBaseTest .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) } + @Test + fun invalidSubjectPositionCardinality() { + get("/invalid-subject-position-cardinality") + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.path").value("/invalid-subject-position-cardinality")) + .andExpect(jsonPath("$.message").value("""Invalid subject position cardinality. Minimum cardinality must be at least one.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun invalidSubjectPositionType() { + get("/invalid-subject-position-type") + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.path").value("/invalid-subject-position-type")) + .andExpect(jsonPath("$.message").value("""Invalid subject position type. Subject position must not be a literal property.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun missingSubjectPosition() { + get("/missing-subject-position") + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.path").value("/missing-subject-position")) + .andExpect(jsonPath("$.message").value("""Missing subject position. There must be at least one property with path "${Predicates.hasSubjectPosition}" that has a minimum cardinality of at least one.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun missingPropertyPlaceholder() { + val index = "4" + + get("/missing-property-placeholder") + .param("index", index) + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.path").value("/missing-property-placeholder")) + .andExpect(jsonPath("$.message").value("""Missing placeholder for property at index "$index".""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + @TestComponent @RestController - internal class FakeExceptionController { + internal class TestController { @GetMapping("/invalid-subject-position-path") fun invalidSubjectPositionPath() { throw InvalidSubjectPositionPath() @@ -319,5 +374,25 @@ internal class RosettaStoneTemplateControllerExceptionUnitTest : MockMvcBaseTest fun newRosettaStoneTemplatePropertyMustBeOptional(@RequestParam placeholder: String) { throw NewRosettaStoneTemplatePropertyMustBeOptional(placeholder) } + + @GetMapping("/invalid-subject-position-cardinality") + fun invalidSubjectPositionCardinality() { + throw InvalidSubjectPositionCardinality() + } + + @GetMapping("/invalid-subject-position-type") + fun invalidSubjectPositionType() { + throw InvalidSubjectPositionType() + } + + @GetMapping("/missing-subject-position") + fun missingSubjectPosition() { + throw MissingSubjectPosition() + } + + @GetMapping("/missing-property-placeholder") + fun missingPropertyPlaceholder(@RequestParam index: Int) { + throw MissingPropertyPlaceholder(index) + } } } diff --git a/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/SmartReviewControllerExceptionUnitTest.kt b/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/SmartReviewExceptionUnitTest.kt similarity index 90% rename from content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/SmartReviewControllerExceptionUnitTest.kt rename to content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/SmartReviewExceptionUnitTest.kt index ac2c2de1a..75033200c 100644 --- a/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/SmartReviewControllerExceptionUnitTest.kt +++ b/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/SmartReviewExceptionUnitTest.kt @@ -1,15 +1,16 @@ -package org.orkg.contenttypes.adapter.input.rest +package org.orkg.contenttypes.adapter.input.rest.exceptions import org.hamcrest.Matchers.`is` import org.hamcrest.Matchers.notNullValue import org.junit.jupiter.api.Test import org.orkg.common.ThingId import org.orkg.common.exceptions.ExceptionHandler -import org.orkg.contenttypes.adapter.input.rest.SmartReviewControllerExceptionUnitTest.FakeExceptionController +import org.orkg.contenttypes.adapter.input.rest.exceptions.SmartReviewExceptionUnitTest.TestController import org.orkg.contenttypes.domain.InvalidSmartReviewTextSectionType import org.orkg.contenttypes.domain.OntologyEntityNotFound import org.orkg.contenttypes.domain.PublishedSmartReviewContentNotFound import org.orkg.contenttypes.domain.SmartReviewAlreadyPublished +import org.orkg.contenttypes.domain.SmartReviewNotModifiable import org.orkg.contenttypes.domain.SmartReviewSectionTypeMismatch import org.orkg.contenttypes.domain.UnrelatedSmartReviewSection import org.orkg.testing.configuration.FixedClockConfig @@ -25,8 +26,8 @@ import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController @WebMvcTest -@ContextConfiguration(classes = [FakeExceptionController::class, ExceptionHandler::class, FixedClockConfig::class]) -internal class SmartReviewControllerExceptionUnitTest : MockMvcBaseTest("smart-reviews") { +@ContextConfiguration(classes = [TestController::class, ExceptionHandler::class, FixedClockConfig::class]) +internal class SmartReviewExceptionUnitTest : MockMvcBaseTest("smart-reviews") { @Test fun publishedSmartReviewContentNotFound() { @@ -179,9 +180,24 @@ internal class SmartReviewControllerExceptionUnitTest : MockMvcBaseTest("smart-r .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) } + @Test + fun smartReviewNotModifiable() { + val id = "R123" + + get("/smart-review-not-modifiable") + .param("id", id) + .perform() + .andExpect(status().isForbidden) + .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) + .andExpect(jsonPath("$.error", `is`("Forbidden"))) + .andExpect(jsonPath("$.path").value("/smart-review-not-modifiable")) + .andExpect(jsonPath("$.message").value("""Smart review "$id" is not modifiable.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + @TestComponent @RestController - internal class FakeExceptionController { + internal class TestController { @GetMapping("/published-smart-review-content-not-found") fun publishedSmartReviewContentNotFound(@RequestParam smartReviewId: ThingId, @RequestParam contentId: ThingId) { throw PublishedSmartReviewContentNotFound(smartReviewId, contentId) @@ -239,5 +255,10 @@ internal class SmartReviewControllerExceptionUnitTest : MockMvcBaseTest("smart-r fun smartReviewAlreadyPublished(@RequestParam id: ThingId) { throw SmartReviewAlreadyPublished(id) } + + @GetMapping("/smart-review-not-modifiable") + fun smartReviewNotModifiable(@RequestParam id: ThingId) { + throw SmartReviewNotModifiable(id) + } } } diff --git a/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/TableControllerExceptionUnitTest.kt b/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/TableExceptionUnitTest.kt similarity index 81% rename from content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/TableControllerExceptionUnitTest.kt rename to content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/TableExceptionUnitTest.kt index f6e7c7e12..bc15e4641 100644 --- a/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/TableControllerExceptionUnitTest.kt +++ b/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/TableExceptionUnitTest.kt @@ -1,11 +1,11 @@ -package org.orkg.contenttypes.adapter.input.rest +package org.orkg.contenttypes.adapter.input.rest.exceptions import org.hamcrest.Matchers.`is` import org.hamcrest.Matchers.notNullValue import org.junit.jupiter.api.Test import org.orkg.common.ThingId import org.orkg.common.exceptions.ExceptionHandler -import org.orkg.contenttypes.adapter.input.rest.TableControllerExceptionUnitTest.FakeExceptionController +import org.orkg.contenttypes.adapter.input.rest.exceptions.TableExceptionUnitTest.TestController import org.orkg.contenttypes.domain.TableNotFound import org.orkg.testing.configuration.FixedClockConfig import org.orkg.testing.spring.MockMvcBaseTest @@ -20,8 +20,8 @@ import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController @WebMvcTest -@ContextConfiguration(classes = [FakeExceptionController::class, ExceptionHandler::class, FixedClockConfig::class]) -internal class TableControllerExceptionUnitTest : MockMvcBaseTest("tables") { +@ContextConfiguration(classes = [TestController::class, ExceptionHandler::class, FixedClockConfig::class]) +internal class TableExceptionUnitTest : MockMvcBaseTest("tables") { @Test fun tableNotFound() { @@ -40,7 +40,7 @@ internal class TableControllerExceptionUnitTest : MockMvcBaseTest("tables") { @TestComponent @RestController - internal class FakeExceptionController { + internal class TestController { @GetMapping("/table-not-found") fun tableNotFound(@RequestParam id: ThingId) { throw TableNotFound(id) diff --git a/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/ExceptionControllerUnitTest.kt b/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/TemplateInstanceExceptionUnitTest.kt similarity index 59% rename from content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/ExceptionControllerUnitTest.kt rename to content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/TemplateInstanceExceptionUnitTest.kt index d5581f081..23b212d19 100644 --- a/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/ExceptionControllerUnitTest.kt +++ b/content-types/content-types-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/contenttypes/adapter/input/rest/exceptions/TemplateInstanceExceptionUnitTest.kt @@ -1,31 +1,17 @@ -package org.orkg.contenttypes.adapter.input.rest +package org.orkg.contenttypes.adapter.input.rest.exceptions import org.hamcrest.Matchers.`is` import org.hamcrest.Matchers.notNullValue import org.junit.jupiter.api.Test import org.orkg.common.ThingId import org.orkg.common.exceptions.ExceptionHandler -import org.orkg.contenttypes.adapter.input.rest.ExceptionControllerUnitTest.FakeExceptionController -import org.orkg.contenttypes.domain.ComparisonNotModifiable -import org.orkg.contenttypes.domain.ComparisonRelatedFigureNotModifiable -import org.orkg.contenttypes.domain.ComparisonRelatedResourceNotModifiable -import org.orkg.contenttypes.domain.InvalidBibTeXReference +import org.orkg.contenttypes.adapter.input.rest.exceptions.TemplateInstanceExceptionUnitTest.TestController import org.orkg.contenttypes.domain.InvalidBounds import org.orkg.contenttypes.domain.InvalidDatatype -import org.orkg.contenttypes.domain.InvalidHeadingSize -import org.orkg.contenttypes.domain.InvalidListSectionEntry import org.orkg.contenttypes.domain.InvalidLiteral -import org.orkg.contenttypes.domain.InvalidMonth -import org.orkg.contenttypes.domain.InvalidSubjectPositionCardinality -import org.orkg.contenttypes.domain.InvalidSubjectPositionType import org.orkg.contenttypes.domain.LabelDoesNotMatchPattern -import org.orkg.contenttypes.domain.LiteratureListNotFound -import org.orkg.contenttypes.domain.LiteratureListNotModifiable -import org.orkg.contenttypes.domain.LiteratureListSectionTypeMismatch import org.orkg.contenttypes.domain.MismatchedDataType -import org.orkg.contenttypes.domain.MissingPropertyPlaceholder import org.orkg.contenttypes.domain.MissingPropertyValues -import org.orkg.contenttypes.domain.MissingSubjectPosition import org.orkg.contenttypes.domain.NumberTooHigh import org.orkg.contenttypes.domain.NumberTooLow import org.orkg.contenttypes.domain.ObjectIsNotAClass @@ -33,16 +19,11 @@ import org.orkg.contenttypes.domain.ObjectIsNotAList import org.orkg.contenttypes.domain.ObjectIsNotALiteral import org.orkg.contenttypes.domain.ObjectIsNotAPredicate import org.orkg.contenttypes.domain.ObjectMustNotBeALiteral -import org.orkg.contenttypes.domain.PaperNotModifiable import org.orkg.contenttypes.domain.ResourceIsNotAnInstanceOfTargetClass -import org.orkg.contenttypes.domain.SmartReviewNotModifiable -import org.orkg.contenttypes.domain.SustainableDevelopmentGoalNotFound import org.orkg.contenttypes.domain.TemplateNotApplicable import org.orkg.contenttypes.domain.TooManyPropertyValues import org.orkg.contenttypes.domain.UnknownTemplateProperties -import org.orkg.contenttypes.domain.UnrelatedLiteratureListSection import org.orkg.contenttypes.domain.UnrelatedTemplateProperty -import org.orkg.graph.domain.Predicates import org.orkg.testing.configuration.FixedClockConfig import org.orkg.testing.spring.MockMvcBaseTest import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest @@ -56,49 +37,8 @@ import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController @WebMvcTest -@ContextConfiguration(classes = [FakeExceptionController::class, ExceptionHandler::class, FixedClockConfig::class]) -internal class ExceptionControllerUnitTest : MockMvcBaseTest("exceptions") { - - @Test - fun invalidMonth() { - get("/invalid-month") - .param("month", "0") - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.path").value("/invalid-month")) - .andExpect(jsonPath("$.message").value("""Invalid month "0". Must be in range [1..12].""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun sustainableDevelopmentGoalNotFound() { - get("/sustainable-development-goal-not-found") - .param("sdgId", "SDG1") - .perform() - .andExpect(status().isNotFound) - .andExpect(jsonPath("$.status").value(HttpStatus.NOT_FOUND.value())) - .andExpect(jsonPath("$.error", `is`("Not Found"))) - .andExpect(jsonPath("$.path").value("/sustainable-development-goal-not-found")) - .andExpect(jsonPath("$.message").value("""Sustainable Development Goal "SDG1" not found.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun literatureListNotFound() { - val id = ThingId("R123") - - get("/errors/literature-list-not-found") - .param("id", id.value) - .perform() - .andExpect(status().isNotFound) - .andExpect(jsonPath("$.status").value(HttpStatus.NOT_FOUND.value())) - .andExpect(jsonPath("$.error").value("Not Found")) - .andExpect(jsonPath("$.path").value("/errors/literature-list-not-found")) - .andExpect(jsonPath("$.message").value("""Literature list "$id" not found.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } +@ContextConfiguration(classes = [TestController::class, ExceptionHandler::class, FixedClockConfig::class]) +internal class TemplateInstanceExceptionUnitTest : MockMvcBaseTest("tables") { @Test fun templateNotApplicable() { @@ -478,253 +418,9 @@ internal class ExceptionControllerUnitTest : MockMvcBaseTest("exceptions") { .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) } - @Test - fun paperNotModifiable() { - val id = "R123" - - get("/paper-not-modifiable") - .param("id", id) - .perform() - .andExpect(status().isForbidden) - .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) - .andExpect(jsonPath("$.error", `is`("Forbidden"))) - .andExpect(jsonPath("$.path").value("/paper-not-modifiable")) - .andExpect(jsonPath("$.message").value("""Paper "$id" is not modifiable.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun comparisonNotModifiable() { - val id = "R123" - - get("/comparison-not-modifiable") - .param("id", id) - .perform() - .andExpect(status().isForbidden) - .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) - .andExpect(jsonPath("$.error", `is`("Forbidden"))) - .andExpect(jsonPath("$.path").value("/comparison-not-modifiable")) - .andExpect(jsonPath("$.message").value("""Comparison "$id" is not modifiable.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun comparisonRelatedResourceNotModifiable() { - val id = "R123" - - get("/comparison-related-resource-not-modifiable") - .param("id", id) - .perform() - .andExpect(status().isForbidden) - .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) - .andExpect(jsonPath("$.error", `is`("Forbidden"))) - .andExpect(jsonPath("$.path").value("/comparison-related-resource-not-modifiable")) - .andExpect(jsonPath("$.message").value("""Comparison related resource "$id" is not modifiable.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun comparisonRelatedFigureNotModifiable() { - val id = "R123" - - get("/comparison-related-figure-not-modifiable") - .param("id", id) - .perform() - .andExpect(status().isForbidden) - .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) - .andExpect(jsonPath("$.error", `is`("Forbidden"))) - .andExpect(jsonPath("$.path").value("/comparison-related-figure-not-modifiable")) - .andExpect(jsonPath("$.message").value("""Comparison related figure "$id" is not modifiable.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun literatureListNotModifiable() { - val id = "R123" - - get("/literature-list-not-modifiable") - .param("id", id) - .perform() - .andExpect(status().isForbidden) - .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) - .andExpect(jsonPath("$.error", `is`("Forbidden"))) - .andExpect(jsonPath("$.path").value("/literature-list-not-modifiable")) - .andExpect(jsonPath("$.message").value("""Literature list "$id" is not modifiable.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun smartReviewNotModifiable() { - val id = "R123" - - get("/smart-review-not-modifiable") - .param("id", id) - .perform() - .andExpect(status().isForbidden) - .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) - .andExpect(jsonPath("$.error", `is`("Forbidden"))) - .andExpect(jsonPath("$.path").value("/smart-review-not-modifiable")) - .andExpect(jsonPath("$.message").value("""Smart review "$id" is not modifiable.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun invalidListSectionEntry() { - val id = "R123" - val expectedAnyInstanceOf = arrayOf("C1", "C2") - - get("/invalid-list-section-entry") - .param("id", id) - .param("expectedAnyInstanceOf", *expectedAnyInstanceOf) - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.path").value("/invalid-list-section-entry")) - .andExpect(jsonPath("$.message").value("""Invalid list section entry "$id". Must be an instance of either "C1", "C2".""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun invalidHeadingSize() { - val headingSize = "5" - - get("/invalid-heading-size") - .param("headingSize", headingSize) - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.path").value("/invalid-heading-size")) - .andExpect(jsonPath("$.message").value("""Invalid heading size "$headingSize". Must be at least 1.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun unrelatedLiteratureListSection() { - val literatureListId = "R123" - val literatureListSectionId = "R456" - - get("/unrelated-literature-list-section") - .param("literatureListId", literatureListId) - .param("literatureListSectionId", literatureListSectionId) - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.path").value("/unrelated-literature-list-section")) - .andExpect(jsonPath("$.message").value("""Literature list section "$literatureListSectionId" does not belong to literature list "$literatureListId".""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun literatureListSectionTypeMismatchMustBeTextSection() { - get("/literature-list-section-type-mismatch-must-be-text-section") - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.path").value("/literature-list-section-type-mismatch-must-be-text-section")) - .andExpect(jsonPath("$.message").value("""Invalid literature list section type. Must be a text section.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun literatureListSectionTypeMismatchMustBeListSection() { - get("/literature-list-section-type-mismatch-must-be-list-section") - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.path").value("/literature-list-section-type-mismatch-must-be-list-section")) - .andExpect(jsonPath("$.message").value("""Invalid literature list section type. Must be a list section.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun invalidSubjectPositionCardinality() { - get("/invalid-subject-position-cardinality") - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.path").value("/invalid-subject-position-cardinality")) - .andExpect(jsonPath("$.message").value("""Invalid subject position cardinality. Minimum cardinality must be at least one.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun invalidSubjectPositionType() { - get("/invalid-subject-position-type") - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.path").value("/invalid-subject-position-type")) - .andExpect(jsonPath("$.message").value("""Invalid subject position type. Subject position must not be a literal property.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun missingSubjectPosition() { - get("/missing-subject-position") - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.path").value("/missing-subject-position")) - .andExpect(jsonPath("$.message").value("""Missing subject position. There must be at least one property with path "${Predicates.hasSubjectPosition}" that has a minimum cardinality of at least one.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun missingPropertyPlaceholder() { - val index = "4" - - get("/missing-property-placeholder") - .param("index", index) - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.path").value("/missing-property-placeholder")) - .andExpect(jsonPath("$.message").value("""Missing placeholder for property at index "$index".""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun invalidBibTeXReference() { - val reference = "not bibtex" - - get("/invalid-bibtex-reference") - .param("reference", reference) - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.path").value("/invalid-bibtex-reference")) - .andExpect(jsonPath("$.message").value("""Invalid BibTeX reference "$reference".""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - @TestComponent @RestController - internal class FakeExceptionController { - @GetMapping("/invalid-month") - fun invalidMonth(@RequestParam month: Int) { - throw InvalidMonth(month) - } - - @GetMapping("/sustainable-development-goal-not-found") - fun sustainableDevelopmentGoalNotFound(@RequestParam sdgId: ThingId) { - throw SustainableDevelopmentGoalNotFound(sdgId) - } - - @GetMapping("/errors/literature-list-not-found") - fun literatureListNotFound(@RequestParam id: ThingId) { - throw LiteratureListNotFound(id) - } - + internal class TestController { @GetMapping("/template-not-applicable") fun templateNotApplicable(@RequestParam templateId: ThingId, @RequestParam id: ThingId) { throw TemplateNotApplicable(templateId, id) @@ -899,88 +595,5 @@ internal class ExceptionControllerUnitTest : MockMvcBaseTest("exceptions") { ) { throw NumberTooHigh(templatePropertyId, objectId, predicateId, label, maxInclusive) } - - @GetMapping("/paper-not-modifiable") - fun paperNotModifiable(@RequestParam id: ThingId) { - throw PaperNotModifiable(id) - } - - @GetMapping("/comparison-not-modifiable") - fun comparisonNotModifiable(@RequestParam id: ThingId) { - throw ComparisonNotModifiable(id) - } - - @GetMapping("/comparison-related-resource-not-modifiable") - fun comparisonRelatedResourceNotModifiable(@RequestParam id: ThingId) { - throw ComparisonRelatedResourceNotModifiable(id) - } - - @GetMapping("/comparison-related-figure-not-modifiable") - fun comparisonRelatedFigureNotModifiable(@RequestParam id: ThingId) { - throw ComparisonRelatedFigureNotModifiable(id) - } - - @GetMapping("/literature-list-not-modifiable") - fun literatureListNotModifiable(@RequestParam id: ThingId) { - throw LiteratureListNotModifiable(id) - } - - @GetMapping("/smart-review-not-modifiable") - fun smartReviewNotModifiable(@RequestParam id: ThingId) { - throw SmartReviewNotModifiable(id) - } - - @GetMapping("/invalid-list-section-entry") - fun invalidListSectionEntry(@RequestParam id: ThingId, @RequestParam expectedAnyInstanceOf: Set) { - throw InvalidListSectionEntry(id, expectedAnyInstanceOf) - } - - @GetMapping("/invalid-heading-size") - fun invalidHeadingSize(@RequestParam headingSize: Int) { - throw InvalidHeadingSize(headingSize) - } - - @GetMapping("/unrelated-literature-list-section") - fun unrelatedLiteratureListSection( - @RequestParam literatureListId: ThingId, - @RequestParam literatureListSectionId: ThingId - ) { - throw UnrelatedLiteratureListSection(literatureListId, literatureListSectionId) - } - - @GetMapping("/literature-list-section-type-mismatch-must-be-text-section") - fun literatureListSectionTypeMismatchMustBeTextSection() { - throw LiteratureListSectionTypeMismatch.mustBeTextSection() - } - - @GetMapping("/literature-list-section-type-mismatch-must-be-list-section") - fun literatureListSectionTypeMismatchMustBeListSection() { - throw LiteratureListSectionTypeMismatch.mustBeListSection() - } - - @GetMapping("/invalid-subject-position-cardinality") - fun invalidSubjectPositionCardinality() { - throw InvalidSubjectPositionCardinality() - } - - @GetMapping("/invalid-subject-position-type") - fun invalidSubjectPositionType() { - throw InvalidSubjectPositionType() - } - - @GetMapping("/missing-subject-position") - fun missingSubjectPosition() { - throw MissingSubjectPosition() - } - - @GetMapping("/missing-property-placeholder") - fun missingPropertyPlaceholder(@RequestParam index: Int) { - throw MissingPropertyPlaceholder(index) - } - - @GetMapping("/invalid-bibtex-reference") - fun invalidBibTeXReference(@RequestParam reference: String) { - throw InvalidBibTeXReference(reference) - } } } diff --git a/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/ExceptionControllerUnitTest.kt b/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/ExceptionControllerUnitTest.kt deleted file mode 100644 index 6f638ed8e..000000000 --- a/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/ExceptionControllerUnitTest.kt +++ /dev/null @@ -1,642 +0,0 @@ -package org.orkg.graph.adapter.input.rest - -import org.eclipse.rdf4j.common.net.ParsedIRI -import org.hamcrest.Matchers.`is` -import org.hamcrest.Matchers.notNullValue -import org.junit.jupiter.api.Test -import org.orkg.common.ContributorId -import org.orkg.common.ThingId -import org.orkg.common.exceptions.ExceptionHandler -import org.orkg.graph.adapter.input.rest.ExceptionControllerUnitTest.FakeExceptionController -import org.orkg.graph.domain.CannotResetURI -import org.orkg.graph.domain.ClassAlreadyExists -import org.orkg.graph.domain.ClassNotAllowed -import org.orkg.graph.domain.ClassNotModifiable -import org.orkg.graph.domain.Classes -import org.orkg.graph.domain.ExternalClassNotFound -import org.orkg.graph.domain.ExternalPredicateNotFound -import org.orkg.graph.domain.ExternalResourceNotFound -import org.orkg.graph.domain.InvalidDescription -import org.orkg.graph.domain.InvalidLabel -import org.orkg.graph.domain.InvalidLiteralLabel -import org.orkg.graph.domain.InvalidStatement -import org.orkg.graph.domain.ListInUse -import org.orkg.graph.domain.LiteralNotModifiable -import org.orkg.graph.domain.MAX_LABEL_LENGTH -import org.orkg.graph.domain.NeitherOwnerNorCurator -import org.orkg.graph.domain.NotACurator -import org.orkg.graph.domain.PredicateNotModifiable -import org.orkg.graph.domain.ResourceNotModifiable -import org.orkg.graph.domain.StatementAlreadyExists -import org.orkg.graph.domain.StatementId -import org.orkg.graph.domain.StatementNotModifiable -import org.orkg.graph.domain.ThingAlreadyExists -import org.orkg.graph.domain.URIAlreadyInUse -import org.orkg.graph.domain.URINotAbsolute -import org.orkg.testing.MockUserId -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.jsonPath -import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.RequestParam -import org.springframework.web.bind.annotation.RestController - -@WebMvcTest -@ContextConfiguration(classes = [FakeExceptionController::class, ExceptionHandler::class, FixedClockConfig::class]) -internal class ExceptionControllerUnitTest : MockMvcBaseTest("exceptions") { - - @Test - fun resourceNotModifiable() { - val id = ThingId("R123") - - get("/resource-not-modifiable") - .param("id", id.value) - .perform() - .andExpect(status().isForbidden) - .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) - .andExpect(jsonPath("$.error", `is`("Forbidden"))) - .andExpect(jsonPath("$.path").value("/resource-not-modifiable")) - .andExpect(jsonPath("$.message").value("""Resource "$id" is not modifiable.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun classNotModifiable() { - val id = ThingId("R123") - - get("/class-not-modifiable") - .param("id", id.value) - .perform() - .andExpect(status().isForbidden) - .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) - .andExpect(jsonPath("$.error", `is`("Forbidden"))) - .andExpect(jsonPath("$.path").value("/class-not-modifiable")) - .andExpect(jsonPath("$.message").value("""Class "$id" is not modifiable.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun predicateNotModifiable() { - val id = ThingId("R123") - - get("/predicate-not-modifiable") - .param("id", id.value) - .perform() - .andExpect(status().isForbidden) - .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) - .andExpect(jsonPath("$.error", `is`("Forbidden"))) - .andExpect(jsonPath("$.path").value("/predicate-not-modifiable")) - .andExpect(jsonPath("$.message").value("""Predicate "$id" is not modifiable.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun literalNotModifiable() { - val id = ThingId("R123") - - get("/literal-not-modifiable") - .param("id", id.value) - .perform() - .andExpect(status().isForbidden) - .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) - .andExpect(jsonPath("$.error", `is`("Forbidden"))) - .andExpect(jsonPath("$.path").value("/literal-not-modifiable")) - .andExpect(jsonPath("$.message").value("""Literal "$id" is not modifiable.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun statementNotModifiable() { - val id = StatementId("S123") - - get("/statement-not-modifiable") - .param("id", id.value) - .perform() - .andExpect(status().isForbidden) - .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) - .andExpect(jsonPath("$.error", `is`("Forbidden"))) - .andExpect(jsonPath("$.path").value("/statement-not-modifiable")) - .andExpect(jsonPath("$.message").value("""Statement "$id" is not modifiable.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun invalidLabel() { - get("/invalid-label") - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.errors.length()").value(1)) - .andExpect(jsonPath("$.errors[0].field").value("label")) - .andExpect(jsonPath("$.errors[0].message").value("""A label must not be blank or contain newlines and must be at most $MAX_LABEL_LENGTH characters long.""")) - .andExpect(jsonPath("$.path").value("/invalid-label")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun invalidLabelWithProperty() { - get("/invalid-label") - .param("property", "title") - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.errors.length()").value(1)) - .andExpect(jsonPath("$.errors[0].field").value("title")) - .andExpect(jsonPath("$.errors[0].message").value("""A label must not be blank or contain newlines and must be at most $MAX_LABEL_LENGTH characters long.""")) - .andExpect(jsonPath("$.path").value("/invalid-label")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun invalidDescription() { - get("/invalid-description") - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.errors.length()").value(1)) - .andExpect(jsonPath("$.errors[0].field").value("description")) - .andExpect(jsonPath("$.errors[0].message").value("""A description must not be blank and must be at most $MAX_LABEL_LENGTH characters long.""")) - .andExpect(jsonPath("$.path").value("/invalid-description")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun invalidDescriptionWithProperty() { - get("/invalid-description") - .param("property", "contents") - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.errors.length()").value(1)) - .andExpect(jsonPath("$.errors[0].field").value("contents")) - .andExpect(jsonPath("$.errors[0].message").value("""A description must not be blank and must be at most $MAX_LABEL_LENGTH characters long.""")) - .andExpect(jsonPath("$.path").value("/invalid-description")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun uriAlreadyInUse() { - val id = ThingId("C123") - val uri = "https://example.org/C123" - - get("/uri-already-in-use") - .param("id", id.value) - .param("uri", uri) - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.errors.length()").value(1)) - .andExpect(jsonPath("$.errors[0].field").value("uri")) - .andExpect(jsonPath("$.errors[0].message").value("""The URI <$uri> is already assigned to class with ID "$id".""")) - .andExpect(jsonPath("$.path").value("/uri-already-in-use")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun uriNotAbsolute() { - val uri = "invalid" - - get("/uri-not-absolute") - .param("uri", uri) - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.errors.length()").value(1)) - .andExpect(jsonPath("$.errors[0].field").value("uri")) - .andExpect(jsonPath("$.errors[0].message").value("""The URI <$uri> is not absolute.""")) - .andExpect(jsonPath("$.path").value("/uri-not-absolute")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun classNotAllowed() { - val id = Classes.list - - get("/class-not-allowed") - .param("id", id.value) - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.path").value("/class-not-allowed")) - .andExpect(jsonPath("$.message").value("""Class id "$id" is not allowed.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun classAlreadyExists() { - val id = Classes.list - - get("/class-already-exists") - .param("id", id.value) - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.path").value("/class-already-exists")) - .andExpect(jsonPath("$.message").value("""Class "$id" already exists.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun cannotResetURI() { - val id = ThingId("C123") - - get("/cannot-reset-uri") - .param("id", id.value) - .perform() - .andExpect(status().isForbidden) - .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) - .andExpect(jsonPath("$.error", `is`("Forbidden"))) - .andExpect(jsonPath("$.path").value("/cannot-reset-uri")) - .andExpect(jsonPath("$.errors.length()").value(1)) - .andExpect(jsonPath("$.errors[0].field").value("uri")) - .andExpect(jsonPath("$.errors[0].message").value("""The class "$id" already has a URI. It is not allowed to change URIs.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun neitherOwnerNorCuratorDelete() { - val contributorId = ContributorId(MockUserId.USER) - - get("/neither-owner-nor-creator-delete") - .param("contributorId", contributorId.toString()) - .perform() - .andExpect(status().isForbidden) - .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) - .andExpect(jsonPath("$.error", `is`("Forbidden"))) - .andExpect(jsonPath("$.path").value("/neither-owner-nor-creator-delete")) - .andExpect(jsonPath("$.message").value("""Contributor <$contributorId> does not own the entity to be deleted and is not a curator.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun neitherOwnerNorCuratorVisibility() { - val id = "R123" - - get("/neither-owner-nor-creator-visibility") - .param("id", id) - .perform() - .andExpect(status().isForbidden) - .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) - .andExpect(jsonPath("$.error", `is`("Forbidden"))) - .andExpect(jsonPath("$.path").value("/neither-owner-nor-creator-visibility")) - .andExpect(jsonPath("$.message").value("""Insufficient permissions to change visibility of entity "$id".""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun invalidStatementIsListElement() { - get("/invalid-statement-is-list-element") - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.path").value("/invalid-statement-is-list-element")) - .andExpect(jsonPath("$.message").value("A list element statement cannot be managed using the statements endpoint. Please see the documentation on how to manage lists.")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun invalidStatementSubjectMustNotBeLiteral() { - get("/invalid-statement-subject-must-not-be-literal") - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.path").value("/invalid-statement-subject-must-not-be-literal")) - .andExpect(jsonPath("$.message").value("Subject must not be a literal.")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun invalidStatementRosettaStoneStatementResource() { - get("/invalid-statement-rosetta-stone-statement-resource") - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.path").value("/invalid-statement-rosetta-stone-statement-resource")) - .andExpect(jsonPath("$.message").value("A rosetta stone statement resource cannot be managed using statements endpoint. Please see the documentation on how to manage rosetta stone statements.")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun thingAlreadyExists() { - val id = "R123" - - get("/thing-already-exists") - .param("id", id) - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.path").value("/thing-already-exists")) - .andExpect(jsonPath("$.message").value("""A thing with id "$id" already exists.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun listInUse() { - val id = "R123" - - get("/list-in-use") - .param("id", id) - .perform() - .andExpect(status().isForbidden) - .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) - .andExpect(jsonPath("$.error", `is`("Forbidden"))) - .andExpect(jsonPath("$.path").value("/list-in-use")) - .andExpect(jsonPath("$.message").value("""Unable to delete list "$id" because it is used in at least one statement.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun externalResourceNotFound() { - val id = "R123" - val ontologyId = "skos" - - get("/external-resource-not-found") - .param("id", id) - .param("ontologyId", ontologyId) - .perform() - .andExpect(status().isNotFound) - .andExpect(jsonPath("$.status").value(HttpStatus.NOT_FOUND.value())) - .andExpect(jsonPath("$.error", `is`("Not Found"))) - .andExpect(jsonPath("$.path").value("/external-resource-not-found")) - .andExpect(jsonPath("$.message").value("""External resource "$id" for ontology "$ontologyId" not found.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun externalPredicateNotFound() { - val id = "R123" - val ontologyId = "skos" - - get("/external-predicate-not-found") - .param("id", id) - .param("ontologyId", ontologyId) - .perform() - .andExpect(status().isNotFound) - .andExpect(jsonPath("$.status").value(HttpStatus.NOT_FOUND.value())) - .andExpect(jsonPath("$.error", `is`("Not Found"))) - .andExpect(jsonPath("$.path").value("/external-predicate-not-found")) - .andExpect(jsonPath("$.message").value("""External predicate "$id" for ontology "$ontologyId" not found.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun externalClassNotFound() { - val id = "R123" - val ontologyId = "skos" - - get("/external-class-not-found") - .param("id", id) - .param("ontologyId", ontologyId) - .perform() - .andExpect(status().isNotFound) - .andExpect(jsonPath("$.status").value(HttpStatus.NOT_FOUND.value())) - .andExpect(jsonPath("$.error", `is`("Not Found"))) - .andExpect(jsonPath("$.path").value("/external-class-not-found")) - .andExpect(jsonPath("$.message").value("""External class "$id" for ontology "$ontologyId" not found.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun notACurator() { - val contributorId = "62bfeea3-4210-436d-b953-effa5a07ed64" - - get("/not-a-curator") - .param("contributorId", contributorId) - .perform() - .andExpect(status().isForbidden) - .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) - .andExpect(jsonPath("$.error", `is`("Forbidden"))) - .andExpect(jsonPath("$.path").value("/not-a-curator")) - .andExpect(jsonPath("$.message").value("""Contributor <$contributorId> is not a curator.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun notACuratorCannotChangeVerifiedStatus() { - val contributorId = "62bfeea3-4210-436d-b953-effa5a07ed64" - - get("/not-a-curator-cannot-change-verified-status") - .param("contributorId", contributorId) - .perform() - .andExpect(status().isForbidden) - .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) - .andExpect(jsonPath("$.error", `is`("Forbidden"))) - .andExpect(jsonPath("$.path").value("/not-a-curator-cannot-change-verified-status")) - .andExpect(jsonPath("$.message").value("""Cannot change verified status: Contributor <$contributorId> is not a curator.""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun invalidLiteralLabelTooLong() { - get("/invalid-literal-label-too-long") - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.errors.length()").value(1)) - .andExpect(jsonPath("$.errors[0].field").value("label")) - .andExpect(jsonPath("$.errors[0].message").value("A literal must be at most $MAX_LABEL_LENGTH characters long.")) - .andExpect(jsonPath("$.path").value("/invalid-literal-label-too-long")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun invalidLiteralLabelConstraintViolation() { - val label = "not a number" - val datatype = "xsd:decimal" - - get("/invalid-literal-label-constraint-violation") - .param("label", label) - .param("datatype", datatype) - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.errors.length()").value(1)) - .andExpect(jsonPath("$.errors[0].field").value("label")) - .andExpect(jsonPath("$.errors[0].message").value("""Literal value "$label" is not a valid "$datatype".""")) - .andExpect(jsonPath("$.path").value("/invalid-literal-label-constraint-violation")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @Test - fun statementAlreadyExists() { - val id = StatementId("S4565") - - get("/statement-already-exists") - .param("id", id.value) - .perform() - .andExpect(status().isBadRequest) - .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) - .andExpect(jsonPath("$.error", `is`("Bad Request"))) - .andExpect(jsonPath("$.path").value("/statement-already-exists")) - .andExpect(jsonPath("$.message").value("""Statement already exists with id "$id".""")) - .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) - } - - @TestComponent - @RestController - internal class FakeExceptionController { - @GetMapping("/resource-not-modifiable") - fun resourceNotModifiable(@RequestParam id: ThingId) { - throw ResourceNotModifiable(id) - } - - @GetMapping("/class-not-modifiable") - fun classNotModifiable(@RequestParam id: ThingId) { - throw ClassNotModifiable(id) - } - - @GetMapping("/predicate-not-modifiable") - fun predicateNotModifiable(@RequestParam id: ThingId) { - throw PredicateNotModifiable(id) - } - - @GetMapping("/literal-not-modifiable") - fun literalNotModifiable(@RequestParam id: ThingId) { - throw LiteralNotModifiable(id) - } - - @GetMapping("/statement-not-modifiable") - fun statementNotModifiable(@RequestParam id: StatementId) { - throw StatementNotModifiable(id) - } - - @GetMapping("/invalid-label") - fun invalidLabel() { - throw InvalidLabel() - } - - @GetMapping("/invalid-label", params = ["property"]) - fun invalidLabel(@RequestParam property: String) { - throw InvalidLabel(property) - } - - @GetMapping("/invalid-description") - fun invalidInvalidDescription() { - throw InvalidDescription() - } - - @GetMapping("/invalid-description", params = ["property"]) - fun invalidInvalidDescription(@RequestParam property: String) { - throw InvalidDescription(property) - } - - @GetMapping("/uri-already-in-use") - fun uriAlreadyInUse(@RequestParam id: ThingId, @RequestParam uri: ParsedIRI) { - throw URIAlreadyInUse(uri, id) - } - - @GetMapping("/uri-not-absolute") - fun uriNotAbsolute(@RequestParam uri: ParsedIRI) { - throw URINotAbsolute(uri) - } - - @GetMapping("/class-not-allowed") - fun classNotAllowed(@RequestParam id: ThingId) { - throw ClassNotAllowed(id) - } - - @GetMapping("/class-already-exists") - fun classAlreadyExists(@RequestParam id: ThingId) { - throw ClassAlreadyExists(id) - } - - @GetMapping("/cannot-reset-uri") - fun cannotResetURI(@RequestParam id: ThingId) { - throw CannotResetURI(id) - } - - @GetMapping("/neither-owner-nor-creator-delete") - fun neitherOwnerNorCuratorDelete(@RequestParam contributorId: ContributorId) { - throw NeitherOwnerNorCurator(contributorId) - } - - @GetMapping("/neither-owner-nor-creator-visibility") - fun neitherOwnerNorCuratorVisibility(@RequestParam id: ThingId) { - throw NeitherOwnerNorCurator.cannotChangeVisibility(id) - } - - @GetMapping("/invalid-statement-is-list-element") - fun invalidStatementIsListElement() { - throw InvalidStatement.isListElementStatement() - } - - @GetMapping("/invalid-statement-subject-must-not-be-literal") - fun invalidStatementSubjectMustNotBeLiteral() { - throw InvalidStatement.subjectMustNotBeLiteral() - } - - @GetMapping("/invalid-statement-rosetta-stone-statement-resource") - fun invalidStatementRosettaStoneStatementResource() { - throw InvalidStatement.includesRosettaStoneStatementResource() - } - - @GetMapping("/thing-already-exists") - fun thingAlreadyExists(@RequestParam id: ThingId) { - throw ThingAlreadyExists(id) - } - - @GetMapping("/list-in-use") - fun listInUse(@RequestParam id: ThingId) { - throw ListInUse(id) - } - - @GetMapping("/external-resource-not-found") - fun externalResourceNotFound(@RequestParam ontologyId: String, @RequestParam id: String) { - throw ExternalResourceNotFound(ontologyId, id) - } - - @GetMapping("/external-predicate-not-found") - fun externalPredicateNotFound(@RequestParam ontologyId: String, @RequestParam id: String) { - throw ExternalPredicateNotFound(ontologyId, id) - } - - @GetMapping("/external-class-not-found") - fun externalClassNotFound(@RequestParam ontologyId: String, @RequestParam id: String) { - throw ExternalClassNotFound(ontologyId, id) - } - - @GetMapping("/not-a-curator") - fun notACurator(@RequestParam contributorId: ContributorId) { - throw NotACurator(contributorId) - } - - @GetMapping("/not-a-curator-cannot-change-verified-status") - fun notACuratorCannotChangeVerifiedStatus(@RequestParam contributorId: ContributorId) { - throw NotACurator.cannotChangeVerifiedStatus(contributorId) - } - - @GetMapping("/invalid-literal-label-too-long") - fun invalidLiteralLabelTooLong() { - throw InvalidLiteralLabel() - } - - @GetMapping("/invalid-literal-label-constraint-violation") - fun invalidLiteralLabelConstraintViolation(@RequestParam label: String, @RequestParam datatype: String) { - throw InvalidLiteralLabel(label, datatype) - } - - @GetMapping("/statement-already-exists") - fun statementAlreadyExists(@RequestParam id: StatementId) { - throw StatementAlreadyExists(id) - } - } -} diff --git a/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/exceptions/ClassExceptionUnitTest.kt b/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/exceptions/ClassExceptionUnitTest.kt new file mode 100644 index 000000000..11ffe45ce --- /dev/null +++ b/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/exceptions/ClassExceptionUnitTest.kt @@ -0,0 +1,187 @@ +package org.orkg.graph.adapter.input.rest.exceptions + +import org.eclipse.rdf4j.common.net.ParsedIRI +import org.hamcrest.Matchers.`is` +import org.hamcrest.Matchers.notNullValue +import org.junit.jupiter.api.Test +import org.orkg.common.ThingId +import org.orkg.common.exceptions.ExceptionHandler +import org.orkg.graph.adapter.input.rest.exceptions.ClassExceptionUnitTest.TestController +import org.orkg.graph.domain.CannotResetURI +import org.orkg.graph.domain.ClassAlreadyExists +import org.orkg.graph.domain.ClassNotAllowed +import org.orkg.graph.domain.ClassNotModifiable +import org.orkg.graph.domain.Classes +import org.orkg.graph.domain.ExternalClassNotFound +import org.orkg.graph.domain.URIAlreadyInUse +import org.orkg.graph.domain.URINotAbsolute +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.jsonPath +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController + +@WebMvcTest +@ContextConfiguration(classes = [TestController::class, ExceptionHandler::class, FixedClockConfig::class]) +internal class ClassExceptionUnitTest : MockMvcBaseTest("exceptions") { + + @Test + fun classNotModifiable() { + val id = ThingId("R123") + + get("/class-not-modifiable") + .param("id", id.value) + .perform() + .andExpect(status().isForbidden) + .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) + .andExpect(jsonPath("$.error", `is`("Forbidden"))) + .andExpect(jsonPath("$.path").value("/class-not-modifiable")) + .andExpect(jsonPath("$.message").value("""Class "$id" is not modifiable.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun uriAlreadyInUse() { + val id = ThingId("C123") + val uri = "https://example.org/C123" + + get("/uri-already-in-use") + .param("id", id.value) + .param("uri", uri) + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.errors.length()").value(1)) + .andExpect(jsonPath("$.errors[0].field").value("uri")) + .andExpect(jsonPath("$.errors[0].message").value("""The URI <$uri> is already assigned to class with ID "$id".""")) + .andExpect(jsonPath("$.path").value("/uri-already-in-use")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun uriNotAbsolute() { + val uri = "invalid" + + get("/uri-not-absolute") + .param("uri", uri) + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.errors.length()").value(1)) + .andExpect(jsonPath("$.errors[0].field").value("uri")) + .andExpect(jsonPath("$.errors[0].message").value("""The URI <$uri> is not absolute.""")) + .andExpect(jsonPath("$.path").value("/uri-not-absolute")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun classNotAllowed() { + val id = Classes.list + + get("/class-not-allowed") + .param("id", id.value) + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.path").value("/class-not-allowed")) + .andExpect(jsonPath("$.message").value("""Class id "$id" is not allowed.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun classAlreadyExists() { + val id = Classes.list + + get("/class-already-exists") + .param("id", id.value) + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.path").value("/class-already-exists")) + .andExpect(jsonPath("$.message").value("""Class "$id" already exists.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun cannotResetURI() { + val id = ThingId("C123") + + get("/cannot-reset-uri") + .param("id", id.value) + .perform() + .andExpect(status().isForbidden) + .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) + .andExpect(jsonPath("$.error", `is`("Forbidden"))) + .andExpect(jsonPath("$.path").value("/cannot-reset-uri")) + .andExpect(jsonPath("$.errors.length()").value(1)) + .andExpect(jsonPath("$.errors[0].field").value("uri")) + .andExpect(jsonPath("$.errors[0].message").value("""The class "$id" already has a URI. It is not allowed to change URIs.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun externalClassNotFound() { + val id = "R123" + val ontologyId = "skos" + + get("/external-class-not-found") + .param("id", id) + .param("ontologyId", ontologyId) + .perform() + .andExpect(status().isNotFound) + .andExpect(jsonPath("$.status").value(HttpStatus.NOT_FOUND.value())) + .andExpect(jsonPath("$.error", `is`("Not Found"))) + .andExpect(jsonPath("$.path").value("/external-class-not-found")) + .andExpect(jsonPath("$.message").value("""External class "$id" for ontology "$ontologyId" not found.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @TestComponent + @RestController + internal class TestController { + @GetMapping("/class-not-modifiable") + fun classNotModifiable(@RequestParam id: ThingId) { + throw ClassNotModifiable(id) + } + + @GetMapping("/uri-already-in-use") + fun uriAlreadyInUse(@RequestParam id: ThingId, @RequestParam uri: ParsedIRI) { + throw URIAlreadyInUse(uri, id) + } + + @GetMapping("/uri-not-absolute") + fun uriNotAbsolute(@RequestParam uri: ParsedIRI) { + throw URINotAbsolute(uri) + } + + @GetMapping("/class-not-allowed") + fun classNotAllowed(@RequestParam id: ThingId) { + throw ClassNotAllowed(id) + } + + @GetMapping("/class-already-exists") + fun classAlreadyExists(@RequestParam id: ThingId) { + throw ClassAlreadyExists(id) + } + + @GetMapping("/cannot-reset-uri") + fun cannotResetURI(@RequestParam id: ThingId) { + throw CannotResetURI(id) + } + + @GetMapping("/external-class-not-found") + fun externalClassNotFound(@RequestParam ontologyId: String, @RequestParam id: String) { + throw ExternalClassNotFound(ontologyId, id) + } + } +} diff --git a/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/exceptions/CommonExceptionUnitTest.kt b/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/exceptions/CommonExceptionUnitTest.kt new file mode 100644 index 000000000..591650f9b --- /dev/null +++ b/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/exceptions/CommonExceptionUnitTest.kt @@ -0,0 +1,194 @@ +package org.orkg.graph.adapter.input.rest.exceptions + +import org.hamcrest.Matchers.`is` +import org.hamcrest.Matchers.notNullValue +import org.junit.jupiter.api.Test +import org.orkg.common.ContributorId +import org.orkg.common.ThingId +import org.orkg.common.exceptions.ExceptionHandler +import org.orkg.graph.adapter.input.rest.exceptions.CommonExceptionUnitTest.TestController +import org.orkg.graph.domain.InvalidDescription +import org.orkg.graph.domain.InvalidLabel +import org.orkg.graph.domain.MAX_LABEL_LENGTH +import org.orkg.graph.domain.NeitherOwnerNorCurator +import org.orkg.graph.domain.NotACurator +import org.orkg.testing.MockUserId +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.jsonPath +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController + +@WebMvcTest +@ContextConfiguration(classes = [TestController::class, ExceptionHandler::class, FixedClockConfig::class]) +internal class CommonExceptionUnitTest : MockMvcBaseTest("exceptions") { + + @Test + fun invalidLabel() { + get("/invalid-label") + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.errors.length()").value(1)) + .andExpect(jsonPath("$.errors[0].field").value("label")) + .andExpect(jsonPath("$.errors[0].message").value("""A label must not be blank or contain newlines and must be at most $MAX_LABEL_LENGTH characters long.""")) + .andExpect(jsonPath("$.path").value("/invalid-label")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun invalidLabelWithProperty() { + get("/invalid-label") + .param("property", "title") + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.errors.length()").value(1)) + .andExpect(jsonPath("$.errors[0].field").value("title")) + .andExpect(jsonPath("$.errors[0].message").value("""A label must not be blank or contain newlines and must be at most $MAX_LABEL_LENGTH characters long.""")) + .andExpect(jsonPath("$.path").value("/invalid-label")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun invalidDescription() { + get("/invalid-description") + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.errors.length()").value(1)) + .andExpect(jsonPath("$.errors[0].field").value("description")) + .andExpect(jsonPath("$.errors[0].message").value("""A description must not be blank and must be at most $MAX_LABEL_LENGTH characters long.""")) + .andExpect(jsonPath("$.path").value("/invalid-description")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun invalidDescriptionWithProperty() { + get("/invalid-description") + .param("property", "contents") + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.errors.length()").value(1)) + .andExpect(jsonPath("$.errors[0].field").value("contents")) + .andExpect(jsonPath("$.errors[0].message").value("""A description must not be blank and must be at most $MAX_LABEL_LENGTH characters long.""")) + .andExpect(jsonPath("$.path").value("/invalid-description")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun neitherOwnerNorCuratorDelete() { + val contributorId = ContributorId(MockUserId.USER) + + get("/neither-owner-nor-creator-delete") + .param("contributorId", contributorId.toString()) + .perform() + .andExpect(status().isForbidden) + .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) + .andExpect(jsonPath("$.error", `is`("Forbidden"))) + .andExpect(jsonPath("$.path").value("/neither-owner-nor-creator-delete")) + .andExpect(jsonPath("$.message").value("""Contributor <$contributorId> does not own the entity to be deleted and is not a curator.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun neitherOwnerNorCuratorVisibility() { + val id = "R123" + + get("/neither-owner-nor-creator-visibility") + .param("id", id) + .perform() + .andExpect(status().isForbidden) + .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) + .andExpect(jsonPath("$.error", `is`("Forbidden"))) + .andExpect(jsonPath("$.path").value("/neither-owner-nor-creator-visibility")) + .andExpect(jsonPath("$.message").value("""Insufficient permissions to change visibility of entity "$id".""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun notACurator() { + val contributorId = "62bfeea3-4210-436d-b953-effa5a07ed64" + + get("/not-a-curator") + .param("contributorId", contributorId) + .perform() + .andExpect(status().isForbidden) + .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) + .andExpect(jsonPath("$.error", `is`("Forbidden"))) + .andExpect(jsonPath("$.path").value("/not-a-curator")) + .andExpect(jsonPath("$.message").value("""Contributor <$contributorId> is not a curator.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun notACuratorCannotChangeVerifiedStatus() { + val contributorId = "62bfeea3-4210-436d-b953-effa5a07ed64" + + get("/not-a-curator-cannot-change-verified-status") + .param("contributorId", contributorId) + .perform() + .andExpect(status().isForbidden) + .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) + .andExpect(jsonPath("$.error", `is`("Forbidden"))) + .andExpect(jsonPath("$.path").value("/not-a-curator-cannot-change-verified-status")) + .andExpect(jsonPath("$.message").value("""Cannot change verified status: Contributor <$contributorId> is not a curator.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @TestComponent + @RestController + internal class TestController { + + @GetMapping("/invalid-label") + fun invalidLabel() { + throw InvalidLabel() + } + + @GetMapping("/invalid-label", params = ["property"]) + fun invalidLabel(@RequestParam property: String) { + throw InvalidLabel(property) + } + + @GetMapping("/invalid-description") + fun invalidInvalidDescription() { + throw InvalidDescription() + } + + @GetMapping("/invalid-description", params = ["property"]) + fun invalidInvalidDescription(@RequestParam property: String) { + throw InvalidDescription(property) + } + + @GetMapping("/neither-owner-nor-creator-delete") + fun neitherOwnerNorCuratorDelete(@RequestParam contributorId: ContributorId) { + throw NeitherOwnerNorCurator(contributorId) + } + + @GetMapping("/neither-owner-nor-creator-visibility") + fun neitherOwnerNorCuratorVisibility(@RequestParam id: ThingId) { + throw NeitherOwnerNorCurator.cannotChangeVisibility(id) + } + + @GetMapping("/not-a-curator") + fun notACurator(@RequestParam contributorId: ContributorId) { + throw NotACurator(contributorId) + } + + @GetMapping("/not-a-curator-cannot-change-verified-status") + fun notACuratorCannotChangeVerifiedStatus(@RequestParam contributorId: ContributorId) { + throw NotACurator.cannotChangeVerifiedStatus(contributorId) + } + } +} diff --git a/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/exceptions/ListExceptionUnitTest.kt b/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/exceptions/ListExceptionUnitTest.kt new file mode 100644 index 000000000..1ce2422ce --- /dev/null +++ b/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/exceptions/ListExceptionUnitTest.kt @@ -0,0 +1,49 @@ +package org.orkg.graph.adapter.input.rest.exceptions + +import org.hamcrest.Matchers.`is` +import org.hamcrest.Matchers.notNullValue +import org.junit.jupiter.api.Test +import org.orkg.common.ThingId +import org.orkg.common.exceptions.ExceptionHandler +import org.orkg.graph.adapter.input.rest.exceptions.ListExceptionUnitTest.TestController +import org.orkg.graph.domain.ListInUse +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.jsonPath +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController + +@WebMvcTest +@ContextConfiguration(classes = [TestController::class, ExceptionHandler::class, FixedClockConfig::class]) +internal class ListExceptionUnitTest : MockMvcBaseTest("exceptions") { + + @Test + fun listInUse() { + val id = "R123" + + get("/list-in-use") + .param("id", id) + .perform() + .andExpect(status().isForbidden) + .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) + .andExpect(jsonPath("$.error", `is`("Forbidden"))) + .andExpect(jsonPath("$.path").value("/list-in-use")) + .andExpect(jsonPath("$.message").value("""Unable to delete list "$id" because it is used in at least one statement.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @TestComponent + @RestController + internal class TestController { + @GetMapping("/list-in-use") + fun listInUse(@RequestParam id: ThingId) { + throw ListInUse(id) + } + } +} diff --git a/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/exceptions/LiteralExceptionUnitTest.kt b/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/exceptions/LiteralExceptionUnitTest.kt new file mode 100644 index 000000000..763b579de --- /dev/null +++ b/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/exceptions/LiteralExceptionUnitTest.kt @@ -0,0 +1,94 @@ +package org.orkg.graph.adapter.input.rest.exceptions + +import org.hamcrest.Matchers.`is` +import org.hamcrest.Matchers.notNullValue +import org.junit.jupiter.api.Test +import org.orkg.common.ThingId +import org.orkg.common.exceptions.ExceptionHandler +import org.orkg.graph.adapter.input.rest.exceptions.LiteralExceptionUnitTest.TestController +import org.orkg.graph.domain.InvalidLiteralLabel +import org.orkg.graph.domain.LiteralNotModifiable +import org.orkg.graph.domain.MAX_LABEL_LENGTH +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.jsonPath +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController + +@WebMvcTest +@ContextConfiguration(classes = [TestController::class, ExceptionHandler::class, FixedClockConfig::class]) +internal class LiteralExceptionUnitTest : MockMvcBaseTest("exceptions") { + + @Test + fun literalNotModifiable() { + val id = ThingId("R123") + + get("/literal-not-modifiable") + .param("id", id.value) + .perform() + .andExpect(status().isForbidden) + .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) + .andExpect(jsonPath("$.error", `is`("Forbidden"))) + .andExpect(jsonPath("$.path").value("/literal-not-modifiable")) + .andExpect(jsonPath("$.message").value("""Literal "$id" is not modifiable.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun invalidLiteralLabelTooLong() { + get("/invalid-literal-label-too-long") + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.errors.length()").value(1)) + .andExpect(jsonPath("$.errors[0].field").value("label")) + .andExpect(jsonPath("$.errors[0].message").value("A literal must be at most $MAX_LABEL_LENGTH characters long.")) + .andExpect(jsonPath("$.path").value("/invalid-literal-label-too-long")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun invalidLiteralLabelConstraintViolation() { + val label = "not a number" + val datatype = "xsd:decimal" + + get("/invalid-literal-label-constraint-violation") + .param("label", label) + .param("datatype", datatype) + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.errors.length()").value(1)) + .andExpect(jsonPath("$.errors[0].field").value("label")) + .andExpect(jsonPath("$.errors[0].message").value("""Literal value "$label" is not a valid "$datatype".""")) + .andExpect(jsonPath("$.path").value("/invalid-literal-label-constraint-violation")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @TestComponent + @RestController + internal class TestController { + @GetMapping("/literal-not-modifiable") + fun literalNotModifiable(@RequestParam id: ThingId) { + throw LiteralNotModifiable(id) + } + + @GetMapping("/invalid-literal-label-too-long") + fun invalidLiteralLabelTooLong() { + throw InvalidLiteralLabel() + } + + @GetMapping("/invalid-literal-label-constraint-violation") + fun invalidLiteralLabelConstraintViolation(@RequestParam label: String, @RequestParam datatype: String) { + throw InvalidLiteralLabel(label, datatype) + } + } +} diff --git a/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/exceptions/PredicateExceptionUnitTest.kt b/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/exceptions/PredicateExceptionUnitTest.kt new file mode 100644 index 000000000..731f9b0f3 --- /dev/null +++ b/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/exceptions/PredicateExceptionUnitTest.kt @@ -0,0 +1,72 @@ +package org.orkg.graph.adapter.input.rest.exceptions + +import org.hamcrest.Matchers.`is` +import org.hamcrest.Matchers.notNullValue +import org.junit.jupiter.api.Test +import org.orkg.common.ThingId +import org.orkg.common.exceptions.ExceptionHandler +import org.orkg.graph.adapter.input.rest.exceptions.PredicateExceptionUnitTest.TestController +import org.orkg.graph.domain.ExternalPredicateNotFound +import org.orkg.graph.domain.PredicateNotModifiable +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.jsonPath +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController + +@WebMvcTest +@ContextConfiguration(classes = [TestController::class, ExceptionHandler::class, FixedClockConfig::class]) +internal class PredicateExceptionUnitTest : MockMvcBaseTest("exceptions") { + + @Test + fun predicateNotModifiable() { + val id = ThingId("R123") + + get("/predicate-not-modifiable") + .param("id", id.value) + .perform() + .andExpect(status().isForbidden) + .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) + .andExpect(jsonPath("$.error", `is`("Forbidden"))) + .andExpect(jsonPath("$.path").value("/predicate-not-modifiable")) + .andExpect(jsonPath("$.message").value("""Predicate "$id" is not modifiable.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun externalPredicateNotFound() { + val id = "R123" + val ontologyId = "skos" + + get("/external-predicate-not-found") + .param("id", id) + .param("ontologyId", ontologyId) + .perform() + .andExpect(status().isNotFound) + .andExpect(jsonPath("$.status").value(HttpStatus.NOT_FOUND.value())) + .andExpect(jsonPath("$.error", `is`("Not Found"))) + .andExpect(jsonPath("$.path").value("/external-predicate-not-found")) + .andExpect(jsonPath("$.message").value("""External predicate "$id" for ontology "$ontologyId" not found.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @TestComponent + @RestController + internal class TestController { + @GetMapping("/predicate-not-modifiable") + fun predicateNotModifiable(@RequestParam id: ThingId) { + throw PredicateNotModifiable(id) + } + + @GetMapping("/external-predicate-not-found") + fun externalPredicateNotFound(@RequestParam ontologyId: String, @RequestParam id: String) { + throw ExternalPredicateNotFound(ontologyId, id) + } + } +} diff --git a/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/exceptions/ResourceExceptionUnitTest.kt b/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/exceptions/ResourceExceptionUnitTest.kt new file mode 100644 index 000000000..7e64ae1b8 --- /dev/null +++ b/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/exceptions/ResourceExceptionUnitTest.kt @@ -0,0 +1,72 @@ +package org.orkg.graph.adapter.input.rest.exceptions + +import org.hamcrest.Matchers.`is` +import org.hamcrest.Matchers.notNullValue +import org.junit.jupiter.api.Test +import org.orkg.common.ThingId +import org.orkg.common.exceptions.ExceptionHandler +import org.orkg.graph.adapter.input.rest.exceptions.ResourceExceptionUnitTest.TestController +import org.orkg.graph.domain.ExternalResourceNotFound +import org.orkg.graph.domain.ResourceNotModifiable +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.jsonPath +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController + +@WebMvcTest +@ContextConfiguration(classes = [TestController::class, ExceptionHandler::class, FixedClockConfig::class]) +internal class ResourceExceptionUnitTest : MockMvcBaseTest("exceptions") { + + @Test + fun resourceNotModifiable() { + val id = ThingId("R123") + + get("/resource-not-modifiable") + .param("id", id.value) + .perform() + .andExpect(status().isForbidden) + .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) + .andExpect(jsonPath("$.error", `is`("Forbidden"))) + .andExpect(jsonPath("$.path").value("/resource-not-modifiable")) + .andExpect(jsonPath("$.message").value("""Resource "$id" is not modifiable.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun externalResourceNotFound() { + val id = "R123" + val ontologyId = "skos" + + get("/external-resource-not-found") + .param("id", id) + .param("ontologyId", ontologyId) + .perform() + .andExpect(status().isNotFound) + .andExpect(jsonPath("$.status").value(HttpStatus.NOT_FOUND.value())) + .andExpect(jsonPath("$.error", `is`("Not Found"))) + .andExpect(jsonPath("$.path").value("/external-resource-not-found")) + .andExpect(jsonPath("$.message").value("""External resource "$id" for ontology "$ontologyId" not found.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @TestComponent + @RestController + internal class TestController { + @GetMapping("/resource-not-modifiable") + fun resourceNotModifiable(@RequestParam id: ThingId) { + throw ResourceNotModifiable(id) + } + + @GetMapping("/external-resource-not-found") + fun externalResourceNotFound(@RequestParam ontologyId: String, @RequestParam id: String) { + throw ExternalResourceNotFound(ontologyId, id) + } + } +} diff --git a/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/exceptions/StatementExceptionUnitTest.kt b/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/exceptions/StatementExceptionUnitTest.kt new file mode 100644 index 000000000..f65979806 --- /dev/null +++ b/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/exceptions/StatementExceptionUnitTest.kt @@ -0,0 +1,122 @@ +package org.orkg.graph.adapter.input.rest.exceptions + +import org.hamcrest.Matchers.`is` +import org.hamcrest.Matchers.notNullValue +import org.junit.jupiter.api.Test +import org.orkg.common.exceptions.ExceptionHandler +import org.orkg.graph.adapter.input.rest.exceptions.StatementExceptionUnitTest.TestController +import org.orkg.graph.domain.InvalidStatement +import org.orkg.graph.domain.StatementAlreadyExists +import org.orkg.graph.domain.StatementId +import org.orkg.graph.domain.StatementNotModifiable +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.jsonPath +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController + +@WebMvcTest +@ContextConfiguration(classes = [TestController::class, ExceptionHandler::class, FixedClockConfig::class]) +internal class StatementExceptionUnitTest : MockMvcBaseTest("exceptions") { + + @Test + fun statementNotModifiable() { + val id = StatementId("S123") + + get("/statement-not-modifiable") + .param("id", id.value) + .perform() + .andExpect(status().isForbidden) + .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())) + .andExpect(jsonPath("$.error", `is`("Forbidden"))) + .andExpect(jsonPath("$.path").value("/statement-not-modifiable")) + .andExpect(jsonPath("$.message").value("""Statement "$id" is not modifiable.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun invalidStatementIsListElement() { + get("/invalid-statement-is-list-element") + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.path").value("/invalid-statement-is-list-element")) + .andExpect(jsonPath("$.message").value("A list element statement cannot be managed using the statements endpoint. Please see the documentation on how to manage lists.")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun invalidStatementSubjectMustNotBeLiteral() { + get("/invalid-statement-subject-must-not-be-literal") + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.path").value("/invalid-statement-subject-must-not-be-literal")) + .andExpect(jsonPath("$.message").value("Subject must not be a literal.")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun invalidStatementRosettaStoneStatementResource() { + get("/invalid-statement-rosetta-stone-statement-resource") + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.path").value("/invalid-statement-rosetta-stone-statement-resource")) + .andExpect(jsonPath("$.message").value("A rosetta stone statement resource cannot be managed using statements endpoint. Please see the documentation on how to manage rosetta stone statements.")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @Test + fun statementAlreadyExists() { + val id = StatementId("S4565") + + get("/statement-already-exists") + .param("id", id.value) + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.path").value("/statement-already-exists")) + .andExpect(jsonPath("$.message").value("""Statement already exists with id "$id".""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @TestComponent + @RestController + internal class TestController { + @GetMapping("/statement-not-modifiable") + fun statementNotModifiable(@RequestParam id: StatementId) { + throw StatementNotModifiable(id) + } + + @GetMapping("/invalid-statement-is-list-element") + fun invalidStatementIsListElement() { + throw InvalidStatement.isListElementStatement() + } + + @GetMapping("/invalid-statement-subject-must-not-be-literal") + fun invalidStatementSubjectMustNotBeLiteral() { + throw InvalidStatement.subjectMustNotBeLiteral() + } + + @GetMapping("/invalid-statement-rosetta-stone-statement-resource") + fun invalidStatementRosettaStoneStatementResource() { + throw InvalidStatement.includesRosettaStoneStatementResource() + } + + @GetMapping("/statement-already-exists") + fun statementAlreadyExists(@RequestParam id: StatementId) { + throw StatementAlreadyExists(id) + } + } +} diff --git a/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/exceptions/ThingExceptionUnitTest.kt b/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/exceptions/ThingExceptionUnitTest.kt new file mode 100644 index 000000000..9fabe47c9 --- /dev/null +++ b/graph/graph-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/graph/adapter/input/rest/exceptions/ThingExceptionUnitTest.kt @@ -0,0 +1,50 @@ +package org.orkg.graph.adapter.input.rest.exceptions + +import org.hamcrest.Matchers.`is` +import org.hamcrest.Matchers.notNullValue +import org.junit.jupiter.api.Test +import org.orkg.common.ThingId +import org.orkg.common.exceptions.ExceptionHandler +import org.orkg.graph.adapter.input.rest.exceptions.ThingExceptionUnitTest.TestController +import org.orkg.graph.domain.ThingAlreadyExists +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.jsonPath +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController + +@WebMvcTest +@ContextConfiguration(classes = [TestController::class, ExceptionHandler::class, FixedClockConfig::class]) +internal class ThingExceptionUnitTest : MockMvcBaseTest("exceptions") { + + @Test + fun thingAlreadyExists() { + val id = "R123" + + get("/thing-already-exists") + .param("id", id) + .perform() + .andExpect(status().isBadRequest) + .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value())) + .andExpect(jsonPath("$.error", `is`("Bad Request"))) + .andExpect(jsonPath("$.path").value("/thing-already-exists")) + .andExpect(jsonPath("$.message").value("""A thing with id "$id" already exists.""")) + .andExpect(jsonPath("$.timestamp", `is`(notNullValue()))) + } + + @TestComponent + @RestController + internal class TestController { + + @GetMapping("/thing-already-exists") + fun thingAlreadyExists(@RequestParam id: ThingId) { + throw ThingAlreadyExists(id) + } + } +} diff --git a/statistics/statistics-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/statistics/adapter/input/rest/StatisticsControllerExceptionUnitTest.kt b/statistics/statistics-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/statistics/adapter/input/rest/exceptions/StatisticsExceptionUnitTest.kt similarity index 81% rename from statistics/statistics-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/statistics/adapter/input/rest/StatisticsControllerExceptionUnitTest.kt rename to statistics/statistics-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/statistics/adapter/input/rest/exceptions/StatisticsExceptionUnitTest.kt index 5875140ed..1526504a9 100644 --- a/statistics/statistics-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/statistics/adapter/input/rest/StatisticsControllerExceptionUnitTest.kt +++ b/statistics/statistics-adapter-input-rest-spring-mvc/src/test/kotlin/org/orkg/statistics/adapter/input/rest/exceptions/StatisticsExceptionUnitTest.kt @@ -1,18 +1,16 @@ -package org.orkg.statistics.adapter.input.rest +package org.orkg.statistics.adapter.input.rest.exceptions import org.hamcrest.Matchers.`is` import org.hamcrest.Matchers.notNullValue import org.junit.jupiter.api.Test import org.orkg.common.exceptions.ExceptionHandler -import org.orkg.statistics.adapter.input.rest.StatisticsControllerExceptionUnitTest.FakeExceptionController +import org.orkg.statistics.adapter.input.rest.exceptions.StatisticsExceptionUnitTest.TestController import org.orkg.statistics.domain.GroupNotFound import org.orkg.statistics.domain.MetricNotFound import org.orkg.testing.configuration.FixedClockConfig -import org.orkg.testing.configuration.SecurityTestConfiguration 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.context.annotation.Import import org.springframework.http.HttpStatus import org.springframework.test.context.ContextConfiguration import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath @@ -21,10 +19,9 @@ import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController -@Import(SecurityTestConfiguration::class) @WebMvcTest -@ContextConfiguration(classes = [FakeExceptionController::class, ExceptionHandler::class, FixedClockConfig::class]) -internal class StatisticsControllerExceptionUnitTest : MockMvcBaseTest("statistics") { +@ContextConfiguration(classes = [TestController::class, ExceptionHandler::class, FixedClockConfig::class]) +internal class StatisticsExceptionUnitTest : MockMvcBaseTest("statistics") { @Test fun groupNotFound() { @@ -60,7 +57,7 @@ internal class StatisticsControllerExceptionUnitTest : MockMvcBaseTest("statisti @TestComponent @RestController - internal class FakeExceptionController { + internal class TestController { @GetMapping("/group-not-found") fun groupNotFound(@RequestParam id: String) { throw GroupNotFound(id)