1
+ /*
2
+ * Copyright 2002-2019 the original author or authors.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ package org.springframework.web.reactive.result
18
+
19
+ import io.mockk.every
20
+ import io.mockk.mockk
21
+ import kotlinx.coroutines.delay
22
+ import org.hamcrest.CoreMatchers.`is`
23
+ import org.hamcrest.MatcherAssert.assertThat
24
+ import org.junit.Assert.assertEquals
25
+ import org.junit.Test
26
+ import org.springframework.http.HttpStatus
27
+ import org.springframework.http.server.reactive.ServerHttpResponse
28
+ import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest.get
29
+ import org.springframework.mock.web.test.server.MockServerWebExchange
30
+ import org.springframework.web.bind.annotation.ResponseStatus
31
+ import org.springframework.web.reactive.BindingContext
32
+ import org.springframework.web.reactive.HandlerResult
33
+ import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver
34
+ import org.springframework.web.reactive.result.method.InvocableHandlerMethod
35
+ import org.springframework.web.reactive.result.method.annotation.ContinuationHandlerMethodArgumentResolver
36
+ import reactor.core.publisher.Mono
37
+ import reactor.test.StepVerifier
38
+ import reactor.test.expectError
39
+ import java.lang.reflect.Method
40
+ import kotlin.reflect.jvm.javaMethod
41
+
42
+ class KotlinInvocableHandlerMethodTests {
43
+
44
+ private val exchange = MockServerWebExchange .from(get(" http://localhost:8080/path" ))
45
+
46
+ private val resolvers = mutableListOf<HandlerMethodArgumentResolver >(ContinuationHandlerMethodArgumentResolver ())
47
+
48
+ @Test
49
+ fun resolveNoArg () {
50
+ this .resolvers.add(stubResolver(Mono .empty()))
51
+ val method = CoroutinesController ::singleArg.javaMethod!!
52
+ val result = invoke(CoroutinesController (), method, null )
53
+ assertHandlerResultValue(result, " success:null" )
54
+ }
55
+
56
+ @Test
57
+ fun resolveArg () {
58
+ this .resolvers.add(stubResolver(" foo" ))
59
+ val method = CoroutinesController ::singleArg.javaMethod!!
60
+ val result = invoke(CoroutinesController (), method," foo" )
61
+ assertHandlerResultValue(result, " success:foo" )
62
+ }
63
+
64
+ @Test
65
+ fun resolveNoArgs () {
66
+ val method = CoroutinesController ::noArgs.javaMethod!!
67
+ val result = invoke(CoroutinesController (), method)
68
+ assertHandlerResultValue(result, " success" )
69
+ }
70
+
71
+ @Test
72
+ fun invocationTargetException () {
73
+ val method = CoroutinesController ::exceptionMethod.javaMethod!!
74
+ val result = invoke(CoroutinesController (), method)
75
+
76
+ StepVerifier .create(result)
77
+ .consumeNextWith { StepVerifier .create(it.returnValue as Mono <* >).expectError(IllegalStateException ::class ).verify() }
78
+ .verifyComplete()
79
+ }
80
+
81
+ @Test
82
+ fun responseStatusAnnotation () {
83
+ val method = CoroutinesController ::created.javaMethod!!
84
+ val result = invoke(CoroutinesController (), method)
85
+
86
+ assertHandlerResultValue(result, " created" )
87
+ assertThat<HttpStatus >(this .exchange.response.statusCode, `is `(HttpStatus .CREATED ))
88
+ }
89
+
90
+ @Test
91
+ fun voidMethodWithResponseArg () {
92
+ val response = this .exchange.response
93
+ this .resolvers.add(stubResolver(response))
94
+ val method = CoroutinesController ::response.javaMethod!!
95
+ val result = invoke(CoroutinesController (), method)
96
+
97
+ StepVerifier .create(result)
98
+ .consumeNextWith { StepVerifier .create(it.returnValue as Mono <* >).verifyComplete() }
99
+ .verifyComplete()
100
+ assertEquals(" bar" , this .exchange.response.headers.getFirst(" foo" ))
101
+ }
102
+
103
+ private fun invoke (handler : Any , method : Method , vararg providedArgs : Any? ): Mono <HandlerResult > {
104
+ val invocable = InvocableHandlerMethod (handler, method)
105
+ invocable.setArgumentResolvers(this .resolvers)
106
+ return invocable.invoke(this .exchange, BindingContext (), * providedArgs)
107
+ }
108
+
109
+ private fun stubResolver (stubValue : Any? ): HandlerMethodArgumentResolver {
110
+ return stubResolver(Mono .justOrEmpty(stubValue))
111
+ }
112
+
113
+ private fun stubResolver (stubValue : Mono <Any >): HandlerMethodArgumentResolver {
114
+ val resolver = mockk<HandlerMethodArgumentResolver >()
115
+ every { resolver.supportsParameter(any()) } returns true
116
+ every { resolver.resolveArgument(any(), any(), any()) } returns stubValue
117
+ return resolver
118
+ }
119
+
120
+ private fun assertHandlerResultValue (mono : Mono <HandlerResult >, expected : String ) {
121
+ StepVerifier .create(mono)
122
+ .consumeNextWith { StepVerifier .create(it.returnValue as Mono <* >).expectNext(expected).verifyComplete() }
123
+ .verifyComplete()
124
+ }
125
+
126
+ class CoroutinesController {
127
+
128
+ suspend fun singleArg (q : String? ): String {
129
+ delay(10 )
130
+ return " success:$q "
131
+ }
132
+
133
+ suspend fun noArgs (): String {
134
+ delay(10 )
135
+ return " success"
136
+ }
137
+
138
+ suspend fun exceptionMethod () {
139
+ throw IllegalStateException (" boo" )
140
+ }
141
+
142
+ @ResponseStatus(HttpStatus .CREATED )
143
+ suspend fun created (): String {
144
+ delay(10 )
145
+ return " created"
146
+ }
147
+
148
+ suspend fun response (response : ServerHttpResponse ) {
149
+ delay(10 )
150
+ response.headers.add(" foo" , " bar" )
151
+ }
152
+
153
+
154
+ }
155
+ }
0 commit comments