Skip to content

Commit 121cd6c

Browse files
authored
Merge pull request #892 from simple-robot/fix-arg-type-mismatch-using-filtervalue
修复使用KeywordBinder时无法正确处理参数类型的问题
2 parents 3f506ef + ef31604 commit 121cd6c

File tree

4 files changed

+172
-4
lines changed
  • simbot-cores/simbot-core-spring-boot-starter/src/test/kotlin/love/forte/simbot/spring/test
  • simbot-quantcat/simbot-quantcat-common/src

4 files changed

+172
-4
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
/*
2+
* Copyright (c) 2024. ForteScarlet.
3+
*
4+
* Project https://github.com/simple-robot/simpler-robot
5+
6+
*
7+
* This file is part of the Simple Robot Library (Alias: simple-robot, simbot, etc.).
8+
*
9+
* This program is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU Lesser General Public License as published by
11+
* the Free Software Foundation, either version 3 of the License, or
12+
* (at your option) any later version.
13+
*
14+
* This program is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
* Lesser GNU General Public License for more details.
18+
*
19+
* You should have received a copy of the Lesser GNU General Public License
20+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
21+
*
22+
*/
23+
24+
package love.forte.simbot.spring.test
25+
26+
import io.mockk.*
27+
import kotlinx.coroutines.test.runTest
28+
import love.forte.simbot.annotations.InternalSimbotAPI
29+
import love.forte.simbot.common.attribute.AttributeMapContainer
30+
import love.forte.simbot.common.attribute.mutableAttributeMapOf
31+
import love.forte.simbot.common.attribute.set
32+
import love.forte.simbot.event.EventListener
33+
import love.forte.simbot.event.EventListenerContext
34+
import love.forte.simbot.event.MessageEvent
35+
import love.forte.simbot.event.handleWith
36+
import love.forte.simbot.quantcat.common.annotations.Filter
37+
import love.forte.simbot.quantcat.common.annotations.FilterValue
38+
import love.forte.simbot.quantcat.common.binder.ParameterBinderFactory
39+
import love.forte.simbot.quantcat.common.binder.impl.KeywordBinderFactory
40+
import love.forte.simbot.quantcat.common.convert.NonConverters
41+
import love.forte.simbot.quantcat.common.keyword.KeywordListAttribute
42+
import love.forte.simbot.quantcat.common.keyword.SimpleKeyword
43+
import love.forte.simbot.spring.configuration.binder.DefaultBinderManagerProvidersConfiguration
44+
import love.forte.simbot.spring.configuration.listener.KFunctionEventListenerImpl
45+
import org.springframework.beans.factory.annotation.Autowired
46+
import org.springframework.boot.SpringBootConfiguration
47+
import org.springframework.boot.test.context.SpringBootTest
48+
import kotlin.test.Test
49+
import kotlin.test.assertEquals
50+
import kotlin.test.assertNotNull
51+
import kotlin.test.assertTrue
52+
53+
54+
/**
55+
*
56+
* @author ForteScarlet
57+
*/
58+
@SpringBootTest(
59+
classes = [
60+
KeywordBinderTests::class,
61+
DefaultBinderManagerProvidersConfiguration::class,
62+
]
63+
)
64+
@SpringBootConfiguration
65+
open class KeywordBinderTests {
66+
67+
@Suppress("MemberVisibilityCanBePrivate")
68+
fun doParam(value: Int) {
69+
println("param.value = $value")
70+
}
71+
72+
@Filter("(?<value>\\d+)num")
73+
fun param(
74+
@FilterValue("value")
75+
value: Int
76+
) {
77+
doParam(value)
78+
}
79+
80+
@OptIn(InternalSimbotAPI::class)
81+
@Test
82+
fun keywordBinderTest(
83+
@Autowired factory: KeywordBinderFactory
84+
) {
85+
mockkObject(NonConverters)
86+
87+
val p = ::param.parameters.first()
88+
89+
val context = mockk<ParameterBinderFactory.Context>()
90+
91+
every { context.parameter } returns p
92+
93+
val binderResult = factory.resolveToBinder(context)
94+
95+
val binder0 = binderResult.binder
96+
assertNotNull(binder0)
97+
val binder = spyk(binder0, recordPrivateCalls = true)
98+
99+
val binderContext = mockk<EventListenerContext>(relaxed = true)
100+
val event = mockk<MessageEvent>()
101+
val listener = mockk<EventListener>(
102+
moreInterfaces = arrayOf(AttributeMapContainer::class)
103+
)
104+
105+
every { binderContext.listener } returns listener
106+
every { binderContext.event } returns event
107+
108+
every {
109+
(listener as AttributeMapContainer).attributeMap[KeywordListAttribute]
110+
} returns mutableListOf(SimpleKeyword("(?<value>\\d+)num", isPlainText = false))
111+
112+
every {
113+
binderContext.plainText
114+
} returns "123num"
115+
116+
val bindResult = binder.arg(binderContext)
117+
118+
verify { binder["convert"](any()) }
119+
verify { NonConverters.convert(any(), any()) }
120+
121+
assertTrue(bindResult.isSuccess)
122+
assertEquals(123, bindResult.getOrNull())
123+
}
124+
125+
126+
@Test
127+
fun kFunctionListenerImplKeywordBinderTest(
128+
@Autowired factory: KeywordBinderFactory
129+
) = runTest {
130+
val instance = spyk<KeywordBinderTests>()
131+
132+
val func = instance::param
133+
val context = mockk<ParameterBinderFactory.Context>()
134+
every { context.parameter } returns func.parameters.first()
135+
val binderResult = factory.resolveToBinder(context)
136+
val binder = assertNotNull(binderResult.binder)
137+
138+
val listener = KFunctionEventListenerImpl(
139+
instance = instance,
140+
caller = func,
141+
binders = arrayOf(binder),
142+
attributes = mutableAttributeMapOf().apply {
143+
set(
144+
KeywordListAttribute,
145+
mutableListOf(
146+
SimpleKeyword("(?<value>\\d+)num", isPlainText = false)
147+
)
148+
)
149+
},
150+
matcher = { true }
151+
)
152+
153+
val ec = mockk<EventListenerContext>(
154+
relaxed = true
155+
)
156+
every { ec.listener } returns listener
157+
every { ec.plainText } returns "123num"
158+
159+
listener.handleWith(ec)
160+
161+
verify { instance.doParam(any()) }
162+
}
163+
164+
}

