mirror of
https://github.com/TermoraDev/termora.git
synced 2026-01-16 10:22:58 +08:00
chore!: migrate to version 2.x
This commit is contained in:
16
src/test/kotlin/app/termora/AntPathMatcherTest.kt
Normal file
16
src/test/kotlin/app/termora/AntPathMatcherTest.kt
Normal 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"))
|
||||
}
|
||||
}
|
||||
39
src/test/kotlin/app/termora/ExposedTest.kt
Normal file
39
src/test/kotlin/app/termora/ExposedTest.kt
Normal 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)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
197
src/test/kotlin/app/termora/OptionSorter.kt
Normal file
197
src/test/kotlin/app/termora/OptionSorter.kt
Normal 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")
|
||||
}
|
||||
@@ -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)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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)}")
|
||||
|
||||
@@ -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",
|
||||
|
||||
33
src/test/kotlin/app/termora/SemverTest.kt
Normal file
33
src/test/kotlin/app/termora/SemverTest.kt
Normal 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)
|
||||
}
|
||||
|
||||
}
|
||||
15
src/test/kotlin/app/termora/account/ServerManagerTest.kt
Normal file
15
src/test/kotlin/app/termora/account/ServerManagerTest.kt
Normal 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"
|
||||
)
|
||||
}
|
||||
}
|
||||
21
src/test/kotlin/app/termora/account/TeamTest.kt
Normal file
21
src/test/kotlin/app/termora/account/TeamTest.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
10
src/test/kotlin/app/termora/plugin/PluginManagerTest.kt
Normal file
10
src/test/kotlin/app/termora/plugin/PluginManagerTest.kt
Normal file
@@ -0,0 +1,10 @@
|
||||
package app.termora.plugin
|
||||
|
||||
import kotlin.test.Test
|
||||
|
||||
class PluginManagerTest {
|
||||
@Test
|
||||
fun test() {
|
||||
println(PluginManager.getInstance())
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user