2
0
Эх сурвалжийг харах

Improve LoginCommand logic, remove unsafe checks, and add tests

Him188 4 жил өмнө
parent
commit
366ea34fde

+ 25 - 16
mirai-console/backend/mirai-console/src/command/BuiltInCommands.kt

@@ -50,6 +50,9 @@ import net.mamoe.mirai.console.util.*
 import net.mamoe.mirai.event.events.EventCancelledException
 import net.mamoe.mirai.message.nextMessageOrNull
 import net.mamoe.mirai.utils.BotConfiguration
+import net.mamoe.mirai.utils.Either
+import net.mamoe.mirai.utils.Either.Companion.flatMapNull
+import net.mamoe.mirai.utils.Either.Companion.fold
 import net.mamoe.mirai.utils.MiraiLogger
 import net.mamoe.mirai.utils.secondsToMillis
 import java.lang.management.ManagementFactory
@@ -188,9 +191,12 @@ public object BuiltInCommands {
         ConsoleCommandOwner, "login", "登录",
         description = "登录一个账号",
     ), BuiltInCommandInternal {
-        private suspend fun Bot.doLogin() = kotlin.runCatching {
-            login(); this
-        }.onFailure { close() }.getOrThrow()
+        internal var doLogin: suspend Bot.() -> Bot = {
+            kotlin.runCatching {
+                login()
+                this
+            }.onFailure { close() }.getOrThrow()
+        } // workaround since LoginCommand is object
 
         @Handler
         @JvmOverloads
@@ -204,24 +210,27 @@ public object BuiltInCommands {
                 return this
             }
 
-            suspend fun getPassword(id: Long): Any? {
+            fun getPassword(id: Long): Either<ByteArray, String?> {
                 val config = DataScope.get<AutoLoginConfig>()
-                val acc = config.accounts.firstOrNull { it.account == id.toString() }
-                if (acc == null) {
-                    sendMessage("Could not find '$id' in AutoLogin config. Please specify password.")
-                    return null
-                }
+                val acc = config.accounts.firstOrNull { it.account == id.toString() } ?: return Either.right(null)
                 val strv = acc.password.value
-                return if (acc.password.kind == MD5) strv.autoHexToBytes() else strv
+                return if (acc.password.kind == MD5) Either.left(strv.autoHexToBytes()) else Either.right(strv)
             }
 
-            val pwd: Any = password ?: getPassword(id) ?: return
+            val pwd = Either.right<ByteArray, String?>(password).flatMapNull { getPassword(id) }
             kotlin.runCatching {
-                when (pwd) {
-                    is String -> MiraiConsole.addBot(id, pwd) { setup(protocol) }.doLogin()
-                    is ByteArray -> MiraiConsole.addBot(id, pwd) { setup(protocol) }.doLogin()
-                    else -> throw AssertionError("Assertion failed, please report to https://github.com/mamoe/mirai-console/issues/new/choose, debug=${pwd.javaClass}")// Unreachable
-                }
+                pwd.fold(
+                    onLeft = { pass ->
+                        MiraiConsole.addBot(id, pass) { setup(protocol) }.doLogin()
+                    },
+                    onRight = { pass ->
+                        if (pass == null) {
+                            sendMessage("Could not find '$id' in AutoLogin config. Please specify password.")
+                            return
+                        }
+                        MiraiConsole.addBot(id, pass) { setup(protocol) }.doLogin()
+                    }
+                )
             }.fold(
                 onSuccess = { scopeWith(ConsoleCommandSender).sendMessage("${it.nick} ($id) Login successful") },
                 onFailure = { throwable ->

+ 19 - 0
mirai-console/backend/mirai-console/test/command/AbstractCommandTest.kt

@@ -0,0 +1,19 @@
+/*
+ * Copyright 2019-2022 Mamoe Technologies and contributors.
+ *
+ * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
+ * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+package net.mamoe.mirai.console.command
+
+import net.mamoe.mirai.console.internal.data.builtins.ConsoleDataScopeImpl
+import net.mamoe.mirai.console.internal.data.builtins.DataScope
+import net.mamoe.mirai.console.testFramework.AbstractConsoleInstanceTest
+
+internal abstract class AbstractCommandTest : AbstractConsoleInstanceTest() {
+    val dataScope get() = DataScope as ConsoleDataScopeImpl
+    val consoleSender get() = ConsoleCommandSender
+}

+ 118 - 0
mirai-console/backend/mirai-console/test/command/LoginCommandTest.kt

@@ -0,0 +1,118 @@
+/*
+ * Copyright 2019-2022 Mamoe Technologies and contributors.
+ *
+ * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
+ * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
+
+package net.mamoe.mirai.console.command
+
+import kotlinx.coroutines.CompletableDeferred
+import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge
+import net.mamoe.mirai.console.command.BuiltInCommands.LoginCommand
+import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
+import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig
+import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig.Account
+import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig.Account.PasswordKind
+import net.mamoe.mirai.utils.md5
+import net.mamoe.mirai.utils.toUHexString
+import org.junit.jupiter.api.Test
+import kotlin.test.assertContentEquals
+import kotlin.test.assertEquals
+
+@OptIn(ExperimentalCommandDescriptors::class)
+@JvmBlockingBridge
+internal class LoginCommandTest : AbstractCommandTest() {
+
+    @Test
+    suspend fun `login with provided password`() {
+        val myId = 123L
+        val myPwd = "password001"
+
+        val bot = awaitDeferred<net.mamoe.mirai.internal.QQAndroidBot> { cont ->
+            LoginCommand.doLogin = {
+                val bot = bot as net.mamoe.mirai.internal.QQAndroidBot
+                cont.complete(bot)
+                bot
+            }
+
+            LoginCommand.execute(consoleSender, "$myId $myPwd")
+        }
+
+        val account = bot.account
+        assertContentEquals(myPwd.md5(), account.passwordMd5)
+        assertEquals(myId, account.id)
+    }
+
+    @Test
+    suspend fun `login with saved plain password`() {
+        val myId = 123L
+        val myPwd = "password001"
+
+        dataScope.set(AutoLoginConfig().apply {
+            accounts.add(
+                Account(
+                    account = myId.toString(),
+                    password = Account.Password(PasswordKind.PLAIN, myPwd)
+                )
+            )
+        })
+
+        val bot = awaitDeferred<net.mamoe.mirai.internal.QQAndroidBot> { cont ->
+            LoginCommand.doLogin = {
+                val bot = bot as net.mamoe.mirai.internal.QQAndroidBot
+                cont.complete(bot)
+                bot
+            }
+
+            LoginCommand.execute(consoleSender, "$myId")
+        }
+
+        val account = bot.account
+        assertContentEquals(myPwd.md5(), account.passwordMd5)
+        assertEquals(myId, account.id)
+    }
+
+    @Test
+    suspend fun `login with saved md5 password`() {
+        val myId = 123L
+        val myPwd = "password001"
+
+        dataScope.set(AutoLoginConfig().apply {
+            accounts.add(
+                Account(
+                    account = myId.toString(),
+                    password = Account.Password(PasswordKind.MD5, myPwd.md5().toUHexString(""))
+                )
+            )
+        })
+
+        val bot = awaitDeferred<net.mamoe.mirai.internal.QQAndroidBot> { cont ->
+            LoginCommand.doLogin = {
+                val bot = bot as net.mamoe.mirai.internal.QQAndroidBot
+                cont.complete(bot)
+                bot
+            }
+
+            LoginCommand.execute(consoleSender, "$myId")
+        }
+
+        val account = bot.account
+        assertContentEquals(myPwd.md5(), account.passwordMd5)
+        assertEquals(myId, account.id)
+    }
+}
+
+@BuilderInference
+internal suspend inline fun <T> awaitDeferred(
+    @BuilderInference
+    crossinline block: suspend (CompletableDeferred<T>) -> Unit
+): T {
+    val deferred = CompletableDeferred<T>()
+    block(deferred)
+    return deferred.await()
+}