|
|
|
@ -16,14 +16,18 @@ |
|
|
|
|
|
|
|
|
|
|
|
package org.springframework.web.method.support |
|
|
|
package org.springframework.web.method.support |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import kotlinx.coroutines.delay |
|
|
|
import org.assertj.core.api.Assertions |
|
|
|
import org.assertj.core.api.Assertions |
|
|
|
import org.junit.jupiter.api.Test |
|
|
|
import org.junit.jupiter.api.Test |
|
|
|
|
|
|
|
import org.springframework.core.MethodParameter |
|
|
|
import org.springframework.util.ReflectionUtils |
|
|
|
import org.springframework.util.ReflectionUtils |
|
|
|
|
|
|
|
import org.springframework.web.bind.support.WebDataBinderFactory |
|
|
|
import org.springframework.web.context.request.NativeWebRequest |
|
|
|
import org.springframework.web.context.request.NativeWebRequest |
|
|
|
import org.springframework.web.context.request.ServletWebRequest |
|
|
|
import org.springframework.web.context.request.ServletWebRequest |
|
|
|
import org.springframework.web.testfixture.method.ResolvableMethod |
|
|
|
|
|
|
|
import org.springframework.web.testfixture.servlet.MockHttpServletRequest |
|
|
|
import org.springframework.web.testfixture.servlet.MockHttpServletRequest |
|
|
|
import org.springframework.web.testfixture.servlet.MockHttpServletResponse |
|
|
|
import org.springframework.web.testfixture.servlet.MockHttpServletResponse |
|
|
|
|
|
|
|
import reactor.core.publisher.Mono |
|
|
|
|
|
|
|
import reactor.test.StepVerifier |
|
|
|
import java.lang.reflect.Method |
|
|
|
import java.lang.reflect.Method |
|
|
|
import kotlin.reflect.jvm.javaGetter |
|
|
|
import kotlin.reflect.jvm.javaGetter |
|
|
|
import kotlin.reflect.jvm.javaMethod |
|
|
|
import kotlin.reflect.jvm.javaMethod |
|
|
|
@ -33,6 +37,7 @@ import kotlin.reflect.jvm.javaMethod |
|
|
|
* |
|
|
|
* |
|
|
|
* @author Sebastien Deleuze |
|
|
|
* @author Sebastien Deleuze |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
|
|
|
|
@Suppress("UNCHECKED_CAST") |
|
|
|
class InvocableHandlerMethodKotlinTests { |
|
|
|
class InvocableHandlerMethodKotlinTests { |
|
|
|
|
|
|
|
|
|
|
|
private val request: NativeWebRequest = ServletWebRequest(MockHttpServletRequest(), MockHttpServletResponse()) |
|
|
|
private val request: NativeWebRequest = ServletWebRequest(MockHttpServletRequest(), MockHttpServletResponse()) |
|
|
|
@ -110,6 +115,12 @@ class InvocableHandlerMethodKotlinTests { |
|
|
|
Assertions.assertThat(value).isEqualTo("foo") |
|
|
|
Assertions.assertThat(value).isEqualTo("foo") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
|
|
|
fun resultOfUnitReturnValue() { |
|
|
|
|
|
|
|
val value = getInvocable(ValueClassHandler::resultOfUnitReturnValue.javaMethod!!).invokeForRequest(request, null) |
|
|
|
|
|
|
|
Assertions.assertThat(value).isNull() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
fun valueClassDefaultValue() { |
|
|
|
fun valueClassDefaultValue() { |
|
|
|
composite.addResolver(StubArgumentResolver(Double::class.java)) |
|
|
|
composite.addResolver(StubArgumentResolver(Double::class.java)) |
|
|
|
@ -138,6 +149,60 @@ class InvocableHandlerMethodKotlinTests { |
|
|
|
Assertions.assertThat(value).isEqualTo('a') |
|
|
|
Assertions.assertThat(value).isEqualTo('a') |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
|
|
|
fun suspendingValueClass() { |
|
|
|
|
|
|
|
composite.addResolver(ContinuationHandlerMethodArgumentResolver()) |
|
|
|
|
|
|
|
composite.addResolver(StubArgumentResolver(Long::class.java, 1L)) |
|
|
|
|
|
|
|
val value = getInvocable(SuspendingValueClassHandler::longValueClass.javaMethod!!).invokeForRequest(request, null) |
|
|
|
|
|
|
|
StepVerifier.create(value as Mono<Long>).expectNext(1L).verifyComplete() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
|
|
|
fun suspendingValueClassReturnValue() { |
|
|
|
|
|
|
|
composite.addResolver(ContinuationHandlerMethodArgumentResolver()) |
|
|
|
|
|
|
|
val value = getInvocable(SuspendingValueClassHandler::valueClassReturnValue.javaMethod!!).invokeForRequest(request, null) |
|
|
|
|
|
|
|
StepVerifier.create(value as Mono<String>).expectNext("foo").verifyComplete() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
|
|
|
fun suspendingResultOfUnitReturnValue() { |
|
|
|
|
|
|
|
composite.addResolver(ContinuationHandlerMethodArgumentResolver()) |
|
|
|
|
|
|
|
val value = getInvocable(SuspendingValueClassHandler::resultOfUnitReturnValue.javaMethod!!).invokeForRequest(request, null) |
|
|
|
|
|
|
|
StepVerifier.create(value as Mono<Unit>).verifyComplete() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
|
|
|
fun suspendingValueClassDefaultValue() { |
|
|
|
|
|
|
|
composite.addResolver(ContinuationHandlerMethodArgumentResolver()) |
|
|
|
|
|
|
|
composite.addResolver(StubArgumentResolver(Double::class.java)) |
|
|
|
|
|
|
|
val value = getInvocable(SuspendingValueClassHandler::doubleValueClass.javaMethod!!).invokeForRequest(request, null) |
|
|
|
|
|
|
|
StepVerifier.create(value as Mono<Double>).expectNext(3.1).verifyComplete() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
|
|
|
fun suspendingValueClassWithInit() { |
|
|
|
|
|
|
|
composite.addResolver(ContinuationHandlerMethodArgumentResolver()) |
|
|
|
|
|
|
|
composite.addResolver(StubArgumentResolver(String::class.java, "")) |
|
|
|
|
|
|
|
val value = getInvocable(SuspendingValueClassHandler::valueClassWithInit.javaMethod!!).invokeForRequest(request, null) |
|
|
|
|
|
|
|
StepVerifier.create(value as Mono<String>).verifyError(IllegalArgumentException::class.java) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
|
|
|
fun suspendingValueClassWithNullable() { |
|
|
|
|
|
|
|
composite.addResolver(ContinuationHandlerMethodArgumentResolver()) |
|
|
|
|
|
|
|
composite.addResolver(StubArgumentResolver(LongValueClass::class.java, null)) |
|
|
|
|
|
|
|
val value = getInvocable(SuspendingValueClassHandler::valueClassWithNullable.javaMethod!!).invokeForRequest(request, null) |
|
|
|
|
|
|
|
StepVerifier.create(value as Mono<Long>).verifyComplete() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
|
|
|
fun suspendingValueClassWithPrivateConstructor() { |
|
|
|
|
|
|
|
composite.addResolver(ContinuationHandlerMethodArgumentResolver()) |
|
|
|
|
|
|
|
composite.addResolver(StubArgumentResolver(Char::class.java, 'a')) |
|
|
|
|
|
|
|
val value = getInvocable(SuspendingValueClassHandler::valueClassWithPrivateConstructor.javaMethod!!).invokeForRequest(request, null) |
|
|
|
|
|
|
|
StepVerifier.create(value as Mono<Char>).expectNext('a').verifyComplete() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
fun propertyAccessor() { |
|
|
|
fun propertyAccessor() { |
|
|
|
val value = getInvocable(PropertyAccessorHandler::prop.javaGetter!!).invokeForRequest(request, null) |
|
|
|
val value = getInvocable(PropertyAccessorHandler::prop.javaGetter!!).invokeForRequest(request, null) |
|
|
|
@ -206,23 +271,58 @@ class InvocableHandlerMethodKotlinTests { |
|
|
|
|
|
|
|
|
|
|
|
private class ValueClassHandler { |
|
|
|
private class ValueClassHandler { |
|
|
|
|
|
|
|
|
|
|
|
fun valueClassReturnValue() = |
|
|
|
fun valueClassReturnValue() = StringValueClass("foo") |
|
|
|
StringValueClass("foo") |
|
|
|
|
|
|
|
|
|
|
|
fun resultOfUnitReturnValue() = Result.success(Unit) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun longValueClass(limit: LongValueClass) = limit.value |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun doubleValueClass(limit: DoubleValueClass = DoubleValueClass(3.1)) = limit.value |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun valueClassWithInit(valueClass: ValueClassWithInit) = valueClass |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun valueClassWithNullable(limit: LongValueClass?) = limit?.value |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun valueClassWithPrivateConstructor(limit: ValueClassWithPrivateConstructor) = limit.value |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private class SuspendingValueClassHandler { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
suspend fun valueClassReturnValue(): StringValueClass { |
|
|
|
|
|
|
|
delay(1) |
|
|
|
|
|
|
|
return StringValueClass("foo") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
suspend fun resultOfUnitReturnValue(): Result<Unit> { |
|
|
|
|
|
|
|
delay(1) |
|
|
|
|
|
|
|
return Result.success(Unit) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fun longValueClass(limit: LongValueClass) = |
|
|
|
suspend fun longValueClass(limit: LongValueClass): Long { |
|
|
|
limit.value |
|
|
|
delay(1) |
|
|
|
|
|
|
|
return limit.value |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fun doubleValueClass(limit: DoubleValueClass = DoubleValueClass(3.1)) = |
|
|
|
|
|
|
|
limit.value |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun valueClassWithInit(valueClass: ValueClassWithInit) = |
|
|
|
suspend fun doubleValueClass(limit: DoubleValueClass = DoubleValueClass(3.1)): Double { |
|
|
|
valueClass |
|
|
|
delay(1) |
|
|
|
|
|
|
|
return limit.value |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fun valueClassWithNullable(limit: LongValueClass?) = |
|
|
|
suspend fun valueClassWithInit(valueClass: ValueClassWithInit): ValueClassWithInit { |
|
|
|
limit?.value |
|
|
|
delay(1) |
|
|
|
|
|
|
|
return valueClass |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
suspend fun valueClassWithNullable(limit: LongValueClass?): Long? { |
|
|
|
|
|
|
|
delay(1) |
|
|
|
|
|
|
|
return limit?.value |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fun valueClassWithPrivateConstructor(limit: ValueClassWithPrivateConstructor) = |
|
|
|
suspend fun valueClassWithPrivateConstructor(limit: ValueClassWithPrivateConstructor): Char { |
|
|
|
limit.value |
|
|
|
delay(1) |
|
|
|
|
|
|
|
return limit.value |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private class PropertyAccessorHandler { |
|
|
|
private class PropertyAccessorHandler { |
|
|
|
@ -282,4 +382,19 @@ class InvocableHandlerMethodKotlinTests { |
|
|
|
|
|
|
|
|
|
|
|
class CustomException(message: String) : Throwable(message) |
|
|
|
class CustomException(message: String) : Throwable(message) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Avoid adding a spring-webmvc dependency |
|
|
|
|
|
|
|
class ContinuationHandlerMethodArgumentResolver : HandlerMethodArgumentResolver { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
override fun supportsParameter(parameter: MethodParameter) = |
|
|
|
|
|
|
|
"kotlin.coroutines.Continuation" == parameter.getParameterType().getName() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
override fun resolveArgument( |
|
|
|
|
|
|
|
parameter: MethodParameter, |
|
|
|
|
|
|
|
mavContainer: ModelAndViewContainer?, |
|
|
|
|
|
|
|
webRequest: NativeWebRequest, |
|
|
|
|
|
|
|
binderFactory: WebDataBinderFactory? |
|
|
|
|
|
|
|
) = null |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|