1
+ package io.github.debop.rsocket.server
2
+
3
+ import io.github.debop.rsocket.api.Message
4
+ import io.rsocket.SocketAcceptor
5
+ import io.rsocket.core.RSocketServer
6
+ import io.rsocket.frame.decoder.PayloadDecoder
7
+ import io.rsocket.metadata.WellKnownMimeType
8
+ import io.rsocket.transport.netty.server.CloseableChannel
9
+ import io.rsocket.transport.netty.server.TcpServerTransport
10
+ import kotlinx.coroutines.delay
11
+ import kotlinx.coroutines.flow.Flow
12
+ import kotlinx.coroutines.flow.collect
13
+ import kotlinx.coroutines.flow.collectIndexed
14
+ import kotlinx.coroutines.flow.flow
15
+ import kotlinx.coroutines.flow.take
16
+ import kotlinx.coroutines.reactive.asFlow
17
+ import kotlinx.coroutines.runBlocking
18
+ import mu.KLogging
19
+ import org.amshove.kluent.shouldBeEqualTo
20
+ import org.junit.jupiter.api.AfterAll
21
+ import org.junit.jupiter.api.Test
22
+ import org.springframework.beans.factory.getBean
23
+ import org.springframework.context.annotation.AnnotationConfigApplicationContext
24
+ import org.springframework.context.annotation.Bean
25
+ import org.springframework.context.annotation.Configuration
26
+ import org.springframework.http.codec.cbor.Jackson2CborDecoder
27
+ import org.springframework.http.codec.cbor.Jackson2CborEncoder
28
+ import org.springframework.messaging.handler.annotation.MessageMapping
29
+ import org.springframework.messaging.rsocket.RSocketRequester
30
+ import org.springframework.messaging.rsocket.RSocketStrategies
31
+ import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler
32
+ import org.springframework.messaging.rsocket.retrieveAndAwait
33
+ import org.springframework.messaging.rsocket.retrieveAndAwaitOrNull
34
+ import org.springframework.messaging.rsocket.retrieveFlow
35
+ import org.springframework.util.MimeTypeUtils
36
+ import reactor.core.publisher.Flux
37
+ import reactor.core.publisher.Mono
38
+ import java.time.Duration
39
+
40
+ class RSocketControllerTest {
41
+
42
+ companion object : KLogging ()
43
+
44
+ class ClientHandler {
45
+
46
+ companion object : KLogging ()
47
+
48
+ @MessageMapping(" client-status" )
49
+ fun statusUpdate (status : String ): Flow <String > {
50
+ logger.info { " Connection: $status " }
51
+ return flow {
52
+ delay(5000 )
53
+ emit(Runtime .getRuntime().freeMemory().toString())
54
+ }
55
+ }
56
+ }
57
+
58
+ @Configuration
59
+ class ServerConfig {
60
+ @Bean
61
+ fun controller (): RSocketController = RSocketController ()
62
+
63
+ @Bean
64
+ fun messageHandler (): RSocketMessageHandler = RSocketMessageHandler ().apply {
65
+ rSocketStrategies = rsocketStrategies()
66
+ }
67
+
68
+ @Bean
69
+ fun rsocketStrategies (): RSocketStrategies =
70
+ RSocketStrategies .builder()
71
+ .encoder(Jackson2CborEncoder ())
72
+ .decoder(Jackson2CborDecoder ())
73
+ .build()
74
+ }
75
+
76
+ // 자제적인 Bean 설정을 가지고 테스트를 수행합니다.
77
+ private val metadataMimeType = MimeTypeUtils .parseMimeType(WellKnownMimeType .MESSAGE_RSOCKET_ROUTING .string)
78
+ private val context = AnnotationConfigApplicationContext (ServerConfig ::class .java)
79
+ private val messageHandler = context.getBean<RSocketMessageHandler >()
80
+ private val responder: SocketAcceptor = messageHandler.responder()
81
+
82
+ private val server: CloseableChannel =
83
+ RSocketServer .create(responder)
84
+ .payloadDecoder(PayloadDecoder .ZERO_COPY )
85
+ .bind(TcpServerTransport .create(" localhost" , 7001 ))
86
+ .block()!!
87
+
88
+ private val requester: RSocketRequester =
89
+ RSocketRequester .builder()
90
+ .metadataMimeType(metadataMimeType)
91
+ .rsocketStrategies(context.getBean<RSocketStrategies >())
92
+ .tcp(" localhost" , 7001 )
93
+
94
+ @AfterAll
95
+ fun cleanup () {
96
+ requester.rsocket()?.dispose()
97
+ server.dispose()
98
+ }
99
+
100
+ @Test
101
+ fun `fire and forget` () = runBlocking<Unit > {
102
+ val result = requester.route(" fire-and-forget" )
103
+ .data(Message (" TEST" , " Fire-And-Forget" ))
104
+ .retrieveAndAwaitOrNull<Unit >()
105
+ }
106
+
107
+ @Test
108
+ fun `request get response` () = runBlocking<Unit > {
109
+ val result = requester.route(" request-response" )
110
+ .data(Message (" TEST" , " Request" ))
111
+ .retrieveAndAwait<Message >()
112
+
113
+ result shouldBeEqualTo Message (RSocketController .SERVER , RSocketController .RESPONSE , 0 )
114
+ }
115
+
116
+ @Test
117
+ fun `request get stream` () = runBlocking<Unit > {
118
+ val result = requester.route(" stream" )
119
+ .data(Message (" TEST" , " Stream" ))
120
+ .retrieveFlow<Message >()
121
+
122
+ result.take(5 ).collectIndexed { i, m ->
123
+ m shouldBeEqualTo Message (RSocketController .SERVER , RSocketController .STREAM , i.toLong())
124
+ }
125
+ }
126
+
127
+ @Test
128
+ fun `channel - stream get stream` () = runBlocking<Unit > {
129
+ val setting1 = Mono .just(Duration .ofSeconds(6 )).delayElement(Duration .ofSeconds(0 ))
130
+ val setting2 = Mono .just(Duration .ofSeconds(6 )).delayElement(Duration .ofSeconds(9 ))
131
+ val settings = Flux .concat(setting1, setting2).asFlow()
132
+
133
+ val result = requester.route(" channel" )
134
+ .data(settings)
135
+ .retrieveFlow<Message >()
136
+
137
+ try {
138
+ result.take(2 ).collect {
139
+ it shouldBeEqualTo Message (RSocketController .SERVER , RSocketController .CHANNEL , 0L )
140
+ }
141
+ } catch (e: Exception ) {
142
+ logger.error(e) { " Canceled." }
143
+ }
144
+ }
145
+
146
+
147
+ }
0 commit comments