fix: add JUnit5 dependency, new tests, StaticResource cleanup
Build & Deploy / build-and-deploy (push) Failing after 30s

- Added quarkus-junit5 test dependency (was missing — caused assertNull failures)
- ExecutableDetectorTest: 12 tests (findAll, findMain prioritization, extender/sfx skip, setup detection)
- CdImageServiceTest: 7 tests (findInDirectory, fixCueReferences, findInBundle)
- ConfigPatcherTest: 8 tests (path rewriting, non-C drive skip, binary/large file skip, unix paths)
- StaticResource: removed unused content types (.html/.js/.css/.conf/.txt)
This commit is contained in:
David Alvarez
2026-06-09 13:01:40 +02:00
parent bb38066069
commit f8adddec52
5 changed files with 354 additions and 4 deletions
+6
View File
@@ -51,6 +51,12 @@
<groupId>io.quarkus</groupId> <groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId> <artifactId>quarkus-arc</artifactId>
</dependency> </dependency>
<!-- Test -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
@@ -72,11 +72,7 @@ public class StaticResource {
if (n.endsWith(".png")) return "image/png"; if (n.endsWith(".png")) return "image/png";
if (n.endsWith(".webp")) return "image/webp"; if (n.endsWith(".webp")) return "image/webp";
if (n.endsWith(".gif")) return "image/gif"; if (n.endsWith(".gif")) return "image/gif";
if (n.endsWith(".conf") || n.endsWith(".txt")) return "text/plain";
if (n.endsWith(".json")) return "application/json"; if (n.endsWith(".json")) return "application/json";
if (n.endsWith(".html")) return "text/html";
if (n.endsWith(".js")) return "application/javascript";
if (n.endsWith(".css")) return "text/css";
return MediaType.APPLICATION_OCTET_STREAM; return MediaType.APPLICATION_OCTET_STREAM;
} }
@@ -0,0 +1,103 @@
package com.dostalgia;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
class CdImageServiceTest {
private final CdImageService service = new CdImageService();
@Test
void findInDirectory_findsAllCdFormats(@TempDir Path dir) throws Exception {
Files.writeString(dir.resolve("game.cue"), "FILE \"game.bin\" BINARY");
Files.writeString(dir.resolve("game.bin"), "data");
Files.writeString(dir.resolve("game.iso"), "iso");
Files.writeString(dir.resolve("readme.txt"), "text");
List<String> result = service.findInDirectory(dir);
assertEquals(3, result.size());
assertTrue(result.contains("game.bin"));
assertTrue(result.contains("game.cue"));
assertTrue(result.contains("game.iso"));
assertFalse(result.contains("readme.txt"));
}
@Test
void findInDirectory_emptyDir(@TempDir Path dir) throws Exception {
assertTrue(service.findInDirectory(dir).isEmpty());
}
@Test
void findInDirectory_caseInsensitive(@TempDir Path dir) throws Exception {
Files.writeString(dir.resolve("GAME.CUE"), "data");
Files.writeString(dir.resolve("GAME.ISO"), "data");
List<String> result = service.findInDirectory(dir);
assertEquals(2, result.size());
}
@Test
void fixCueReferences_rewritesNonexistentReference(@TempDir Path dir) throws Exception {
// CUE references game.bin, but only game.img exists
Files.writeString(dir.resolve("game.cue"), "FILE \"game.bin\" BINARY\n TRACK 01 MODE1/2352\n INDEX 01 00:00:00");
Files.writeString(dir.resolve("game.img"), "data");
service.fixCueReferences(dir);
String fixed = Files.readString(dir.resolve("game.cue"));
assertTrue(fixed.contains("game.img"), "Should reference existing .img file");
assertFalse(fixed.contains("game.bin"), "Should no longer reference missing .bin");
}
@Test
void fixCueReferences_skipsWhenExists(@TempDir Path dir) throws Exception {
// CUE references game.bin — and it exists, so no change
String original = "FILE \"game.bin\" BINARY\n TRACK 01 MODE1/2352\n INDEX 01 00:00:00";
Files.writeString(dir.resolve("game.cue"), original);
Files.writeString(dir.resolve("game.bin"), "data");
service.fixCueReferences(dir);
assertEquals(original, Files.readString(dir.resolve("game.cue")).trim());
}
@Test
void fixCueReferences_noFileLine_noChange(@TempDir Path dir) throws Exception {
String original = "TITLE \"Test Game\"\nTRACK 01 MODE1/2352";
Files.writeString(dir.resolve("game.cue"), original);
service.fixCueReferences(dir);
assertEquals(original, Files.readString(dir.resolve("game.cue")).trim());
}
@Test
void findInBundle_findsCdImages(@TempDir Path dir) throws Exception {
// Create a test bundle with CD images and regular files
Path bundle = dir.resolve("test.jsdos");
try (var zos = new java.util.zip.ZipOutputStream(Files.newOutputStream(bundle))) {
zos.putNextEntry(new java.util.zip.ZipEntry("game.cue"));
zos.write("cue data".getBytes());
zos.closeEntry();
zos.putNextEntry(new java.util.zip.ZipEntry("game.iso"));
zos.write("iso data".getBytes());
zos.closeEntry();
zos.putNextEntry(new java.util.zip.ZipEntry("readme.txt"));
zos.write("text".getBytes());
zos.closeEntry();
zos.putNextEntry(new java.util.zip.ZipEntry("subdir/game.img"));
zos.write("img".getBytes());
zos.closeEntry();
}
List<String> result = service.findInBundle(bundle);
assertEquals(3, result.size());
assertTrue(result.contains("game.cue"));
assertTrue(result.contains("game.iso"));
assertTrue(result.contains("subdir/game.img"));
}
}
@@ -0,0 +1,109 @@
package com.dostalgia;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
class ConfigPatcherTest {
private final ConfigPatcher patcher = new ConfigPatcher();
@Test
void fixAbsolutePaths_rewritesStalePrefix(@TempDir Path dir) throws Exception {
// Create a file that references C:\FALLOUT1\MASTER.DAT
// but after flattening, MASTER.DAT is at the root
String content = "master_dat=C:\\FALLOUT1\\MASTER.DAT\ncritter_dat=C:\\FALLOUT1\\CRITTER.DAT\n";
Files.writeString(dir.resolve("FALLOUT.CFG"), content, StandardCharsets.ISO_8859_1);
// The referenced files exist at root
Files.writeString(dir.resolve("MASTER.DAT"), "data");
Files.writeString(dir.resolve("CRITTER.DAT"), "data");
patcher.fixAbsolutePaths(dir);
String fixed = Files.readString(dir.resolve("FALLOUT.CFG"), StandardCharsets.ISO_8859_1);
assertTrue(fixed.contains(".\\MASTER.DAT"), "Should rewrite to relative path");
assertTrue(fixed.contains(".\\CRITTER.DAT"), "Both files should be rewritten");
assertFalse(fixed.contains("FALLOUT1"), "Stale prefix should be removed");
}
@Test
void fixAbsolutePaths_leavesNonCDrives(@TempDir Path dir) throws Exception {
// D: drive references are CD-ROM — should stay
String content = "cd_path=D:\\GAMEDATA\\SOUND.DAT\n";
Files.writeString(dir.resolve("config.ini"), content, StandardCharsets.ISO_8859_1);
patcher.fixAbsolutePaths(dir);
String fixed = Files.readString(dir.resolve("config.ini"), StandardCharsets.ISO_8859_1);
assertEquals(content.trim(), fixed.trim(), "D: references should be preserved");
}
@Test
void fixAbsolutePaths_leavesValidPaths(@TempDir Path dir) throws Exception {
// Path exists with full structure — should stay as-is
Path sub = Files.createDirectory(dir.resolve("VALIDDIR"));
Files.writeString(sub.resolve("file.dat"), "data");
String content = "data=C:\\VALIDDIR\\file.dat\n";
Files.writeString(dir.resolve("config.ini"), content, StandardCharsets.ISO_8859_1);
patcher.fixAbsolutePaths(dir);
String fixed = Files.readString(dir.resolve("config.ini"), StandardCharsets.ISO_8859_1);
assertEquals(content.trim(), fixed.trim(), "Valid full paths should be preserved");
}
@Test
void fixAbsolutePaths_skipsNonTextFiles(@TempDir Path dir) throws Exception {
// Binary file with null bytes — should not be processed
byte[] binary = new byte[200];
binary[0] = 'C';
binary[1] = ':';
binary[2] = '\\';
binary[3] = 'D';
binary[10] = 0; // null byte makes it "not text"
Files.write(dir.resolve("binary.dat"), binary);
// Should not throw or modify binary files
assertDoesNotThrow(() -> patcher.fixAbsolutePaths(dir));
byte[] result = Files.readAllBytes(dir.resolve("binary.dat"));
assertArrayEquals(binary, result, "Binary file should be unchanged");
}
@Test
void fixAbsolutePaths_skipsLargeFiles(@TempDir Path dir) throws Exception {
// File > 100KB should be skipped
byte[] large = new byte[101 * 1024];
large[0] = 'C';
large[1] = ':';
large[2] = '\\';
Files.write(dir.resolve("large.cfg"), large);
// Should not throw
assertDoesNotThrow(() -> patcher.fixAbsolutePaths(dir));
}
@Test
void fixAbsolutePaths_emptyDir_noError(@TempDir Path dir) {
assertDoesNotThrow(() -> patcher.fixAbsolutePaths(dir));
}
@Test
void fixAbsolutePaths_unixStylePath(@TempDir Path dir) throws Exception {
// Some games use forward slashes: C:/GAME/DATA
String content = "path=C:/MYGAME/DATA.DAT\n";
Files.writeString(dir.resolve("config.cfg"), content, StandardCharsets.ISO_8859_1);
Files.writeString(dir.resolve("DATA.DAT"), "data");
patcher.fixAbsolutePaths(dir);
String fixed = Files.readString(dir.resolve("config.cfg"), StandardCharsets.ISO_8859_1);
assertTrue(fixed.contains("./DATA.DAT"), "Forward-slash paths should be fixed");
}
}
@@ -0,0 +1,136 @@
package com.dostalgia;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
class ExecutableDetectorTest {
private final ExecutableDetector detector = new ExecutableDetector();
@Test
void findAll_findsExeComBat(@TempDir Path dir) throws Exception {
Files.writeString(dir.resolve("GAME.EXE"), "MZ");
Files.writeString(dir.resolve("helper.com"), "MZ");
Files.writeString(dir.resolve("run.bat"), "@echo off");
Files.writeString(dir.resolve("readme.txt"), "text"); // should NOT be found
List<String> result = detector.findAll(dir);
assertEquals(3, result.size());
assertTrue(result.contains("GAME.EXE"));
assertTrue(result.contains("helper.com"));
assertTrue(result.contains("run.bat"));
assertFalse(result.contains("readme.txt"));
}
@Test
void findMain_prefersLargestNonInstaller(@TempDir Path dir) throws Exception {
// Small installer
Files.write(dir.resolve("INSTALL.EXE"), createMZ(100));
// Large real game
Files.write(dir.resolve("GAME.EXE"), createMZ(5000));
String main = detector.findMain(dir);
assertEquals(dir.resolve("GAME.EXE").toString(), main);
}
@Test
void findMain_skipsExtenders(@TempDir Path dir) throws Exception {
Files.write(dir.resolve("DOS4GW.EXE"), createMZ(200));
Files.write(dir.resolve("GAME.EXE"), createMZ(1000));
String main = detector.findMain(dir);
assertEquals(dir.resolve("GAME.EXE").toString(), main);
}
@Test
void findMain_skipsSelfExtractors(@TempDir Path dir) throws Exception {
byte[] sfx = createMZ(500);
// Embed PKSFX string
String content = new String(sfx);
int pos = content.indexOf("MZ") + 2;
byte[] withSfx = new byte[sfx.length + 20];
System.arraycopy(sfx, 0, withSfx, 0, sfx.length);
byte[] pksfx = "PKSFX".getBytes();
System.arraycopy(pksfx, 0, withSfx, pos, pksfx.length);
Files.write(dir.resolve("GAME.EXE"), withSfx);
Files.write(dir.resolve("REAL.EXE"), createMZ(2000));
String main = detector.findMain(dir);
assertEquals(dir.resolve("REAL.EXE").toString(), main);
}
@Test
void findMain_emptyDir_returnsNull(@TempDir Path dir) {
assertNull(detector.findMain(dir));
}
@Test
void findMain_subdirectoryDeprioritized(@TempDir Path dir) throws Exception {
// Root-level large exe
Files.write(dir.resolve("GAME.EXE"), createMZ(1000));
// Subdirectory exe (should be deprioritized even if larger)
Path sub = Files.createDirectory(dir.resolve("SUBDIR"));
Files.write(sub.resolve("OTHER.EXE"), createMZ(2000));
String main = detector.findMain(dir);
assertEquals(dir.resolve("GAME.EXE").toString(), main);
}
@Test
void findSetup_findsSetupExe(@TempDir Path dir) throws Exception {
Files.writeString(dir.resolve("SETUP.EXE"), "MZ");
Files.writeString(dir.resolve("GAME.EXE"), "MZ");
String setup = detector.findSetup(dir);
assertEquals(dir.resolve("SETUP.EXE").toString(), setup);
}
@Test
void findSetup_findsInstallBat(@TempDir Path dir) throws Exception {
Files.writeString(dir.resolve("INSTALL.BAT"), "@echo off");
String setup = detector.findSetup(dir);
assertEquals(dir.resolve("INSTALL.BAT").toString(), setup);
}
@Test
void findSetup_noSetup_returnsNull(@TempDir Path dir) throws Exception {
Files.writeString(dir.resolve("GAME.EXE"), "MZ");
assertNull(detector.findSetup(dir));
}
@Test
void isLikelySelfExtractor_detectsPKSFX(@TempDir Path dir) throws Exception {
byte[] buf = new byte[1000];
buf[0] = 0x4D; // M
buf[1] = 0x5A; // Z
byte[] pksfx = "PKSFX".getBytes();
System.arraycopy(pksfx, 0, buf, 10, pksfx.length);
Path f = dir.resolve("sfx.exe");
Files.write(f, buf);
assertTrue(ExecutableDetector.isLikelySelfExtractor(f));
}
@Test
void isLikelySelfExtractor_plainExe_returnsFalse(@TempDir Path dir) throws Exception {
byte[] buf = createMZ(500);
Path f = dir.resolve("game.exe");
Files.write(f, buf);
assertFalse(ExecutableDetector.isLikelySelfExtractor(f));
}
// ─── helpers ─────────────────────────────────────────────────
private static byte[] createMZ(int size) {
byte[] buf = new byte[size];
buf[0] = 0x4D; // M
buf[1] = 0x5A; // Z
return buf;
}
}