I was trying to make a unit test that verifies that an exception thrown by hibernate would be handled and would result in a proper response. The exception is a PersistenceException() with cause being a ConstraintViolationException(). So I handled it in ktor's StatusPages in the following way:
exception<PersistenceException> {
if (it.cause is DataException) call.respond(
HttpStatusCode.BadRequest,
AppError("Cannot fulfill request due to Invalid Data.")
)
else if (it.cause is ConstraintViolationException) call.respond(
HttpStatusCode.ExpectationFailed,
AppError("Cannot fulfill request due to Constraint Violations.")
)
}
And it works when I force the error by trying to insert a duplicated row but when I mock a method to throw said exception it gets encapsulated by PersistenceException twice:
- when the error happens in real use:
javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:154)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:727)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:707)
(...)
Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:109)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
(...)
Caused by: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "subscription_unique_topic_appid_ssl_subject_trio"
Detail: Key (topic_id, app_id, ssl_subject_name)=(1, something, something) already exists.
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2532)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2267)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:312)
(...)
- when the error happens in the unit test:
javax.persistence.PersistenceException: Ex
(Coroutine boundary)
at io.ktor.features.ContentNegotiation$Feature$install$1.invokeSuspend(ContentNegotiation.kt:107)
at io.ktor.features.StatusPages$interceptCall$2.invokeSuspend(StatusPages.kt:101)
at io.ktor.features.StatusPages.interceptCall(StatusPages.kt:100)
at io.ktor.features.StatusPages$Feature$install$2.invokeSuspend(StatusPages.kt:140)
at io.ktor.features.CallLogging$Feature$install$2.invokeSuspend(CallLogging.kt:139)
at io.ktor.server.testing.TestApplicationEngine$callInterceptor$1.invokeSuspend(TestApplicationEngine.kt:291)
at io.ktor.server.testing.TestApplicationEngine$2.invokeSuspend(TestApplicationEngine.kt:50)
Caused by: javax.persistence.PersistenceException: Ex
... 8 more
Caused by: javax.persistence.PersistenceException: Ex
(Coroutine boundary)
at io.ktor.routing.Routing.executeResult(Routing.kt:147)
at io.ktor.routing.Routing.interceptor(Routing.kt:34)
at io.ktor.routing.Routing$Feature$install$1.invokeSuspend(Routing.kt:99)
at io.ktor.features.ContentNegotiation$Feature$install$1.invokeSuspend(ContentNegotiation.kt:107)
at io.ktor.features.StatusPages$interceptCall$2.invokeSuspend(StatusPages.kt:101)
at io.ktor.features.StatusPages.interceptCall(StatusPages.kt:100)
at io.ktor.features.StatusPages$Feature$install$2.invokeSuspend(StatusPages.kt:140)
at io.ktor.features.CallLogging$Feature$install$2.invokeSuspend(CallLogging.kt:139)
at io.ktor.server.testing.TestApplicationEngine$callInterceptor$1.invokeSuspend(TestApplicationEngine.kt:291)
at io.ktor.server.testing.TestApplicationEngine$2.invokeSuspend(TestApplicationEngine.kt:50)
at io.ktor.server.testing.TestApplicationEngine$handleRequest$pipelineJob$1.invokeSuspend(TestApplicationEngine.kt:290)
Caused by: org.hibernate.exception.ConstraintViolationException: Ex
at com.controller.SubscriptionRequestControllerTest.POST request handles constraint issue with inserting in the database(SubscriptionRequestControllerTest.kt:298)
(...)
Caused by: org.postgresql.util.PSQLException: Ex
(...)
So it doesn't comply with the condition of 'it.cause is ConstraintViolationException' and I don't know why since I have similar unit tests throwing other exceptions and it works fine.
Here is the unit test:
fun `POST request handles constraint issue with inserting in the database`() {
(...)
val subscriptionsRepository = get(SubscriptionsRepository::class.java)
every {
subscriptionsRepository.addSubscriptionRequest(any())
} throws PersistenceException(
"Constraint Exception",
ConstraintViolationException("Ex", PSQLException("Ex", PSQLState.UNIQUE_VIOLATION), "Ex")
)
(...)
val fakeRequest = SubscriptionRequestDTO(...)
withTestApplication(Application::main) {
val request = handleRequest {
method = HttpMethod.Post
addHeader("content-type", "application/json")
uri = "subscriptions/requests"
setBody(Gson().toJson(fakeRequest))
addJwtHeader(makeToken(validAlgorithm, isExpired = false))
}
request.response.status() shouldBe HttpStatusCode.ExpectationFailed // fails saying that the status is null
}
}