mirror of
https://github.com/Suwayomi/Tachidesk.git
synced 2025-12-10 06:42:07 +01:00
improve tests
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -3,6 +3,9 @@
|
|||||||
.idea
|
.idea
|
||||||
gradle.properties
|
gradle.properties
|
||||||
|
|
||||||
|
# But we need these
|
||||||
|
!.idea/runConfigurations
|
||||||
|
|
||||||
# Ignore Gradle build output directory
|
# Ignore Gradle build output directory
|
||||||
build
|
build
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ package suwayomi.tachidesk.manga.impl
|
|||||||
import org.jetbrains.exposed.sql.SortOrder
|
import org.jetbrains.exposed.sql.SortOrder
|
||||||
import org.jetbrains.exposed.sql.and
|
import org.jetbrains.exposed.sql.and
|
||||||
import org.jetbrains.exposed.sql.deleteWhere
|
import org.jetbrains.exposed.sql.deleteWhere
|
||||||
import org.jetbrains.exposed.sql.insert
|
import org.jetbrains.exposed.sql.insertAndGetId
|
||||||
import org.jetbrains.exposed.sql.select
|
import org.jetbrains.exposed.sql.select
|
||||||
import org.jetbrains.exposed.sql.selectAll
|
import org.jetbrains.exposed.sql.selectAll
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
@@ -27,18 +27,21 @@ object Category {
|
|||||||
/**
|
/**
|
||||||
* The new category will be placed at the end of the list
|
* The new category will be placed at the end of the list
|
||||||
*/
|
*/
|
||||||
fun createCategory(name: String) {
|
fun createCategory(name: String): Int {
|
||||||
// creating a category named Default is illegal
|
// creating a category named Default is illegal
|
||||||
if (name.equals(DEFAULT_CATEGORY_NAME, ignoreCase = true)) return
|
if (name.equals(DEFAULT_CATEGORY_NAME, ignoreCase = true)) return -1
|
||||||
|
|
||||||
transaction {
|
return transaction {
|
||||||
if (CategoryTable.select { CategoryTable.name eq name }.firstOrNull() == null) {
|
if (CategoryTable.select { CategoryTable.name eq name }.firstOrNull() == null) {
|
||||||
CategoryTable.insert {
|
val newCategoryId = CategoryTable.insertAndGetId {
|
||||||
it[CategoryTable.name] = name
|
it[CategoryTable.name] = name
|
||||||
it[CategoryTable.order] = Int.MAX_VALUE
|
it[CategoryTable.order] = Int.MAX_VALUE
|
||||||
}
|
}.value
|
||||||
|
|
||||||
normalizeCategories()
|
normalizeCategories()
|
||||||
}
|
|
||||||
|
newCategoryId
|
||||||
|
} else -1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,9 +23,7 @@ object DBManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun databaseUp() {
|
fun databaseUp(db: Database = DBManager.db) {
|
||||||
// must mention db object so the lazy block executes
|
|
||||||
val db = DBManager.db
|
|
||||||
db.useNestedTransactions = true
|
db.useNestedTransactions = true
|
||||||
|
|
||||||
val migrations = loadMigrationsFrom("suwayomi.tachidesk.server.database.migration", ServerConfig::class.java)
|
val migrations = loadMigrationsFrom("suwayomi.tachidesk.server.database.migration", ServerConfig::class.java)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package suwayomi
|
package masstest
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) Contributors to the Suwayomi project
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package suwayomi
|
package masstest
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) Contributors to the Suwayomi project
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
157
server/src/test/kotlin/suwayomi/tachidesk/ApplicationTest.kt
Normal file
157
server/src/test/kotlin/suwayomi/tachidesk/ApplicationTest.kt
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
package suwayomi.tachidesk
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import eu.kanade.tachiyomi.source.local.LocalSource
|
||||||
|
import mu.KotlinLogging
|
||||||
|
import org.jetbrains.exposed.sql.Database
|
||||||
|
import org.junit.jupiter.api.BeforeAll
|
||||||
|
import org.kodein.di.DI
|
||||||
|
import org.kodein.di.bind
|
||||||
|
import org.kodein.di.conf.global
|
||||||
|
import org.kodein.di.singleton
|
||||||
|
import masstest.BASE_PATH
|
||||||
|
import suwayomi.tachidesk.server.ApplicationDirs
|
||||||
|
import suwayomi.tachidesk.server.JavalinSetup
|
||||||
|
import suwayomi.tachidesk.server.ServerConfig
|
||||||
|
import suwayomi.tachidesk.server.androidCompat
|
||||||
|
import suwayomi.tachidesk.server.database.databaseUp
|
||||||
|
import suwayomi.tachidesk.server.serverConfig
|
||||||
|
import suwayomi.tachidesk.server.systemTrayInstance
|
||||||
|
import suwayomi.tachidesk.server.util.AppMutex
|
||||||
|
import xyz.nulldev.androidcompat.AndroidCompatInitializer
|
||||||
|
import xyz.nulldev.ts.config.CONFIG_PREFIX
|
||||||
|
import xyz.nulldev.ts.config.ConfigKodeinModule
|
||||||
|
import xyz.nulldev.ts.config.GlobalConfigManager
|
||||||
|
import java.io.File
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
open class ApplicationTest {
|
||||||
|
companion object {
|
||||||
|
@BeforeAll
|
||||||
|
@JvmStatic
|
||||||
|
fun beforeAll() {
|
||||||
|
if (!initializedTheApp) {
|
||||||
|
val dataRoot = File(BASE_PATH).absolutePath
|
||||||
|
System.setProperty("$CONFIG_PREFIX.server.rootDir", dataRoot)
|
||||||
|
|
||||||
|
testingSetup()
|
||||||
|
|
||||||
|
databaseSetup()
|
||||||
|
|
||||||
|
initializedTheApp = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val logger = KotlinLogging.logger {}
|
||||||
|
private var initializedTheApp = false
|
||||||
|
|
||||||
|
fun testingSetup() {
|
||||||
|
// Application dirs
|
||||||
|
val applicationDirs = ApplicationDirs()
|
||||||
|
|
||||||
|
DI.global.addImport(
|
||||||
|
DI.Module("Server") {
|
||||||
|
bind<ApplicationDirs>() with singleton { applicationDirs }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug("Data Root directory is set to: ${applicationDirs.dataRoot}")
|
||||||
|
|
||||||
|
// make dirs we need
|
||||||
|
listOf(
|
||||||
|
applicationDirs.dataRoot,
|
||||||
|
applicationDirs.extensionsRoot,
|
||||||
|
applicationDirs.extensionsRoot + "/icon",
|
||||||
|
applicationDirs.mangaThumbnailsRoot,
|
||||||
|
applicationDirs.animeThumbnailsRoot,
|
||||||
|
applicationDirs.mangaRoot,
|
||||||
|
applicationDirs.localMangaRoot,
|
||||||
|
).forEach {
|
||||||
|
File(it).mkdirs()
|
||||||
|
}
|
||||||
|
|
||||||
|
// register Tachidesk's config which is dubbed "ServerConfig"
|
||||||
|
GlobalConfigManager.registerModule(
|
||||||
|
ServerConfig.register(GlobalConfigManager.config)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Make sure only one instance of the app is running
|
||||||
|
AppMutex.handleAppMutex()
|
||||||
|
|
||||||
|
// Load config API
|
||||||
|
DI.global.addImport(ConfigKodeinModule().create())
|
||||||
|
// Load Android compatibility dependencies
|
||||||
|
AndroidCompatInitializer().init()
|
||||||
|
// start app
|
||||||
|
androidCompat.startApp(App())
|
||||||
|
|
||||||
|
// create conf file if doesn't exist
|
||||||
|
try {
|
||||||
|
val dataConfFile = File("${applicationDirs.dataRoot}/server.conf")
|
||||||
|
if (!dataConfFile.exists()) {
|
||||||
|
JavalinSetup::class.java.getResourceAsStream("/server-reference.conf").use { input ->
|
||||||
|
dataConfFile.outputStream().use { output ->
|
||||||
|
input.copyTo(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logger.error("Exception while creating initial server.conf", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy local source icon
|
||||||
|
try {
|
||||||
|
val localSourceIconFile = File("${applicationDirs.extensionsRoot}/icon/localSource.png")
|
||||||
|
if (!localSourceIconFile.exists()) {
|
||||||
|
JavalinSetup::class.java.getResourceAsStream("/icon/localSource.png").use { input ->
|
||||||
|
localSourceIconFile.outputStream().use { output ->
|
||||||
|
input.copyTo(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logger.error("Exception while copying Local source's icon", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create system tray
|
||||||
|
if (serverConfig.systemTrayEnabled) {
|
||||||
|
try {
|
||||||
|
systemTrayInstance
|
||||||
|
} catch (e: Throwable) { // cover both java.lang.Exception and java.lang.Error
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable jetty's logging
|
||||||
|
System.setProperty("org.eclipse.jetty.util.log.announce", "false")
|
||||||
|
System.setProperty("org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.StdErrLog")
|
||||||
|
System.setProperty("org.eclipse.jetty.LEVEL", "OFF")
|
||||||
|
|
||||||
|
// socks proxy settings
|
||||||
|
if (serverConfig.socksProxyEnabled) {
|
||||||
|
System.getProperties()["socksProxyHost"] = serverConfig.socksProxyHost
|
||||||
|
System.getProperties()["socksProxyPort"] = serverConfig.socksProxyPort
|
||||||
|
logger.info("Socks Proxy is enabled to ${serverConfig.socksProxyHost}:${serverConfig.socksProxyPort}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun databaseSetup() {
|
||||||
|
// fixes #119 , ref: https://github.com/Suwayomi/Tachidesk-Server/issues/119#issuecomment-894681292 , source Id calculation depends on String.lowercase()
|
||||||
|
Locale.setDefault(Locale.ENGLISH)
|
||||||
|
|
||||||
|
// in-memory database, don't discard database between connections/transactions
|
||||||
|
val db = Database.connect("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;", "org.h2.Driver")
|
||||||
|
|
||||||
|
databaseUp(db)
|
||||||
|
|
||||||
|
LocalSource.addDbRecords()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,27 +1,22 @@
|
|||||||
package suwayomi.tachidesk.manga.controller
|
package suwayomi.tachidesk.manga.controller
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import org.jetbrains.exposed.sql.deleteAll
|
import org.jetbrains.exposed.sql.deleteAll
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
import org.junit.jupiter.api.AfterEach
|
import org.junit.jupiter.api.AfterEach
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
import org.junit.jupiter.api.BeforeEach
|
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import suwayomi.BASE_PATH
|
|
||||||
import suwayomi.tachidesk.manga.impl.Category
|
import suwayomi.tachidesk.manga.impl.Category
|
||||||
import suwayomi.tachidesk.manga.model.table.CategoryTable
|
import suwayomi.tachidesk.manga.model.table.CategoryTable
|
||||||
import suwayomi.tachidesk.server.applicationSetup
|
import suwayomi.tachidesk.ApplicationTest
|
||||||
import xyz.nulldev.ts.config.CONFIG_PREFIX
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
internal class CategoryControllerTest {
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
internal fun setUp() {
|
|
||||||
val dataRoot = File(BASE_PATH).absolutePath
|
|
||||||
System.setProperty("$CONFIG_PREFIX.server.rootDir", dataRoot)
|
|
||||||
applicationSetup()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
internal class CategoryControllerTest : ApplicationTest() {
|
||||||
@Test
|
@Test
|
||||||
fun categoryReorder() {
|
fun categoryReorder() {
|
||||||
Category.createCategory("foo")
|
Category.createCategory("foo")
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package suwayomi.tachidesk.manga.controller
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
import org.jetbrains.exposed.sql.deleteAll
|
||||||
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
import org.junit.jupiter.api.AfterEach
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.TestInstance
|
||||||
|
import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS
|
||||||
|
import suwayomi.tachidesk.manga.impl.Category
|
||||||
|
import suwayomi.tachidesk.manga.model.table.CategoryTable
|
||||||
|
import suwayomi.tachidesk.ApplicationTest
|
||||||
|
|
||||||
|
@TestInstance(PER_CLASS)
|
||||||
|
internal class SourceControllerTest : ApplicationTest() {
|
||||||
|
@Test
|
||||||
|
fun categoryReorder() {
|
||||||
|
Category.createCategory("foo")
|
||||||
|
Category.createCategory("bar")
|
||||||
|
val cats = Category.getCategoryList()
|
||||||
|
val foo = cats.asSequence().filter { it.name == "foo" }.first()
|
||||||
|
val bar = cats.asSequence().filter { it.name == "bar" }.first()
|
||||||
|
assertEquals(1, foo.order)
|
||||||
|
assertEquals(2, bar.order)
|
||||||
|
Category.reorderCategory(1, 2)
|
||||||
|
val catsReordered = Category.getCategoryList()
|
||||||
|
val fooReordered = catsReordered.asSequence().filter { it.name == "foo" }.first()
|
||||||
|
val barReordered = catsReordered.asSequence().filter { it.name == "bar" }.first()
|
||||||
|
assertEquals(2, fooReordered.order)
|
||||||
|
assertEquals(1, barReordered.order)
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
internal fun tearDown() {
|
||||||
|
transaction {
|
||||||
|
CategoryTable.deleteAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,10 +13,9 @@ import org.jetbrains.exposed.sql.insertAndGetId
|
|||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
import org.junit.jupiter.api.AfterEach
|
import org.junit.jupiter.api.AfterEach
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
import org.junit.jupiter.api.BeforeEach
|
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
import suwayomi.BASE_PATH
|
import suwayomi.tachidesk.manga.impl.Category.DEFAULT_CATEGORY_ID
|
||||||
import suwayomi.tachidesk.manga.model.table.CategoryMangaTable
|
import suwayomi.tachidesk.manga.model.table.CategoryMangaTable
|
||||||
import suwayomi.tachidesk.manga.model.table.CategoryTable
|
import suwayomi.tachidesk.manga.model.table.CategoryTable
|
||||||
import suwayomi.tachidesk.manga.model.table.ChapterTable
|
import suwayomi.tachidesk.manga.model.table.ChapterTable
|
||||||
@@ -26,59 +25,49 @@ import suwayomi.tachidesk.manga.model.table.ChapterTable.name
|
|||||||
import suwayomi.tachidesk.manga.model.table.ChapterTable.sourceOrder
|
import suwayomi.tachidesk.manga.model.table.ChapterTable.sourceOrder
|
||||||
import suwayomi.tachidesk.manga.model.table.ChapterTable.url
|
import suwayomi.tachidesk.manga.model.table.ChapterTable.url
|
||||||
import suwayomi.tachidesk.manga.model.table.MangaTable
|
import suwayomi.tachidesk.manga.model.table.MangaTable
|
||||||
import suwayomi.tachidesk.server.applicationSetup
|
import suwayomi.tachidesk.ApplicationTest
|
||||||
import xyz.nulldev.ts.config.CONFIG_PREFIX
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
class CategoryMangaTest {
|
class CategoryMangaTest : ApplicationTest() {
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
fun setUp() {
|
|
||||||
val dataRoot = File(BASE_PATH).absolutePath
|
|
||||||
System.setProperty("$CONFIG_PREFIX.server.rootDir", dataRoot)
|
|
||||||
applicationSetup()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun getCategoryMangaList() {
|
fun getCategoryMangaList() {
|
||||||
val emptyCats = CategoryManga.getCategoryMangaList(0).size
|
val emptyCats = CategoryManga.getCategoryMangaList(DEFAULT_CATEGORY_ID).size
|
||||||
assertEquals(0, emptyCats, "Default category should be empty at start")
|
assertEquals(0, emptyCats, "Default category should be empty at start")
|
||||||
val mangaId = createManga("Psyren")
|
val mangaId = createLibraryManga("Psyren")
|
||||||
createChapters(mangaId, 10, true)
|
createChapters(mangaId, 10, true)
|
||||||
assertEquals(1, CategoryManga.getCategoryMangaList(0).size, "Default category should have one member")
|
assertEquals(1, CategoryManga.getCategoryMangaList(DEFAULT_CATEGORY_ID).size, "Default category should have one member")
|
||||||
assertEquals(
|
assertEquals(
|
||||||
0, CategoryManga.getCategoryMangaList(0)[0].unreadCount,
|
0, CategoryManga.getCategoryMangaList(DEFAULT_CATEGORY_ID)[0].unreadCount,
|
||||||
"Manga should not have any unread chapters"
|
"Manga should not have any unread chapters"
|
||||||
)
|
)
|
||||||
createChapters(mangaId, 10, false)
|
createChapters(mangaId, 10, false)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
10, CategoryManga.getCategoryMangaList(0)[0].unreadCount,
|
10, CategoryManga.getCategoryMangaList(DEFAULT_CATEGORY_ID)[0].unreadCount,
|
||||||
"Manga should have unread chapters"
|
"Manga should have unread chapters"
|
||||||
)
|
)
|
||||||
|
|
||||||
Category.createCategory("Old") // category id 1
|
val categoryId = Category.createCategory("Old")
|
||||||
assertEquals(
|
assertEquals(
|
||||||
0,
|
0,
|
||||||
CategoryManga.getCategoryMangaList(1).size,
|
CategoryManga.getCategoryMangaList(categoryId).size,
|
||||||
"Newly created category shouldn't have any Mangas"
|
"Newly created category shouldn't have any Mangas"
|
||||||
)
|
)
|
||||||
CategoryManga.addMangaToCategory(mangaId, 1)
|
CategoryManga.addMangaToCategory(mangaId, categoryId)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
1, CategoryManga.getCategoryMangaList(1).size,
|
1, CategoryManga.getCategoryMangaList(categoryId).size,
|
||||||
"Manga should been moved"
|
"Manga should been moved"
|
||||||
)
|
)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
10, CategoryManga.getCategoryMangaList(1)[0].unreadCount,
|
10, CategoryManga.getCategoryMangaList(categoryId)[0].unreadCount,
|
||||||
"Manga should keep it's unread count in moved category"
|
"Manga should keep it's unread count in moved category"
|
||||||
)
|
)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
0, CategoryManga.getCategoryMangaList(0).size,
|
0, CategoryManga.getCategoryMangaList(DEFAULT_CATEGORY_ID).size,
|
||||||
"Manga shouldn't be member of default category after moving"
|
"Manga shouldn't be member of default category after moving"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createManga(
|
private fun createLibraryManga(
|
||||||
_title: String
|
_title: String
|
||||||
): Int {
|
): Int {
|
||||||
return transaction {
|
return transaction {
|
||||||
|
|||||||
Reference in New Issue
Block a user