@ -16,14 +16,18 @@
@@ -16,14 +16,18 @@
package org.springframework.web.method.support
import kotlinx.coroutines.delay
import org.assertj.core.api.Assertions
import org.junit.jupiter.api.Test
import org.springframework.core.MethodParameter
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.ServletWebRequest
import org.springframework.web.testfixture.method.ResolvableMethod
import org.springframework.web.testfixture.servlet.MockHttpServletRequest
import org.springframework.web.testfixture.servlet.MockHttpServletResponse
import reactor.core.publisher.Mono
import reactor.test.StepVerifier
import java.lang.reflect.Method
import kotlin.reflect.jvm.javaGetter
import kotlin.reflect.jvm.javaMethod
@ -33,6 +37,7 @@ import kotlin.reflect.jvm.javaMethod
@@ -33,6 +37,7 @@ import kotlin.reflect.jvm.javaMethod
*
* @author Sebastien Deleuze
* /
@Suppress ( " UNCHECKED_CAST " )
class InvocableHandlerMethodKotlinTests {
private val request : NativeWebRequest = ServletWebRequest ( MockHttpServletRequest ( ) , MockHttpServletResponse ( ) )
@ -110,6 +115,12 @@ class InvocableHandlerMethodKotlinTests {
@@ -110,6 +115,12 @@ class InvocableHandlerMethodKotlinTests {
Assertions . assertThat ( value ) . isEqualTo ( " foo " )
}
@Test
fun resultOfUnitReturnValue ( ) {
val value = getInvocable ( ValueClassHandler :: resultOfUnitReturnValue . javaMethod !! ) . invokeForRequest ( request , null )
Assertions . assertThat ( value ) . isNull ( )
}
@Test
fun valueClassDefaultValue ( ) {
composite . addResolver ( StubArgumentResolver ( Double :: class . java ) )
@ -138,6 +149,60 @@ class InvocableHandlerMethodKotlinTests {
@@ -138,6 +149,60 @@ class InvocableHandlerMethodKotlinTests {
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
fun propertyAccessor ( ) {
val value = getInvocable ( PropertyAccessorHandler :: prop . javaGetter !! ) . invokeForRequest ( request , null )
@ -206,23 +271,58 @@ class InvocableHandlerMethodKotlinTests {
@@ -206,23 +271,58 @@ class InvocableHandlerMethodKotlinTests {
private class ValueClassHandler {
fun valueClassReturnValue ( ) =
StringValueClass ( " foo " )
fun valueClassReturnValue ( ) = 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 ) =
limit . value
suspend fun longValueClass ( limit : LongValueClass ) : Long {
delay ( 1 )
return limit . value
}
fun doubleValueClass ( limit : DoubleValueClass = DoubleValueClass ( 3.1 ) ) =
limit . value
fun valueClassWithInit ( valueClass : ValueClassWithInit ) =
valueClass
suspend fun doubleValueClass ( limit : DoubleValueClass = DoubleValueClass ( 3.1 ) ) : Double {
delay ( 1 )
return limit . value
}
fun valueClassWithNullable ( limit : LongValueClass ? ) =
limit ?. value
suspend fun valueClassWithInit ( valueClass : ValueClassWithInit ) : ValueClassWithInit {
delay ( 1 )
return valueClass
}
fun valueClassWithPrivateConstructor ( limit : ValueClassWithPrivateConstructor ) =
limit . value
suspend fun valueClassWithNullable ( limit : LongValueClass ? ) : Long ? {
delay ( 1 )
return limit ?. value
}
suspend fun valueClassWithPrivateConstructor ( limit : ValueClassWithPrivateConstructor ) : Char {
delay ( 1 )
return limit . value
}
}
private class PropertyAccessorHandler {
@ -282,4 +382,19 @@ class InvocableHandlerMethodKotlinTests {
@@ -282,4 +382,19 @@ class InvocableHandlerMethodKotlinTests {
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
}
}