From 92b7e84b3a7eebe981fcf94d1b5e71306d339fd4 Mon Sep 17 00:00:00 2001 From: LamGC Date: Tue, 12 Jul 2022 01:20:53 +0800 Subject: [PATCH] =?UTF-8?q?test(config):=20=E8=A1=A5=E5=85=85=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E7=9A=84=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 经检查, 已确定完全覆盖代码, 为完成单元测试的编写, 稍微改了一下 AppPaths 的代码, 不会有影响的 :P --- scalabot-app/src/main/kotlin/AppConfigs.kt | 15 + scalabot-app/src/test/kotlin/AppConfigTest.kt | 290 +++++++++++++++++- 2 files changed, 296 insertions(+), 9 deletions(-) diff --git a/scalabot-app/src/main/kotlin/AppConfigs.kt b/scalabot-app/src/main/kotlin/AppConfigs.kt index 5b6241a..0232725 100644 --- a/scalabot-app/src/main/kotlin/AppConfigs.kt +++ b/scalabot-app/src/main/kotlin/AppConfigs.kt @@ -167,6 +167,21 @@ internal enum class AppPaths( } } + /** + * 一个内部方法, 用于将 [initialized] 状态重置. + * + * 如果不重置该状态, 将使得单元测试无法让 AppPath 重新初始化文件. + * + * 警告: 该方法不应该被非测试代码调用. + */ + @Suppress("unused") + private fun reset() { + log.warn { + "初始化状态已重置: `${this.name}`, 如果在非测试环境中重置状态, 请报告该问题." + } + initialized.set(false) + } + override fun toString(): String { return path } diff --git a/scalabot-app/src/test/kotlin/AppConfigTest.kt b/scalabot-app/src/test/kotlin/AppConfigTest.kt index a3a6f92..cc96ad1 100644 --- a/scalabot-app/src/test/kotlin/AppConfigTest.kt +++ b/scalabot-app/src/test/kotlin/AppConfigTest.kt @@ -4,31 +4,38 @@ import com.github.stefanbirkner.systemlambda.SystemLambda import io.mockk.every import io.mockk.mockk import io.mockk.verify +import mu.KotlinLogging +import net.lamgc.scalabot.config.MavenRepositoryConfig +import net.lamgc.scalabot.config.ProxyConfig +import net.lamgc.scalabot.config.ProxyType +import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.io.TempDir +import org.telegram.telegrambots.bots.DefaultBotOptions import java.io.File -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNotNull -import kotlin.test.assertTrue +import java.io.IOException +import java.net.URL +import java.nio.file.Files +import java.nio.file.Path +import kotlin.test.* internal class AppPathsTest { @Test fun `Data root path priority`() { - System.setProperty("bot.path.data", "fromSystemProperties") + System.setProperty(AppPaths.PathConst.PROP_DATA_PATH, "fromSystemProperties") assertEquals("fromSystemProperties", AppPaths.DATA_ROOT.file.path, "`DATA_ROOT`没有优先返回 Property 的值.") - System.getProperties().remove("bot.path.data") + System.getProperties().remove(AppPaths.PathConst.PROP_DATA_PATH) val expectEnvValue = "fromEnvironmentVariable" - SystemLambda.withEnvironmentVariable("BOT_DATA_PATH", expectEnvValue).execute { + SystemLambda.withEnvironmentVariable(AppPaths.PathConst.ENV_DATA_PATH, expectEnvValue).execute { assertEquals( expectEnvValue, AppPaths.DATA_ROOT.file.path, "`DATA_ROOT`没有优先返回 env 的值." ) } - SystemLambda.withEnvironmentVariable("BOT_DATA_PATH", null).execute { + SystemLambda.withEnvironmentVariable(AppPaths.PathConst.ENV_DATA_PATH, null).execute { assertEquals( System.getProperty("user.dir"), AppPaths.DATA_ROOT.file.path, "`DATA_ROOT`没有返回 System.properties `user.dir` 的值." @@ -116,5 +123,270 @@ internal class AppPathsTest { defaultInitializerMethod.isAccessible = false } -} + @Test + fun `loadBotConfig test`(@TempDir testDir: File) { + assertNull(loadBotConfigJson(File("/NOT_EXISTS_FILE")), "加载 BotConfigs 失败时应该返回 null.") + SystemLambda.withEnvironmentVariable(AppPaths.PathConst.ENV_DATA_PATH, testDir.canonicalPath).execute { + assertNull(loadBotConfigJson(), "加载 BotConfigs 失败时应该返回 null.") + + File(testDir, "bot.json").apply { + //language=JSON5 + writeText( + """ + [ + { + "enabled": false, + "account": { + "name": "TestBot", + "token": "123456789:AAHErDroUTznQsOd_oZPJ6cQEj4Z5mGHO10", + "creatorId": 123456789 + }, + "proxy": { + "host": "localhost", + "port": 8080, + "type": "HTTP" + }, + "disableBuiltInAbility": false, + "autoUpdateCommandList": true, + "extensions": [ + "org.example.test:test-extension:1.0.0" + ], + "baseApiUrl": "http://localhost:8080" + } + ] + """.trimIndent() + ) + } + + val botConfigJsons = loadBotConfigJson() + assertNotNull(botConfigJsons) + assertEquals(1, botConfigJsons.size()) + } + } + + @Test + fun `loadAppConfig test`(@TempDir testDir: File) { + assertThrows("加载失败时应该抛出 IOException.") { + loadAppConfig(File("/NOT_EXISTS_FILE")) + } + + SystemLambda.withEnvironmentVariable(AppPaths.PathConst.ENV_DATA_PATH, testDir.canonicalPath).execute { + assertThrows("加载失败时应该抛出 IOException.") { + loadAppConfig() + } + + File(testDir, "config.json").apply { + //language=JSON5 + writeText( + """ + { + "proxy": { + "type": "HTTP", + "host": "localhost", + "port": 8080 + }, + "metrics": { + "enable": true, + "port": 8800, + "bindAddress": "127.0.0.1", + "authenticator": { + "username": "username", + "password": "password" + } + }, + "mavenRepositories": [ + { + "url": "https://repository.maven.apache.org/maven2/" + } + ], + "mavenLocalRepository": "file:///tmp/maven-local-repository" + } + """.trimIndent() + ) + } + + val appConfigs = loadAppConfig() + assertNotNull(appConfigs) + } + } + + @Test + fun `ProxyType_toTelegramBotsType test`() { + val expectTypeMapping = mapOf( + ProxyType.NO_PROXY to DefaultBotOptions.ProxyType.NO_PROXY, + ProxyType.SOCKS5 to DefaultBotOptions.ProxyType.SOCKS5, + ProxyType.SOCKS4 to DefaultBotOptions.ProxyType.SOCKS4, + ProxyType.HTTP to DefaultBotOptions.ProxyType.HTTP, + ProxyType.HTTPS to DefaultBotOptions.ProxyType.HTTP + ) + + for (proxyType in ProxyType.values()) { + assertEquals( + expectTypeMapping[proxyType], + proxyType.toTelegramBotsType(), + "ProxyType 转换失败." + ) + } + } + + @Test + fun `ProxyConfig_toAetherProxy test`() { + val host = "proxy.example.org" + val port = 1080 + + val expectNotNullProxyType = setOf( + ProxyType.HTTP, + ProxyType.HTTPS + ) + for (proxyType in ProxyType.values()) { + val proxyConfig = ProxyConfig(proxyType, host, port) + val aetherProxy = proxyConfig.toAetherProxy() + if (expectNotNullProxyType.contains(proxyType)) { + assertNotNull(aetherProxy, "支持的代理类型应该不为 null.") + assertEquals(host, aetherProxy.host) + assertEquals(port, aetherProxy.port) + } else { + assertNull(aetherProxy, "不支持的代理类型应该返回 null.") + } + } + } + + @Test + fun `MavenRepositoryConfig_toRemoteRepository test`() { + val defaultMavenRepositoryConfig = MavenRepositoryConfig( + url = URL(MavenRepositoryExtensionFinder.MAVEN_CENTRAL_URL), + enableReleases = true, + enableSnapshots = false + ) + val remoteRepositoryWithoutId = defaultMavenRepositoryConfig.toRemoteRepository( + ProxyConfig(ProxyType.NO_PROXY, "", 0) + ) + assertEquals(MavenRepositoryExtensionFinder.MAVEN_CENTRAL_URL, remoteRepositoryWithoutId.url.toString()) + assertNotNull(remoteRepositoryWithoutId.id) + assertTrue(remoteRepositoryWithoutId.getPolicy(false).isEnabled) + assertFalse(remoteRepositoryWithoutId.getPolicy(true).isEnabled) + + val remoteRepositoryWithId = defaultMavenRepositoryConfig.copy(id = "test-repo").toRemoteRepository( + ProxyConfig(ProxyType.HTTP, "127.0.0.1", 1080) + ) + + assertEquals("test-repo", remoteRepositoryWithId.id) + assertEquals(MavenRepositoryExtensionFinder.MAVEN_CENTRAL_URL, remoteRepositoryWithId.url.toString()) + assertEquals("http", remoteRepositoryWithId.proxy.type) + assertEquals("127.0.0.1", remoteRepositoryWithId.proxy.host) + assertEquals(1080, remoteRepositoryWithId.proxy.port) + assertEquals(remoteRepositoryWithId.id, remoteRepositoryWithId.id) + + val remoteRepositoryWithProxy = defaultMavenRepositoryConfig.copy( + id = "test-repo", + proxy = ProxyConfig(ProxyType.HTTP, "example.org", 1080).toAetherProxy() + ).toRemoteRepository(ProxyConfig(ProxyType.HTTP, "localhost", 8080)) + assertEquals("http", remoteRepositoryWithProxy.proxy.type) + assertEquals("example.org", remoteRepositoryWithProxy.proxy.host, "未优先使用 MavenRepositoryConfig 中的 proxy 属性.") + assertEquals(1080, remoteRepositoryWithProxy.proxy.port, "未优先使用 MavenRepositoryConfig 中的 proxy 属性.") + } + + @Test + fun `checkRepositoryLayout test`() { + val noProxyConfig = ProxyConfig(ProxyType.NO_PROXY, "", 0) + assertEquals( + "default", MavenRepositoryConfig(url = URL("https://repo.example.org")) + .toRemoteRepository(noProxyConfig).contentType + ) + assertEquals( + "legacy", MavenRepositoryConfig(url = URL("https://repo.example.org"), layout = "LEgaCY") + .toRemoteRepository(noProxyConfig).contentType + ) + assertThrows { + MavenRepositoryConfig( + url = URL("https://repo.example.org"), + layout = "NOT_EXISTS_LAYOUT" + ).toRemoteRepository(noProxyConfig) + } + } + + @Test + fun `initialFiles test`(@TempDir testDir: Path) { + // 这么做是为了让日志文件创建在其他地方, 由于日志文件在运行时会持续占用, 在 windows 中文件会被锁定, + // 导致测试框架无法正常清除测试所使用的临时文件夹. + val logsDir = Files.createTempDirectory("ammmmmm-logs-") + System.setProperty(AppPaths.PathConst.PROP_DATA_PATH, logsDir.toString()) + assertEquals(logsDir.toString(), AppPaths.DATA_ROOT.path, "日志目录设定失败.") + KotlinLogging.logger("TEST").error { "日志占用.(无需理会), 日志目录: $logsDir" } + AppPaths.DATA_LOGS.file.listFiles { _, name -> name.endsWith(".log") }?.forEach { + it.deleteOnExit() + } + + val fullInitializeDir = Files.createTempDirectory(testDir, "fullInitialize") + System.setProperty(AppPaths.PathConst.PROP_DATA_PATH, fullInitializeDir.toString()) + assertEquals(fullInitializeDir.toString(), AppPaths.DATA_ROOT.path, "测试路径设定失败.") + + assertTrue(initialFiles(), "方法未能提醒用户编辑初始配置文件.") + + for (path in AppPaths.values()) { + assertTrue(path.file.exists(), "文件未初始化成功: ${path.path}") + if (path.file.isFile) { + assertNotEquals(0, path.file.length(), "文件未初始化成功(大小为 0): ${path.path}") + } + path.reset() + } + + assertFalse(initialFiles(), "方法试图在配置已初始化的情况下提醒用户编辑初始配置文件.") + + for (path in AppPaths.values()) { + assertTrue(path.file.exists(), "文件未初始化成功: ${path.path}") + if (path.file.isFile) { + assertNotEquals(0, path.file.length(), "文件未初始化成功(大小为 0): ${path.path}") + } + path.reset() + } + + assertTrue(AppPaths.CONFIG_APPLICATION.file.delete(), "config.json 删除失败.") + assertFalse(initialFiles(), "方法试图在部分配置已初始化的情况下提醒用户编辑初始配置文件.") + + for (path in AppPaths.values()) { + assertTrue(path.file.exists(), "文件未初始化成功: ${path.path}") + if (path.file.isFile) { + assertNotEquals(0, path.file.length(), "文件未初始化成功(大小为 0): ${path.path}") + } + path.reset() + } + + assertTrue(AppPaths.CONFIG_BOT.file.delete(), "bot.json 删除失败.") + assertFalse(initialFiles(), "方法试图在部分配置已初始化的情况下提醒用户编辑初始配置文件.") + + for (path in AppPaths.values()) { + assertTrue(path.file.exists(), "文件未初始化成功: ${path.path}") + if (path.file.isFile) { + assertNotEquals(0, path.file.length(), "文件未初始化成功(大小为 0): ${path.path}") + } + path.reset() + } + + assertTrue(AppPaths.CONFIG_APPLICATION.file.delete(), "config.json 删除失败.") + assertTrue(AppPaths.CONFIG_BOT.file.delete(), "bot.json 删除失败.") + assertTrue( + initialFiles(), + "在主要配置文件(config.json 和 bot.json)不存在的情况下初始化文件后, 方法未能提醒用户编辑初始配置文件." + ) + + for (path in AppPaths.values()) { + assertTrue(path.file.exists(), "文件未初始化成功: ${path.path}") + if (path.file.isFile) { + assertNotEquals(0, path.file.length(), "文件未初始化成功(大小为 0): ${path.path}") + } + path.reset() + } + + System.getProperties().remove(AppPaths.PathConst.PROP_DATA_PATH) + } + + private fun AppPaths.reset() { + val method = AppPaths::class.java.getDeclaredMethod("reset") + method.isAccessible = true + method.invoke(this) + method.isAccessible = false + } + +}