forked from lambda-client/lambda-loader
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathVersionControllerTest.kt
More file actions
366 lines (279 loc) · 16.2 KB
/
VersionControllerTest.kt
File metadata and controls
366 lines (279 loc) · 16.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
package com.lambda.loader
import com.lambda.loader.config.ConfigManager
import com.lambda.loader.config.ReleaseMode
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.DynamicTest
import org.junit.jupiter.api.TestFactory
import java.net.URI
import javax.xml.parsers.DocumentBuilderFactory
class VersionControllerTest {
// We'll create the VersionController in each test with the appropriate MC version
// This is necessary because FabricLoader is not available in the test environment
companion object {
private const val MAVEN_RELEASES_URL = "https://maven.lambda-client.org/releases/com/lambda/lambda/maven-metadata.xml"
private const val MAVEN_SNAPSHOTS_URL = "https://maven.lambda-client.org/snapshots/com/lambda/lambda/maven-metadata.xml"
/**
* Fetches all unique Minecraft versions available in Maven repositories
*/
fun getAvailableMinecraftVersions(): Set<String> {
val versions = mutableSetOf<String>()
// Get versions from releases
try {
val releasesXml = URI(MAVEN_RELEASES_URL).toURL().readText()
versions.addAll(parseMinecraftVersionsFromMaven(releasesXml))
} catch (e: Exception) {
println("Warning: Could not fetch releases: ${e.message}")
}
// Get versions from snapshots
try {
val snapshotsXml = URI(MAVEN_SNAPSHOTS_URL).toURL().readText()
versions.addAll(parseMinecraftVersionsFromMaven(snapshotsXml))
} catch (e: Exception) {
println("Warning: Could not fetch snapshots: ${e.message}")
}
return versions
}
/**
* Parses Maven metadata XML to extract unique Minecraft versions
*/
private fun parseMinecraftVersionsFromMaven(xml: String): Set<String> {
val mcVersions = mutableSetOf<String>()
try {
val factory = DocumentBuilderFactory.newInstance()
val builder = factory.newDocumentBuilder()
val document = builder.parse(xml.byteInputStream())
val versionNodes = document.getElementsByTagName("version")
for (i in 0 until versionNodes.length) {
val version = versionNodes.item(i).textContent
// Extract MC version from format: X.X.X+MC_VERSION or X.X.X+MC_VERSION-SNAPSHOT
val mcVersion = version.substringAfter("+").substringBefore("-")
mcVersions.add(mcVersion)
}
} catch (e: Exception) {
println("Error parsing Maven metadata: ${e.message}")
}
return mcVersions
}
}
@TestFactory
fun testAllMinecraftVersions() = sequence {
val availableVersions = getAvailableMinecraftVersions()
println("Found Minecraft versions in Maven: ${availableVersions.joinToString(", ")}")
for (mcVersion in availableVersions.sorted()) {
yield(DynamicTest.dynamicTest("Test Minecraft version $mcVersion") {
testMinecraftVersionAvailability(mcVersion)
})
}
}.asIterable()
private fun testMinecraftVersionAvailability(mcVersion: String) {
println("\n=== Testing Minecraft version: $mcVersion ===")
// Create VersionController with the specific MC version for testing
val versionController = VersionController(minecraftVersionOverride = mcVersion)
// Test STABLE mode
println("Checking STABLE releases for MC $mcVersion...")
ConfigManager.config = ConfigManager.config.copy(clientReleaseMode = ReleaseMode.STABLE)
val stableJar = versionController.getOrDownloadLatestVersion()
if (stableJar != null) {
println("✓ Found STABLE release: ${stableJar.name}")
assertTrue(stableJar.exists(), "STABLE JAR should exist")
if(stableJar.name.contains(mcVersion)) {
println("✓ STABLE JAR found for MC $mcVersion")
}
} else {
println("⚠ No STABLE release found for MC $mcVersion")
}
// Test SNAPSHOT mode
println("Checking SNAPSHOT releases for MC $mcVersion...")
ConfigManager.config = ConfigManager.config.copy(clientReleaseMode = ReleaseMode.SNAPSHOT)
val snapshotJar = versionController.getOrDownloadLatestVersion()
if (snapshotJar != null) {
println("✓ Found SNAPSHOT release: ${snapshotJar.name}")
assertTrue(snapshotJar.exists(), "SNAPSHOT JAR should exist")
// Snapshot filename format: lambda-X.X.X-timestamp-buildnumber.jar
val hasVersion = snapshotJar.name.contains(mcVersion)
assertTrue(hasVersion, "SNAPSHOT JAR should be for MC $mcVersion, got: ${snapshotJar.name}")
} else {
println("⚠ No SNAPSHOT release found for MC $mcVersion")
}
// At least one should be available
assertTrue(stableJar != null || snapshotJar != null,
"At least one release (STABLE or SNAPSHOT) should exist for MC $mcVersion")
}
@Test
fun testGetOrDownloadLatestVersionStable() {
println("Testing getOrDownloadLatestVersion with STABLE mode...")
// Use the first available MC version for testing
val availableVersions = getAvailableMinecraftVersions()
assertTrue(availableVersions.isNotEmpty(), "Should have at least one MC version available")
val mcVersion = availableVersions.sorted().first()
val versionController = VersionController(minecraftVersionOverride = mcVersion)
// Set to STABLE mode
ConfigManager.config = ConfigManager.config.copy(clientReleaseMode = ReleaseMode.STABLE)
val jarFile = versionController.getOrDownloadLatestVersion()
assertNotNull(jarFile, "JAR file should not be null")
assertTrue(jarFile!!.exists(), "JAR file should exist")
assertTrue(jarFile.isFile, "Should be a file")
assertTrue(jarFile.name.endsWith(".jar"), "Should be a JAR file")
assertFalse(jarFile.name.contains("SNAPSHOT"), "STABLE version should not contain SNAPSHOT")
// Check that the JAR filename contains a Minecraft version
val hasMinecraftVersion = jarFile.name.contains(Regex("\\+\\d+\\.\\d+\\.\\d+"))
assertTrue(hasMinecraftVersion, "JAR filename should contain Minecraft version (format: +X.XX.XX)")
println("Downloaded/cached STABLE JAR: ${jarFile.absolutePath}")
println("File size: ${jarFile.length()} bytes")
}
@Test
fun testGetOrDownloadLatestVersionSnapshot() {
println("Testing getOrDownloadLatestVersion with SNAPSHOT mode...")
// Use the first available MC version for testing
val availableVersions = getAvailableMinecraftVersions()
assertTrue(availableVersions.isNotEmpty(), "Should have at least one MC version available")
val mcVersion = availableVersions.sorted().first()
val versionController = VersionController(minecraftVersionOverride = mcVersion)
// Set to SNAPSHOT mode
ConfigManager.config = ConfigManager.config.copy(clientReleaseMode = ReleaseMode.SNAPSHOT)
val jarFile = versionController.getOrDownloadLatestVersion()
assertNotNull(jarFile, "JAR file should not be null")
assertTrue(jarFile!!.exists(), "JAR file should exist")
assertTrue(jarFile.isFile, "Should be a file")
assertTrue(jarFile.name.endsWith(".jar"), "Should be a JAR file")
// Check that the JAR filename contains a Minecraft version
val hasMinecraftVersion = jarFile.name.contains(Regex("\\+\\d+\\.\\d+\\.\\d+"))
assertTrue(hasMinecraftVersion, "JAR filename should contain Minecraft version (format: +X.XX.XX)")
println("Downloaded/cached SNAPSHOT JAR: ${jarFile.absolutePath}")
println("File size: ${jarFile.length()} bytes")
}
@Test
fun testGetOrDownloadLatestVersionCaching() {
println("Testing that getOrDownloadLatestVersion uses cache...")
// Use the first available MC version for testing
val availableVersions = getAvailableMinecraftVersions()
assertTrue(availableVersions.isNotEmpty(), "Should have at least one MC version available")
val mcVersion = availableVersions.sorted().first()
val versionController = VersionController(minecraftVersionOverride = mcVersion)
ConfigManager.config = ConfigManager.config.copy(clientReleaseMode = ReleaseMode.STABLE)
// First call - should download
val jarFile1 = versionController.getOrDownloadLatestVersion()
assertNotNull(jarFile1, "First call should succeed")
val firstModified = jarFile1!!.lastModified()
// Second call - should use cache
Thread.sleep(100) // Small delay to ensure different timestamp if file is recreated
val jarFile2 = versionController.getOrDownloadLatestVersion()
assertNotNull(jarFile2, "Second call should succeed")
// Should be the same file
assertEquals(jarFile1.absolutePath, jarFile2!!.absolutePath, "Should return same file path")
assertEquals(firstModified, jarFile2.lastModified(), "File should not be recreated (cache used)")
println("Cache validation successful - file not recreated")
}
@Test
fun testGetOrDownloadLatestVersionFileStructure() {
println("Testing JAR file structure...")
// Use the first available MC version for testing
val availableVersions = getAvailableMinecraftVersions()
assertTrue(availableVersions.isNotEmpty(), "Should have at least one MC version available")
val mcVersion = availableVersions.sorted().first()
val versionController = VersionController(minecraftVersionOverride = mcVersion)
ConfigManager.config = ConfigManager.config.copy(clientReleaseMode = ReleaseMode.STABLE)
val jarFile = versionController.getOrDownloadLatestVersion()
assertNotNull(jarFile, "JAR file should not be null")
// Read first few bytes to verify it's a valid JAR (ZIP file)
val bytes = jarFile!!.readBytes()
assertTrue(bytes.size > 1000, "JAR should be larger than 1000 bytes")
assertEquals('P'.code.toByte(), bytes[0], "JAR should start with 'P' (ZIP signature)")
assertEquals('K'.code.toByte(), bytes[1], "JAR should have 'K' as second byte (ZIP signature)")
println("JAR file structure validated")
}
@Test
fun testSwitchingBetweenReleaseAndSnapshot() {
println("Testing switching between STABLE and SNAPSHOT modes...")
// Use the first available MC version for testing
val availableVersions = getAvailableMinecraftVersions()
assertTrue(availableVersions.isNotEmpty(), "Should have at least one MC version available")
val mcVersion = availableVersions.sorted().first()
val versionController = VersionController(minecraftVersionOverride = mcVersion)
// Get STABLE version
ConfigManager.config = ConfigManager.config.copy(clientReleaseMode = ReleaseMode.STABLE)
val stableJar = versionController.getOrDownloadLatestVersion()
assertNotNull(stableJar, "STABLE JAR should not be null")
println("STABLE: ${stableJar!!.name}")
// Switch to SNAPSHOT
ConfigManager.config = ConfigManager.config.copy(clientReleaseMode = ReleaseMode.SNAPSHOT)
val snapshotJar = versionController.getOrDownloadLatestVersion()
assertNotNull(snapshotJar, "SNAPSHOT JAR should not be null")
println("SNAPSHOT: ${snapshotJar!!.name}")
// Filenames should be different (unless there's only one version available)
// This is acceptable behavior when testing with limited versions
println("STABLE filename: ${stableJar.name}")
println("SNAPSHOT filename: ${snapshotJar.name}")
println("Successfully switched between release modes")
}
@Test
fun testMinecraftVersionMatching() {
println("Testing that downloaded versions match available Minecraft versions...")
val availableVersions = getAvailableMinecraftVersions()
println("Available Minecraft versions from Maven: ${availableVersions.joinToString(", ")}")
assertTrue(availableVersions.isNotEmpty(), "Should have at least one Minecraft version available")
for (mcVersion in availableVersions.sorted()) {
println("\n--- Testing MC version: $mcVersion ---")
val versionController = VersionController(minecraftVersionOverride = mcVersion)
// Test SNAPSHOT mode (more likely to have versions available)
ConfigManager.config = ConfigManager.config.copy(clientReleaseMode = ReleaseMode.SNAPSHOT)
val jarFile = versionController.getOrDownloadLatestVersion()
if (jarFile != null) {
println("Downloaded: ${jarFile.name}")
// Parse the Minecraft version from the filename
// Format: lambda-X.X.X+MC_VERSION-timestamp-buildnumber.jar or lambda-X.X.X+MC_VERSION.jar
val versionPattern = Regex("\\+(\\d+\\.\\d+\\.\\d+)")
val match = versionPattern.find(jarFile.name)
if (match != null) {
val downloadedMcVersion = match.groupValues[1]
println("Parsed MC version from JAR: $downloadedMcVersion")
// Verify the downloaded version is one of the available versions
assertTrue(availableVersions.contains(downloadedMcVersion),
"Downloaded MC version ($downloadedMcVersion) should be in available versions: ${availableVersions.joinToString(", ")}")
println("✓ Version match confirmed")
} else {
fail("Could not parse Minecraft version from JAR filename: ${jarFile.name}")
}
} else {
println("⚠ No JAR available for MC $mcVersion")
}
}
}
@Test
fun testStableToSnapshotFallback() {
println("Testing fallback from STABLE to SNAPSHOT when no stable version exists...")
// Use a Minecraft version that might only have snapshots
val availableVersions = getAvailableMinecraftVersions()
assertTrue(availableVersions.isNotEmpty(), "Should have at least one MC version available")
// Try to find a version that has both stable and snapshot, or just snapshot
val mcVersion = availableVersions.sorted().last() // Latest version more likely to have snapshots
println("Testing with Minecraft version: $mcVersion")
val versionController = VersionController(minecraftVersionOverride = mcVersion)
// Set to STABLE mode - should fallback to snapshot if stable doesn't exist
ConfigManager.config = ConfigManager.config.copy(clientReleaseMode = ReleaseMode.STABLE, debug = true)
val jarFile = versionController.getOrDownloadLatestVersion()
if (jarFile != null) {
println("✓ Successfully retrieved version (with potential fallback): ${jarFile.name}")
assertTrue(jarFile.exists(), "JAR file should exist")
assertTrue(jarFile.name.endsWith(".jar"), "Should be a JAR file")
println("Fallback test passed!")
} else {
println("⚠ No version found even with fallback - this is expected if neither stable nor snapshot exists for MC $mcVersion")
}
}
@Test
fun testNoVersionAvailableErrorMessage() {
println("Testing error message when no version is available...")
// Use a non-existent Minecraft version to trigger the error
val nonExistentVersion = "99.99.99"
println("Testing with non-existent Minecraft version: $nonExistentVersion")
val versionController = VersionController(minecraftVersionOverride = nonExistentVersion)
// Set to STABLE mode
ConfigManager.config = ConfigManager.config.copy(clientReleaseMode = ReleaseMode.STABLE, debug = true)
val jarFile = versionController.getOrDownloadLatestVersion()
// Should return null and log error messages
assertNull(jarFile, "Should return null when no version is available")
println("✓ Error handling test passed - null returned as expected")
}
}