diff --git a/build.gradle.kts b/build.gradle.kts index 9ce3d74e..93fb8182 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -99,6 +99,7 @@ kotlin { val commonTest by getting { dependencies { implementation(kotlin("test")) + implementation("io.mockk:mockk:1.13.12") } } diff --git a/src/commonMain/kotlin/teksturepako/pakku/api/platforms/CurseForge.kt b/src/commonMain/kotlin/teksturepako/pakku/api/platforms/CurseForge.kt index 6cd3e8a2..0437e9f2 100644 --- a/src/commonMain/kotlin/teksturepako/pakku/api/platforms/CurseForge.kt +++ b/src/commonMain/kotlin/teksturepako/pakku/api/platforms/CurseForge.kt @@ -114,7 +114,7 @@ object CurseForge : Platform( // -- FILES -- - private const val LOADER_VERSION_TYPE_ID = 68441 + internal const val LOADER_VERSION_TYPE_ID = 68441 private fun List.filterFileModels( mcVersions: List, loaders: List @@ -124,10 +124,19 @@ object CurseForge : Platform( .filter { it.gameVersionTypeId == LOADER_VERSION_TYPE_ID } // Filter to loader only .takeIf { it.isNotEmpty() } ?.map { it.gameVersionName.lowercase() }?.any { - loaders.any { loader -> loader == it } || it in validLoaders // Check default valid loaders + it in loaders || it in validLoaders // Check default valid loaders } ?: true // If no loaders found, accept model } + internal fun List.sortByLoaders(loaders: List) = this.sortedWith { fileA, fileB -> + val aLoaders = fileA.sortableGameVersions.filter { it.gameVersionTypeId == LOADER_VERSION_TYPE_ID } + .map { it.gameVersionName.lowercase() } + val bLoaders = fileB.sortableGameVersions.filter { it.gameVersionTypeId == LOADER_VERSION_TYPE_ID } + .map { it.gameVersionName.lowercase() } + loaders.indexOfFirst { it in aLoaders }.let { if (it == -1) loaders.size else it } + .minus(loaders.indexOfFirst { it in bLoaders }.let { if (it == -1) loaders.size else it }) + } + private fun CfModModel.File.toProjectFile(gameVersionTypeIds: List): ProjectFile { return ProjectFile( @@ -192,6 +201,7 @@ object CurseForge : Platform( this.requestProjectBody(requestUrl) ?: return mutableSetOf() ).data .filterFileModels(mcVersions, loaders) + .sortByLoaders(loaders) .map { it.toProjectFile(gameVersionTypeIds) } .debugIfEmpty { println("${this::class.simpleName}#requestProjectFiles: file is null") @@ -223,6 +233,7 @@ object CurseForge : Platform( ).data .filterFileModels(mcVersions, loaders) .sortedByDescending { it.fileDate } + .sortByLoaders(loaders) .map { it.toProjectFile(gameVersionTypeIds) } .debugIfEmpty { println("${this::class.simpleName}#requestMultipleProjectFiles: file is null") diff --git a/src/commonMain/kotlin/teksturepako/pakku/api/platforms/Modrinth.kt b/src/commonMain/kotlin/teksturepako/pakku/api/platforms/Modrinth.kt index 8ea98583..4f659300 100644 --- a/src/commonMain/kotlin/teksturepako/pakku/api/platforms/Modrinth.kt +++ b/src/commonMain/kotlin/teksturepako/pakku/api/platforms/Modrinth.kt @@ -14,6 +14,7 @@ import teksturepako.pakku.api.models.mr.GetVersionsFromHashesRequest import teksturepako.pakku.api.models.mr.MrProjectModel import teksturepako.pakku.api.models.mr.MrVersionModel import teksturepako.pakku.api.projects.* +import teksturepako.pakku.debug import teksturepako.pakku.debugIfEmpty import kotlin.system.exitProcess import kotlin.time.Duration.Companion.seconds @@ -139,10 +140,15 @@ object Modrinth : Platform( version.gameVersions.any { it in mcVersions } && version.loaders .takeIf { it.isNotEmpty() } ?.map { it.lowercase() }?.any { - loaders.any { loader -> loader == it } || it in validLoaders // Check default valid loaders + it in loaders || it in validLoaders // Check default valid loaders } ?: true // If no loaders found, accept model } + internal fun List.sortByLoaders(loaders: List) = this.sortedWith { aVersion, bVersion -> + loaders.indexOfFirst { it in aVersion.loaders }.let { if (it == -1) loaders.size else it } + .minus(loaders.indexOfFirst { it in bVersion.loaders }.let { if (it == -1) loaders.size else it }) + } + private fun MrVersionModel.toProjectFiles(): List { return this.files.sortedBy { it.primary }.map { versionFile -> @@ -182,6 +188,7 @@ object Modrinth : Platform( this.requestProjectBody("project/$projectId/version") ?: return mutableSetOf() ) .filterFileModels(mcVersions, loaders) + .sortByLoaders(loaders) .flatMap { version -> version.toProjectFiles() } .debugIfEmpty { println("${this::class.simpleName}#requestProjectFiles: file is null") @@ -213,8 +220,9 @@ object Modrinth : Platform( } .awaitAll() .flatten() - .sortedByDescending { it.datePublished } .filterFileModels(mcVersions, loaders) + .sortedByDescending { it.datePublished } + .sortByLoaders(loaders) .flatMap { version -> version.toProjectFiles() } .toMutableSet() } diff --git a/src/commonTest/kotlin/teksturepako/pakku/api/platforms/CurseForgeTest.kt b/src/commonTest/kotlin/teksturepako/pakku/api/platforms/CurseForgeTest.kt index 58db8d7e..0f94ae94 100644 --- a/src/commonTest/kotlin/teksturepako/pakku/api/platforms/CurseForgeTest.kt +++ b/src/commonTest/kotlin/teksturepako/pakku/api/platforms/CurseForgeTest.kt @@ -1,6 +1,13 @@ package teksturepako.pakku.api.platforms +import io.mockk.every +import io.mockk.mockk +import teksturepako.pakku.api.models.cf.CfModModel +import teksturepako.pakku.api.platforms.CurseForge.LOADER_VERSION_TYPE_ID +import teksturepako.pakku.api.platforms.CurseForge.sortByLoaders import kotlin.test.Test +import kotlin.test.assertContentEquals +import kotlin.test.assertEquals class CurseForgeTest { @@ -8,4 +15,115 @@ class CurseForgeTest fun requestProject() { } + + @Test + fun sortByLoaders_WithValidLoaders_ShouldSortCorrectly() { + val files = listOf( + mockk { + every { sortableGameVersions } returns listOf( + mockk { + every { gameVersionTypeId } returns LOADER_VERSION_TYPE_ID + every { gameVersionName } returns "loaderB" + }, + mockk { + every { gameVersionTypeId } returns LOADER_VERSION_TYPE_ID + every { gameVersionName } returns "loaderC" + } + ) + }, + mockk { + every { sortableGameVersions } returns listOf( + mockk { + every { gameVersionTypeId } returns LOADER_VERSION_TYPE_ID + every { gameVersionName } returns "loaderA" + }, + mockk { + every { gameVersionTypeId } returns LOADER_VERSION_TYPE_ID + every { gameVersionName } returns "loaderB" + } + ) + }, + mockk { + every { sortableGameVersions } returns listOf( + mockk { + every { gameVersionTypeId } returns LOADER_VERSION_TYPE_ID + every { gameVersionName } returns "loaderB" + }, + mockk { + every { gameVersionTypeId } returns LOADER_VERSION_TYPE_ID + every { gameVersionName } returns "loaderB" + } + ) + }, + mockk { + every { sortableGameVersions } returns listOf( + mockk { + every { gameVersionTypeId } returns LOADER_VERSION_TYPE_ID + every { gameVersionName } returns "loaderA" + }, + mockk { + every { gameVersionTypeId } returns LOADER_VERSION_TYPE_ID + every { gameVersionName } returns "loaderC" + } + ) + } + ) + + val loaders = listOf("loadera", "loaderb", "loaderc") + val sortedFiles = files.toList().sortByLoaders(loaders) + + assertContentEquals(listOf(files[1], files[3], files[0], files[2]), sortedFiles) + } + + @Test + fun sortByLoaders_WithNoMatchingLoaders_ShouldNotChangeOrder() { + val files = listOf( + mockk { + every { sortableGameVersions } returns listOf( + mockk { + every { gameVersionTypeId } returns LOADER_VERSION_TYPE_ID + every { gameVersionName } returns "loaderB" + } + ) + }, + mockk { + every { sortableGameVersions } returns listOf( + mockk { + every { gameVersionTypeId } returns LOADER_VERSION_TYPE_ID + every { gameVersionName } returns "loaderA" + } + ) + } + ) + + val loaders = listOf("loader1", "loader2") + val sortedFiles = files.toList().sortByLoaders(loaders) + assertContentEquals(files, sortedFiles) + } + + @Test + fun sortByLoaders_WithSomeMatchingLoaders_ShouldSortCorrectly() { + val files = listOf( + mockk { + every { sortableGameVersions } returns listOf( + mockk { + every { gameVersionTypeId } returns LOADER_VERSION_TYPE_ID + every { gameVersionName } returns "loaderA" + } + ) + }, + mockk { + every { sortableGameVersions } returns listOf( + mockk { + every { gameVersionTypeId } returns LOADER_VERSION_TYPE_ID + every { gameVersionName } returns "loader1" + } + ) + } + ) + + val loaders = listOf("loader1", "loader2") + val sortedFiles = files.toList().sortByLoaders(loaders) + assertEquals(files[0], sortedFiles[1]) + } } \ No newline at end of file diff --git a/src/commonTest/kotlin/teksturepako/pakku/api/platforms/ModrinthTest.kt b/src/commonTest/kotlin/teksturepako/pakku/api/platforms/ModrinthTest.kt new file mode 100644 index 00000000..d841460c --- /dev/null +++ b/src/commonTest/kotlin/teksturepako/pakku/api/platforms/ModrinthTest.kt @@ -0,0 +1,69 @@ +package teksturepako.pakku.api.platforms + +import io.mockk.every +import io.mockk.mockk +import teksturepako.pakku.api.models.mr.MrVersionModel +import teksturepako.pakku.api.platforms.Modrinth.sortByLoaders +import kotlin.test.Test +import kotlin.test.assertContentEquals +import kotlin.test.assertEquals + +class ModrinthTest +{ + @Test + fun sortByLoaders_WithValidLoaders_ShouldSortCorrectly() { + val versions = listOf( + mockk { + every { loaders } returns listOf("loaderb", "loaderc") + }, + mockk { + every { loaders } returns listOf("loadera", "loaderb") + }, + mockk { + every { loaders } returns listOf("loaderb", "loaderb") + }, + mockk { + every { loaders } returns listOf("loadera", "loaderc") + } + ) + + val loaders = listOf("loadera", "loaderb", "loaderc") + val sortedVersions = versions.toList().sortByLoaders(loaders) + + assertContentEquals(listOf(versions[1], versions[3], versions[0], versions[2]), sortedVersions) + } + + @Test + fun sortByLoaders_WithNoMatchingLoaders_ShouldNotChangeOrder() { + val versions = listOf( + mockk { + every { loaders } returns listOf("loaderB") + }, + mockk { + every { loaders } returns listOf("loaderA") + } + ) + + val loaders = listOf("loader1", "loader2") + val sortedVersions = versions.toList().sortByLoaders(loaders) + + assertContentEquals(versions, sortedVersions) + } + + @Test + fun sortByLoaders_WithSomeMatchingLoaders_ShouldSortCorrectly() { + val versions = listOf( + mockk { + every { loaders } returns listOf("loadera") + }, + mockk { + every { loaders } returns listOf("loader1") + } + ) + + val loaders = listOf("loader1", "loader2") + val sortedVersions = versions.toList().sortByLoaders(loaders) + + assertEquals(versions[0], sortedVersions[1]) + } +} \ No newline at end of file