simbot-quantcat/simbot-quantcat-common/src/commonMain/kotlin/love/forte/simbot/quantcat/common/convert/NonConverters.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
package love.forte.simbot.quantcat.common.convert
2525

26+
import love.forte.simbot.annotations.InternalSimbotAPI
2627
import kotlin.reflect.KClass
2728
import kotlin.reflect.cast
2829
import kotlin.reflect.safeCast
@@ -32,7 +33,8 @@ import kotlin.reflect.safeCast
3233
*
3334
* @author ForteScarlet
3435
*/
35-
internal object NonConverters {
36+
@InternalSimbotAPI
37+
public object NonConverters {
3638
private val primitives: Set<KClass<*>> = setOf(
3739
Byte::class, Short::class, Int::class, Long::class,
3840
UInt::class, ULong::class,
@@ -45,7 +47,7 @@ internal object NonConverters {
4547
*
4648
* @throws ConvertException if [type &#39;to&#39;][TO] is not assignable from [type &#39;from&#39;][FROM]
4749
*/
48-
fun <FROM : Any, TO : Any> convert(instance: FROM, to: KClass<TO>): TO {
50+
public fun <FROM : Any, TO : Any> convert(instance: FROM, to: KClass<TO>): TO {
4951
val fromType: KClass<out FROM> = instance::class
5052

5153
val safeCast: TO? = to.safeCast(instance)

simbot-quantcat/simbot-quantcat-common/src/jvmMain/kotlin/love/forte/simbot/quantcat/common/binder/impl/KeywordBinder.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
package love.forte.simbot.quantcat.common.binder.impl
2525

26+
import love.forte.simbot.annotations.InternalSimbotAPI
2627
import love.forte.simbot.common.attribute.AttributeMapContainer
2728
import love.forte.simbot.event.EventListenerContext
2829
import love.forte.simbot.quantcat.common.binder.BindException
@@ -46,7 +47,7 @@ public class KeywordBinderFactory(
4647
) : ParameterBinderFactory {
4748
override fun resolveToBinder(context: ParameterBinderFactory.Context): ParameterBinderResult {
4849
val value = filterValueReader(context) ?: return ParameterBinderResult.empty()
49-
val paramType = context.parameter.type as? KClass<*>
50+
val paramType = context.parameter.type.classifier as? KClass<*>
5051
return ParameterBinderResult.normal(
5152
if (value.required) {
5253
KeywordBinder.Required(value.value, paramType)
@@ -60,6 +61,7 @@ public class KeywordBinderFactory(
6061

6162
private sealed class KeywordBinder(val name: String, val paramType: KClass<*>?) : ParameterBinder {
6263

64+
@OptIn(InternalSimbotAPI::class)
6365
private val converter: (Any) -> Any = if (paramType != null && paramType != String::class) {
6466
{
6567
NonConverters.convert(instance = it, to = paramType)

0 commit comments

Comments
 (0)