2020
2121package love.forte.simbot.kook.api.channel
2222
23+ import io.ktor.client.*
24+ import io.ktor.client.engine.mock.*
2325import io.ktor.http.*
26+ import kotlinx.coroutines.test.runTest
2427import kotlinx.serialization.json.Json
28+ import kotlinx.serialization.json.jsonObject
29+ import kotlinx.serialization.json.jsonPrimitive
2530import love.forte.simbot.kook.Kook
31+ import love.forte.simbot.kook.api.ApiResult
32+ import love.forte.simbot.kook.api.requestResult
2633import kotlin.test.Test
2734import kotlin.test.assertEquals
2835import kotlin.test.assertNotNull
2936import kotlin.test.assertTrue
3037
3138/* *
32- * Tests for [ChannelKickoutApi] focusing on API structure and serialization.
33- *
39+ * Comprehensive tests for [ChannelKickoutApi] focusing on API structure, serialization/deserialization,
40+ * and realistic scenarios based on official documentation.
41+ *
42+ * Tests include JSON serialization validation, API structure verification,
43+ * and response deserialization using patterns from official KOOK API documentation.
44+ *
3445 * @author ForteScarlet
46+ * @since 4.2.0
3547 */
3648class ChannelKickoutApiTest {
3749 private val json = Json (Kook .DEFAULT_JSON ) {
3850 ignoreUnknownKeys = true
51+ prettyPrint = true
3952 }
4053
4154 @Test
4255 fun testApiBasics () {
43- val channelId = " 123456789 "
44- val userId = " 987654321 "
56+ val channelId = " 3321010478582002 "
57+ val userId = " 1700000 "
4558 val api = ChannelKickoutApi .create(channelId, userId)
46-
59+
4760 // Test API properties
4861 assertEquals(HttpMethod .Post , api.method)
49- assertTrue(api.url.toString().contains( " channel/kickout" ))
50-
62+ assertEquals( " https://www.kookapp.cn/api/v3/ channel/kickout" , api.url.toString( ))
63+
5164 // Test body content
5265 val body = api.body
5366 assertNotNull(body, " Body should not be null" )
5467 }
55-
68+
5669 @Test
5770 fun testUrlConstruction () {
5871 val api = ChannelKickoutApi .create(" test_channel" , " test_user" )
5972 val url = api.url.toString()
60-
73+
6174 // Verify URL contains correct Kook API base and path
62- assertTrue(url.contains(" kookapp.cn/api/v3" ))
63- assertTrue(url.contains(" channel/kickout" ))
75+ assertEquals(" https://www.kookapp.cn/api/v3/channel/kickout" , url)
6476 }
65-
77+
6678 @Test
67- fun testRequestBodyNotNull () {
68- val channelId = " voice_channel_123 "
69- val userId = " user_456 "
79+ fun testRequestBodyStructure () = runTest {
80+ val channelId = " 3321010478582002 "
81+ val userId = " 1700000 "
7082 val api = ChannelKickoutApi .create(channelId, userId)
83+
84+ // Capture actual request body using MockEngine
85+ var capturedRequestBody: String? = null
86+ val mockEngine = MockEngine { request ->
87+ capturedRequestBody = request.body.toByteArray().decodeToString()
88+ respond(
89+ content = """ {"code": 0, "message": "操作成功", "data": []}""" ,
90+ status = HttpStatusCode .OK ,
91+ headers = headersOf(HttpHeaders .ContentType , " application/json" )
92+ )
93+ }
94+
95+ val client = HttpClient (mockEngine)
7196
72- val body = api. body
73- assertNotNull(body , " Body should not be null " )
97+ // Make actual request to capture serialized body
98+ api.requestResult(client , " Bot test-token " )
7499
75- // Verify that the body object exists and is not null
76- assertTrue(body.toString().isNotEmpty())
100+ // Validate the captured JSON body structure
101+ assertNotNull(capturedRequestBody, " Request body should be captured" )
102+ val requestJson = json.parseToJsonElement(capturedRequestBody).jsonObject
103+
104+ // Verify JSON structure matches expected serialization
105+ assertEquals(channelId, requestJson[" channel_id" ]?.jsonPrimitive?.content)
106+ assertEquals(userId, requestJson[" user_id" ]?.jsonPrimitive?.content)
107+ assertEquals(2 , requestJson.size)
77108 }
78-
109+
79110 @Test
80- fun testMultipleApiInstances () {
81- val api1 = ChannelKickoutApi .create(" channel1" , " user1" )
82- val api2 = ChannelKickoutApi .create(" channel2" , " user2" )
83-
84- // Verify that different instances have different bodies
85- val body1 = api1.body
86- val body2 = api2.body
87-
88- assertNotNull(body1)
89- assertNotNull(body2)
90-
91- // Bodies should be different objects (different parameters)
92- assertTrue(body1 != = body2, " Different API instances should have different body objects" )
111+ fun testSuccessfulResponseDeserialization () {
112+ // Based on official documentation example
113+ // language=json
114+ val successResponseJson = """ {
115+ "code": 0,
116+ "message": "操作成功",
117+ "data": []
118+ }"""
119+
120+ val apiResult = json.decodeFromString(ApiResult .serializer(), successResponseJson)
121+
122+ assertNotNull(apiResult)
123+ assertEquals(0 , apiResult.code)
124+ assertEquals(" 操作成功" , apiResult.message)
125+ assertNotNull(apiResult.data)
126+ }
127+
128+ @Test
129+ fun testErrorResponseDeserialization () {
130+ // Test realistic error scenario - channel doesn't exist or not a voice channel
131+ // language=json
132+ val errorResponseJson = """ {
133+ "code": 40001,
134+ "message": "频道不存在或不是语音频道"
135+ }"""
136+
137+ val apiResult = json.decodeFromString(ApiResult .serializer(), errorResponseJson)
138+
139+ assertNotNull(apiResult)
140+ assertEquals(40001 , apiResult.code)
141+ assertEquals(" 频道不存在或不是语音频道" , apiResult.message)
93142 }
94-
143+
95144 @Test
96145 fun testApiFactory () {
97- val channelId = " factory_test_channel "
98- val userId = " factory_test_user "
99-
146+ val channelId = " 7480000000000000 "
147+ val userId = " 2418000000 "
148+
100149 // Test factory method
101150 val api = ChannelKickoutApi .create(channelId, userId)
102151 assertNotNull(api)
103-
152+
104153 // Verify the created API has the correct properties
105154 assertEquals(HttpMethod .Post , api.method)
106155 assertNotNull(api.body)
107156 assertNotNull(api.url)
157+
158+ // Verify API path structure
159+ val url = api.url
160+ assertEquals(" https://www.kookapp.cn/api/v3/channel/kickout" , url.toString())
161+ assertEquals(listOf (" " , " api" , " v3" , " channel" , " kickout" ), url.pathSegments)
108162 }
109-
163+
110164 @Test
111- fun testApiPathCorrectness () {
112- val api = ChannelKickoutApi .create(" test" , " test" )
113- val url = api.url
114-
115- // Check that the URL path is correct
116- assertTrue(url.pathSegments.contains(" channel" ))
117- assertTrue(url.pathSegments.contains(" kickout" ))
118-
119- // Verify it's a POST API
120- assertEquals(HttpMethod .Post , api.method)
165+ fun testMultipleApiInstances () {
166+ val api1 = ChannelKickoutApi .create(" 3321010478582001" , " 1700001" )
167+ val api2 = ChannelKickoutApi .create(" 3321010478582002" , " 1700002" )
168+
169+ // Verify that different instances have different bodies
170+ val body1 = api1.body
171+ val body2 = api2.body
172+
173+ assertNotNull(body1)
174+ assertNotNull(body2)
175+
176+ // Bodies should be different objects (different parameters)
177+ assertTrue(body1 != = body2, " Different API instances should have different body objects" )
178+
179+ // Verify they have different string representations
180+ val string1 = body1.toString()
181+ val string2 = body2.toString()
182+ assertTrue(string1 != string2, " Different instances should have different string representations" )
121183 }
122-
184+
123185 @Test
124- fun testBodyNotNull () {
125- val api = ChannelKickoutApi .create(" channel_test" , " user_test" )
186+ fun testRealisticScenario () = runTest {
187+ // Test with realistic IDs from documentation
188+ val voiceChannelId = " 3321010478582002" // Voice channel from docs
189+ val userId = " 1700000" // User ID from docs
190+
191+ val api = ChannelKickoutApi .create(voiceChannelId, userId)
192+
193+ // Verify API structure
194+ assertEquals(HttpMethod .Post , api.method)
195+ assertEquals(" https://www.kookapp.cn/api/v3/channel/kickout" , api.url.toString())
196+
197+ // Capture actual request body using MockEngine
198+ var capturedRequestBody: String? = null
199+ val mockEngine = MockEngine { request ->
200+ capturedRequestBody = request.body.toByteArray().decodeToString()
201+ respond(
202+ content = """ {"code": 0, "message": "操作成功", "data": []}""" ,
203+ status = HttpStatusCode .OK ,
204+ headers = headersOf(HttpHeaders .ContentType , " application/json" )
205+ )
206+ }
207+
208+ val client = HttpClient (mockEngine)
126209
127- // Body should never be null for this API
128- assertNotNull( api.body )
210+ // Make actual request to capture serialized body
211+ api.requestResult(client, " Bot test-token " )
129212
130- // Multiple calls to body should return the same non-null value
131- val body1 = api.body
132- val body2 = api.body
133- assertNotNull(body1)
134- assertNotNull(body2)
213+ // Validate the captured JSON body structure
214+ assertNotNull(capturedRequestBody, " Request body should be captured" )
215+ val requestJson = json.parseToJsonElement(capturedRequestBody).jsonObject
216+
217+ // Verify JSON structure matches expected serialization for realistic scenario
218+ assertEquals(voiceChannelId, requestJson[" channel_id" ]?.jsonPrimitive?.content)
219+ assertEquals(userId, requestJson[" user_id" ]?.jsonPrimitive?.content)
220+ assertEquals(2 , requestJson.size)
221+ }
222+
223+ @Test
224+ fun testParameterValidation () = runTest {
225+ // Test with various parameter combinations
226+ val testCases = listOf (
227+ " 7480000000000000" to " 2418000000" , // From documentation
228+ " 3321010478582002" to " 1700000" , // Voice channel example
229+ " test_channel_123" to " test_user_456" // Generic test case
230+ )
231+
232+ testCases.forEach { (channelId, userId) ->
233+ val api = ChannelKickoutApi .create(channelId, userId)
234+
235+ // Verify each API instance is properly constructed
236+ assertEquals(HttpMethod .Post , api.method)
237+ assertNotNull(api.body)
238+ assertNotNull(api.url)
239+
240+ // Capture actual request body using MockEngine for each test case
241+ var capturedRequestBody: String? = null
242+ val mockEngine = MockEngine { request ->
243+ capturedRequestBody = request.body.toByteArray().decodeToString()
244+ respond(
245+ content = """ {"code": 0, "message": "操作成功", "data": []}""" ,
246+ status = HttpStatusCode .OK ,
247+ headers = headersOf(HttpHeaders .ContentType , " application/json" )
248+ )
249+ }
250+
251+ val client = HttpClient (mockEngine)
252+
253+ // Make actual request to capture serialized body
254+ api.requestResult(client, " Bot test-token" )
255+
256+ // Validate the captured JSON body structure for each parameter combination
257+ assertNotNull(capturedRequestBody, " Request body should be captured for channelId=$channelId , userId=$userId " )
258+ val requestJson = json.parseToJsonElement(capturedRequestBody).jsonObject
259+
260+ // Verify JSON structure matches expected serialization
261+ assertEquals(channelId, requestJson[" channel_id" ]?.jsonPrimitive?.content)
262+ assertEquals(userId, requestJson[" user_id" ]?.jsonPrimitive?.content)
263+ assertEquals(2 , requestJson.size)
264+ }
265+ }
266+
267+ @Test
268+ fun testApiResultStructureWithWrapper () {
269+ // Test that API result properly handles KOOK's wrapper structure
270+
271+ // language=json
272+ val wrappedResponseJson = """ {
273+ "code": 0,
274+ "message": "操作成功",
275+ "data": []
276+ }"""
277+
278+ val result = json.decodeFromString(ApiResult .serializer(), wrappedResponseJson)
279+
280+ // Verify wrapper structure is properly handled
281+ assertNotNull(result)
282+ assertEquals(0 , result.code)
283+ assertEquals(" 操作成功" , result.message)
284+ assertNotNull(result.data)
135285 }
136- }
286+ }
0 commit comments