chore!: migrate to version 2.x

This commit is contained in:
hstyi
2025-06-13 15:16:56 +08:00
committed by GitHub
parent ca484618c7
commit 6177bbdc68
444 changed files with 18594 additions and 3832 deletions

View File

@@ -0,0 +1,16 @@
package app.termora
import kotlin.test.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class AntPathMatcherTest {
@Test
fun test() {
val matcher = AntPathMatcher(".")
assertTrue(matcher.match("*.baidu.com", "www.baidu.com"))
assertTrue(matcher.match("*.baidu.com", "wwwwwwwwwwww123123aaa.baidu.com"))
assertFalse(matcher.match("*.baidu.com", "sub.sub.baidu.com"))
assertTrue(matcher.match("**.baidu.com", "sub.sub.baidu.com"))
}
}

View File

@@ -0,0 +1,39 @@
package app.termora
import app.termora.database.DataEntity
import app.termora.database.DataType
import app.termora.database.OwnerType
import app.termora.database.SettingEntity
import org.jetbrains.exposed.v1.jdbc.Database
import org.jetbrains.exposed.v1.jdbc.SchemaUtils
import org.jetbrains.exposed.v1.jdbc.insert
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
import kotlin.test.Test
class ExposedTest {
@Test
fun test() {
val database = Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver", user = "sa")
transaction(database) {
SchemaUtils.create(DataEntity, SettingEntity)
println(DataEntity.insert {
it[ownerId] = "Test"
it[ownerType] = OwnerType.User.name
it[type] = DataType.KeywordHighlight.name
it[data] = "hello 中文".repeat(10000)
} get DataEntity.id)
println(SettingEntity.insert {
it[name] = "Test"
it[value] = "hello 中文".repeat(10000)
} get SettingEntity.id)
}
}
}

View File

