WsTest.kt (6070B)
1 /* 2 * This file is part of LibEuFin. 3 * Copyright (C) 2024 Taler Systems S.A. 4 5 * LibEuFin is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU Affero General Public License as 7 * published by the Free Software Foundation; either version 3, or 8 * (at your option) any later version. 9 10 * LibEuFin is distributed in the hope that it will be useful, but 11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General 13 * Public License for more details. 14 15 * You should have received a copy of the GNU Affero General Public 16 * License along with LibEuFin; see the file COPYING. If not, see 17 * <http://www.gnu.org/licenses/> 18 */ 19 20 import io.ktor.http.HttpHeaders 21 import io.ktor.serialization.kotlinx.* 22 import io.ktor.server.application.* 23 import io.ktor.server.routing.* 24 import io.ktor.server.testing.* 25 import io.ktor.server.websocket.* 26 import io.ktor.websocket.* 27 import kotlinx.coroutines.channels.ClosedReceiveChannelException 28 import kotlinx.serialization.json.Json 29 import kotlinx.serialization.json.JsonObject 30 import kotlinx.serialization.json.decodeFromJsonElement 31 import kotlinx.serialization.json.encodeToJsonElement 32 import tech.libeufin.ebics.* 33 import kotlin.test.Test 34 import kotlin.test.assertEquals 35 import kotlin.test.assertIs 36 37 class WsTest { 38 // WSS params example from the spec 39 val PARAMS_EXAMPLE = """ 40 { 41 "URL": "https://bankmitwebsocket.de", 42 "TOKEN": "550e8400-e29b-11d4-a716-446655440000", 43 "OTT": "N", 44 "VALIDITY": "2019-03-21T10:35:22Z", 45 "PARTNERID": "K1234567", 46 "USERID": "USER4711" 47 } 48 """ 49 // Authorization header example from the spec 50 val AUTH_EXAMPLE = "Basic SzEyMzQ1NjdfVVNFUjQ3MTE6NTUwZTg0MDAtZTI5Yi0xMWQ0LWE3MTYtNDQ2NjU1NDQwMDAw" 51 // Notifications examples from the spec 52 val NOTIFICATION_EXAMPLES = sequenceOf( 53 """ 54 { 55 "MCLASS": [ 56 { 57 "NAME": "EBICS-HAA", 58 "VERS": "1.0", 59 "TIMESTAMP": "2019-05-13T12:21:50Z" 60 } 61 ], 62 "PARTNERID": "K1234567", 63 "USERID": "USER471", 64 "BTF": [ 65 { 66 "SERVICE": "REP", 67 "SCOPE": "DE", 68 "CONTTYPE": "ZIP", 69 "MSGNAME": "camt.054" 70 } 71 ], 72 "ORDERTYPE": [ 73 "C5N" 74 ] 75 } 76 """, """ 77 { 78 "MCLASS": [ 79 { 80 "NAME": "EBICS-HAA", 81 "VERS": "1.0", 82 "TIMESTAMP": "2019-05-13T12:21:53Z" 83 } 84 ], 85 "PARTNERID": "K1234567", 86 "USERID": "USER471", 87 "BTF": [ 88 { 89 "SERVICE": "REP", 90 "SCOPE": "DE", 91 "CONTTYPE": "ZIP", 92 "MSGNAME": "camt.052" 93 }, 94 { 95 "SERVICE": "REP", 96 "SCOPE": "DE", 97 "OPTION": "SCI", 98 "CONTTYPE": "ZIP", 99 "MSGNAME": "pain.002" 100 } 101 ], 102 "ORDERTYPE": [ 103 "C52", 104 "CIZ" 105 ] 106 } 107 """, """ 108 { 109 "MCLASS": [ 110 { 111 "NAME": "INFO", 112 "VERS": "1.0", 113 "TIMESTAMP": "2019-03-25T12:25:34Z" 114 } 115 ], 116 "INFO": [ 117 { 118 "LANG": "EN", 119 "FREE": " The EBICS-Service is limited on 30.03.2019 from 10:00 a.m. - 11:00a.m. due to maintenance work " 120 } 121 ] 122 } 123 """ 124 ) 125 126 /** Test JSON serialization roudtrip */ 127 inline fun <reified B> roundtrip(raw: String): B { 128 val json: JsonObject = Json.decodeFromString(raw) 129 val decoded: B = Json.decodeFromJsonElement(json) 130 val encoded = Json.encodeToJsonElement(decoded) 131 assertEquals(json, encoded) 132 return decoded 133 } 134 135 /** Test our serialization implementation works with spec examples */ 136 @Test 137 fun serialization() { 138 roundtrip<WssParams>(PARAMS_EXAMPLE) 139 for (raw in NOTIFICATION_EXAMPLES) { 140 roundtrip<WssNotification>(raw) 141 } 142 } 143 144 /** Test our implementation works with spec examples */ 145 @Test 146 fun wss() { 147 val params: WssParams = Json.decodeFromString(PARAMS_EXAMPLE) 148 149 testApplication { 150 externalServices { 151 hosts(params.URL.replace("https://", "wss://")) { 152 install(WebSockets) { 153 contentConverter = KotlinxWebsocketSerializationConverter(Json) 154 } 155 routing { 156 webSocket("/") { 157 assertEquals(AUTH_EXAMPLE, call.request.headers[HttpHeaders.Authorization]) 158 // Send all examples 159 for (example in NOTIFICATION_EXAMPLES) { 160 send(example) 161 } 162 close(CloseReason(CloseReason.Codes.NORMAL, "Test done")) 163 } 164 } 165 } 166 } 167 var count = 0 168 try { 169 params.connect(client) { msg -> 170 count++ 171 // Check message number and type 172 assert(count <= 3) 173 if (count == 3) { 174 assertIs<WssGeneralInfo>(msg) 175 } else { 176 assertIs<WssNewData>(msg) 177 } 178 } 179 } catch (e: ClosedReceiveChannelException) { 180 // Expected 181 } 182 // Check receive all messages 183 assertEquals(3, count) 184 } 185 } 186 }