package com.catlas.escapepenacony import com.catlas.escapepenacony.data.SceneTeleportPayload import com.catlas.escapepenacony.data.TrailblazerConfig import org.json.JSONObject import org.junit.Assert.* import org.junit.Test class UpdateAndSceneTest { // --- 1. Version Parsing Logic Tests --- private fun parseVersions(raw: String): List { val sanitized = if (raw.startsWith("\uFEFF")) raw.substring(1) else raw return sanitized.split(Regex("[\\r\\n,]+")) .map { it.trim() } .filter { it.isNotBlank() } .distinct() } private fun buildDownloadUrl(baseUrl: String, version: String, filename: String = "build.zip"): String { val sanitizedVersion = version.trim() if (sanitizedVersion.contains("/") || sanitizedVersion.contains("\\") || sanitizedVersion.contains("..")) { throw IllegalArgumentException("Invalid version format: traversal characters detected") } return "$baseUrl$sanitizedVersion/$filename" } @Test fun testVersionParsing_CRLF_LF_Comma_Duplicates() { val sample = "\uFEFF4.3.5X\r\n4.4.5X\n4.5.5X, 4.4.5X\r\n 4.6.5X \r\n\r\n" val parsed = parseVersions(sample) assertEquals(4, parsed.size) assertEquals("4.3.5X", parsed[0]) assertEquals("4.4.5X", parsed[1]) assertEquals("4.5.5X", parsed[2]) assertEquals("4.6.5X", parsed[3]) assertEquals("4.6.5X", parsed.last()) } @Test fun testVersionParsing_SuffixesPreserved() { val sample = "4.6.5X\n4.7.0-BETA_1\n4.7.0-RC2" val parsed = parseVersions(sample) assertEquals("4.7.0-RC2", parsed.last()) } @Test fun testVersionParsing_EmptyOrWhitespace() { val sample = " \r\n \n \r\n" val parsed = parseVersions(sample) assertTrue(parsed.isEmpty()) } @Test fun testBuildDownloadUrl_Valid() { val baseUrl = "https://file.garden/aewBviVxx2XDWgM-/" val url = buildDownloadUrl(baseUrl, "4.6.5X") assertEquals("https://file.garden/aewBviVxx2XDWgM-/4.6.5X/build.zip", url) } @Test(expected = IllegalArgumentException::class) fun testBuildDownloadUrl_RejectsTraversal_Slash() { val baseUrl = "https://file.garden/aewBviVxx2XDWgM-/" buildDownloadUrl(baseUrl, "4.6.5X/hack") } @Test(expected = IllegalArgumentException::class) fun testBuildDownloadUrl_RejectsTraversal_DotDot() { val baseUrl = "https://file.garden/aewBviVxx2XDWgM-/" buildDownloadUrl(baseUrl, "../etc") } // --- 2. Persistent.json Transformation Tests --- @Test fun testPersistentTransformation_PreservesOtherKeys() { val originalJson = """ { "send_hotfix": false, "key": "secret123", "avatar": { "tb_gender": "Male", "tb_path": "Harmony", "march_path": "Hunt" }, "scene": { "plane_id": 10000, "x": 10, "y": 20, "z": 30 } } """.trimIndent() val root = JSONObject(originalJson) val payload = SceneTeleportPayload( planeId = 20461, x = 12080, y = 393572, z = 322230, calyxX = 12080, calyxY = 393572, calyxZ = 324030, mapLayer = 2, calyxGroupId = 186, calyxInstId = 300001, calyxEntityId = 1337, calyxPropId = 808 ) // Apply scene val sceneObj = root.optJSONObject("scene") ?: JSONObject().also { root.put("scene", it) } sceneObj.put("plane_id", payload.planeId) sceneObj.put("x", payload.x) sceneObj.put("y", payload.y) sceneObj.put("z", payload.z) sceneObj.put("calyx_x", payload.calyxX) sceneObj.put("calyx_y", payload.calyxY) sceneObj.put("calyx_z", payload.calyxZ) sceneObj.put("map_layer", payload.mapLayer) sceneObj.put("calyx_group_id", payload.calyxGroupId) sceneObj.put("calyx_inst_id", payload.calyxInstId) sceneObj.put("calyx_entity_id", payload.calyxEntityId) sceneObj.put("calyx_prop_id", payload.calyxPropId) // Verify other keys strictly preserved assertFalse(root.getBoolean("send_hotfix")) assertEquals("secret123", root.getString("key")) assertEquals("Male", root.getJSONObject("avatar").getString("tb_gender")) assertEquals("Harmony", root.getJSONObject("avatar").getString("tb_path")) // Verify updated scene keys val updatedScene = root.getJSONObject("scene") assertEquals(20461, updatedScene.getInt("plane_id")) assertEquals(12080, updatedScene.getInt("x")) assertEquals(393572, updatedScene.getInt("y")) assertEquals(322230, updatedScene.getInt("z")) assertEquals(2, updatedScene.getInt("map_layer")) assertEquals(186, updatedScene.getInt("calyx_group_id")) } @Test fun testTrailblazerConfigUpdate() { val root = JSONObject("""{"avatar": {}}""") val avatar = root.getJSONObject("avatar") val newConfig = TrailblazerConfig(gender = "Female", tbPath = "Elation", marchPath = "Preservation") avatar.put("tb_gender", newConfig.gender) avatar.put("tb_path", newConfig.tbPath) avatar.put("march_path", newConfig.marchPath) assertEquals("Female", avatar.getString("tb_gender")) assertEquals("Elation", avatar.getString("tb_path")) assertEquals("Preservation", avatar.getString("march_path")) } @Test fun testExRankConfigUpdate() { val root = JSONObject("""{"avatar": {}}""") val avatar = root.getJSONObject("avatar") val list = listOf( com.catlas.escapepenacony.data.ExRankConfig("JingYuan", 1204, true), com.catlas.escapepenacony.data.ExRankConfig("DanHeng IL", 1213, false), com.catlas.escapepenacony.data.ExRankConfig("Acheron", 1308, true) ) val array = org.json.JSONArray() for (cfg in list) { val item = JSONObject() item.put("name", cfg.name) item.put("avatar_id", cfg.avatarId) item.put("active", cfg.active) array.put(item) } avatar.put("ex_rank", array) val savedArray = avatar.getJSONArray("ex_rank") assertEquals(3, savedArray.length()) assertEquals("JingYuan", savedArray.getJSONObject(0).getString("name")) assertEquals(1204, savedArray.getJSONObject(0).getInt("avatar_id")) assertTrue(savedArray.getJSONObject(0).getBoolean("active")) assertEquals("DanHeng IL", savedArray.getJSONObject(1).getString("name")) assertFalse(savedArray.getJSONObject(1).getBoolean("active")) assertEquals("Acheron", savedArray.getJSONObject(2).getString("name")) assertTrue(savedArray.getJSONObject(2).getBoolean("active")) } }