@@ -1,7 +1,6 @@
package app.termora
import app.termora.Application.ohMyJson
import kotlinx.serialization.encodeToString
import kotlin.test.Test
class HostTest {
@@ -11,7 +10,7 @@ class HostTest {
"""
{
"name": "test",
"protocol": "SSH",
"protocol": SSHProtocolProvider.PROTOCOL,
"test": ""
}
""".trimIndent()

View File

@@ -0,0 +1,197 @@
package app.termora
import app.termora.OptionsPane.Anchor
import app.termora.OptionsPane.Option
class OptionSorter {
fun sortOptions(options: List<Option>): List<Option> {
if (options.isEmpty()) return emptyList()
// 如果所有选项的 Anchor 都是 Null则不排序保持原始顺序
if (options.all { it.getAnchor() is Anchor.Null }) {
return options
}
val optionMap = options.associateBy { it.getIdentifier() }
val result = mutableListOf<Option>()
// 分组处理不同类型的锚点
val nullOptions = options.filter { it.getAnchor() is Anchor.Null }
val firstOptions = options.filter { it.getAnchor() is Anchor.First }
val lastOptions = options.filter { it.getAnchor() is Anchor.Last }
// 分离有效和无效的相对位置选项
val (validRelativeOptions, invalidRelativeOptions) = options.filter {
it.getAnchor() is Anchor.Before || it.getAnchor() is Anchor.After
}.partition { option ->
when (val anchor = option.getAnchor()) {
is Anchor.Before -> optionMap.containsKey(anchor.target)
is Anchor.After -> optionMap.containsKey(anchor.target)
else -> false
}
}
// 收集所有需要参与相对排序的选项,包括它们的目标选项
val targetIds = validRelativeOptions.mapNotNull { option ->
when (val anchor = option.getAnchor()) {
is Anchor.Before -> anchor.target
is Anchor.After -> anchor.target
else -> null
}
}.toSet()
val targetOptions = targetIds.mapNotNull { targetId -> optionMap[targetId] }
val allOptionsToSort = (validRelativeOptions + nullOptions + targetOptions).distinctBy { it.getIdentifier() }
val sortedRelativeAndNull = sortRelativeOptions(allOptionsToSort, optionMap)
// 按优先级组合结果,但要排除已经在相对排序中处理过的选项
val sortedIds = sortedRelativeAndNull.map { it.getIdentifier() }.toSet()
result.addAll(firstOptions.filter { it.getIdentifier() !in sortedIds }.sortedBy { it.getIdentifier() })
result.addAll(sortedRelativeAndNull)
result.addAll(invalidRelativeOptions.sortedBy { it.getIdentifier() })
result.addAll(lastOptions.filter { it.getIdentifier() !in sortedIds }.sortedBy { it.getIdentifier() })
return result
}
private fun sortRelativeOptions(options: List<Option>, optionMap: Map<String, Option>): List<Option> {
val visited = mutableSetOf<String>()
val visiting = mutableSetOf<String>()
val result = mutableListOf<Option>()
val localOptionMap = options.associateBy { it.getIdentifier() }
fun dfs(optionId: String): Boolean {
if (visiting.contains(optionId)) {
return false // 循环依赖
}
if (visited.contains(optionId)) {
return true
}
visiting.add(optionId)
val option = localOptionMap[optionId] ?: return false
// 先处理所有必须在当前选项之前的选项
options.forEach { otherOption ->
val otherAnchor = otherOption.getAnchor()
val otherId = otherOption.getIdentifier()
// 如果其他选项声明要在当前选项之前Before先处理其他选项
if (otherAnchor is Anchor.Before && otherAnchor.target == optionId && !visited.contains(otherId)) {
dfs(otherId)
}
// 如果当前选项声明要在其他选项之后After先处理其他选项
if (option.getAnchor() is Anchor.After &&
(option.getAnchor() as Anchor.After).target == otherId &&
!visited.contains(otherId)) {
dfs(otherId)
}
}
visiting.remove(optionId)
visited.add(optionId)
result.add(option)
return true
}
// 开始排序 - 按照原始顺序处理,确保稳定排序
options.forEach { option ->
if (!visited.contains(option.getIdentifier())) {
dfs(option.getIdentifier())
}
}
return result
}
}
// 使用示例
fun main() {
// 创建测试选项
class TestOption(
private val title: String,
private val identifier: String,
private val anchor: Anchor
) : Option {
override fun getIcon(isSelected: Boolean) = TODO("Not implemented")
override fun getTitle() = title
override fun getJComponent() = TODO("Not implemented")
override fun getIdentifier() = identifier
override fun getAnchor() = anchor
}
val options = listOf(
TestOption("Appearance", "Appearance", Anchor.Null),
TestOption("Before Appearance", "BeforeApp", Anchor.Before("Appearance")),
TestOption("After Appearance", "AfterApp", Anchor.After("Appearance")),
TestOption("First Option", "First", Anchor.First),
TestOption("Last Option", "Last", Anchor.Last),
TestOption("Another Null", "AnotherNull", Anchor.Null),
TestOption("Invalid Target", "Invalid", Anchor.Before("NonExistent"))
)
val sorter = OptionSorter()
val sortedOptions = sorter.sortOptions(options)
println("排序结果:")
sortedOptions.forEachIndexed { index, option ->
println("${index + 1}. ${option.getTitle()} (${option.getIdentifier()}) - ${option.getAnchor()}")
}
println("\n预期结果说明:")
println("- First Option 应该在最前面")
println("- Before Appearance 应该在 Appearance 前面")
println("- After Appearance 应该在 Appearance 后面")
println("- Null 选项(Appearance, Another Null)保持原始相对顺序")
println("- Invalid Target 应该在 Null 选项后面")
println("- Last Option 应该在最后面")
println("\n测试全部为 Null 的情况:")
val nullOnlyOptions = listOf(
TestOption("Option X", "X", Anchor.Null),
TestOption("Option Y", "Y", Anchor.Null),
TestOption("Option Z", "Z", Anchor.Null)
)
val sortedNullOnly = sorter.sortOptions(nullOnlyOptions)
println("原始顺序: ${nullOnlyOptions.map { it.getTitle() }}")
println("排序后: ${sortedNullOnly.map { it.getTitle() }}")
println("是否保持原始顺序: ${nullOnlyOptions == sortedNullOnly}")
// 额外测试复杂的依赖关系
println("\n测试复杂依赖关系:")
val complexOptions = listOf(
TestOption("A", "A", Anchor.Null),
TestOption("B", "B", Anchor.After("A")),
TestOption("C", "C", Anchor.Before("A")),
TestOption("D", "D", Anchor.After("B")),
TestOption("E", "E", Anchor.Before("C"))
)
val sortedComplex = sorter.sortOptions(complexOptions)
println("复杂依赖排序结果:")
sortedComplex.forEachIndexed { index, option ->
println("${index + 1}. ${option.getTitle()} - ${option.getAnchor()}")
}
// 额外测试Before/After 指向不同优先级的选项
println("\n测试 Before/After 指向不同优先级的选项:")
val mixedPriorityOptions = listOf(
TestOption("A", "A", Anchor.Null),
TestOption("B", "B", Anchor.Before("D")), // B Before D (D 是 Last)
TestOption("C", "C", Anchor.After("F")), // C After F (F 是 First)
TestOption("D", "D", Anchor.Last),
TestOption("E", "E", Anchor.Null),
TestOption("F", "F", Anchor.First)
)
val sortedMixed = sorter.sortOptions(mixedPriorityOptions)
println("混合优先级排序结果:")
sortedMixed.forEachIndexed { index, option ->
println("${index + 1}. ${option.getTitle()} - ${option.getAnchor()}")
}
println("预期: F -> C -> A -> E -> B -> D")
}

View File

@@ -1,17 +0,0 @@
package app.termora
import org.apache.commons.codec.binary.Hex
import kotlin.test.Test
import kotlin.test.assertEquals
class PBKDF2Test {
@Test
fun test() {
val password = "password"
assertEquals(
"72629a41b076e588fba8c71ca37fadc9acdc8e7321b9cb4ea55fd0bf9fe8ed72", Hex.encodeHexString(
PBKDF2.generateSecret(password.toCharArray(), "salt".toByteArray(), 10000, 256)
)
)
}
}

View File

@@ -10,7 +10,7 @@ class RSA2048Test {
fun test() {
val data = "hello world. 中国 😄".toByteArray()
val pair = RSA.generateKeyPair()
val pair = RSA.generateKeyPair(2048)
println("publicKey: ${Base64.encodeBase64String(pair.public.encoded)}")
println("privateKey: ${Base64.encodeBase64String(pair.private.encoded)}")

View File

@@ -22,7 +22,7 @@ abstract class SSHDTest {
protected val host
get() = Host(
name = sshd.containerName,
protocol = Protocol.SSH,
protocol = "SSH",
host = "127.0.0.1",
port = sshd.getMappedPort(2222),
username = "foo",

View File

@@ -0,0 +1,33 @@
package app.termora
import org.semver4j.Semver
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class SemverTest {
@Test
fun test() {
val a = Semver.parse("1.0.0") ?: throw IllegalArgumentException("Semver is invalid")
assertTrue(a.satisfies("1.0.0 - 2.0.0"))
assertTrue(a.satisfies(">=1.0.0"))
}
@Test
fun testBeta() {
val a = Semver.parse("2.0.0-beta.1") ?: return
val b = Semver.parse("2.0.0-beta.2") ?: return
val c = Semver.parse("2.0.1-beta.2") ?: return
assertTrue(a.compareTo(b) == -1)
assertTrue(a.compareTo(c) == -1)
val list = listOf(b, c, a)
println(list.sortedBy { it })
println(list.sortedByDescending { it })
assertEquals(list.minBy { it }, a)
assertEquals(list.maxBy { it }, c)
}
}

View File

@@ -0,0 +1,15 @@
package app.termora.account
import kotlin.test.Test
class ServerManagerTest {
@Test
fun test() {
ServerManager.getInstance().login(
Server(
name = "test",
server = "http://127.0.0.1:8080"
), "admin", "admin"
)
}
}

View File

@@ -0,0 +1,21 @@
package app.termora.account
import app.termora.Application.ohMyJson
import kotlin.test.Test
class TeamTest {
@Test
fun test() {
val team = ohMyJson.decodeFromString<Team>(
ohMyJson.encodeToString(
Team(
id = "test",
name = "test",
secretKey = byteArrayOf(1, 123, 123, 123, 123, 123, 123, 123, 123),
role = TeamRole.Member
)
)
)
println(team)
}
}

View File

@@ -0,0 +1,10 @@
package app.termora.plugin
import kotlin.test.Test
class PluginManagerTest {
@Test
fun test() {
println(PluginManager.getInstance())
}
}

View File

@@ -1,13 +1,12 @@
package app.termora.vfs2.sftp
import app.termora.SSHDTest
import app.termora.toSimpleString
import app.termora.randomUUID
import org.apache.commons.vfs2.*
import org.apache.commons.vfs2.impl.DefaultFileSystemManager
import org.apache.commons.vfs2.provider.local.DefaultLocalFileProvider
import org.apache.sshd.sftp.client.SftpClientFactory
import java.io.File
import java.util.*
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
@@ -18,7 +17,7 @@ class MySftpFileProviderTest : SSHDTest() {
companion object {
init {
val fileSystemManager = DefaultFileSystemManager()
fileSystemManager.addProvider("sftp", MySftpFileProvider())
fileSystemManager.addProvider("sftp", MySftpFileProvider.instance)
fileSystemManager.addProvider("file", DefaultLocalFileProvider())
fileSystemManager.init()
VFS.setManager(fileSystemManager)
@@ -87,7 +86,7 @@ class MySftpFileProviderTest : SSHDTest() {
@Test
fun testCopy() {
val file = newFileObject("/config/sshd.pid")
val filepath = File("build", UUID.randomUUID().toSimpleString())
val filepath = File("build", randomUUID())
val localFile = getVFS().resolveFile("file://${filepath.absolutePath}")
localFile.copyFrom(file, Selectors.SELECT_ALL)
@@ -106,7 +105,8 @@ class MySftpFileProviderTest : SSHDTest() {
private fun newFileObject(path: String): FileObject {
val vfs = getVFS()
val fileSystemOptions = FileSystemOptions()
MySftpFileSystemConfigBuilder.getInstance().setClientSession(fileSystemOptions, newClientSession())
MySftpFileSystemConfigBuilder.getInstance()
.setSftpFileSystem(fileSystemOptions, SftpClientFactory.instance().createSftpFileSystem(newClientSession()))
return vfs.resolveFile("sftp://${path}", fileSystemOptions)
}