This commit is contained in:
Brian Neumann-Fopiano committed 2026-08-12 16:31:27 -04:00
1 parent c59592e448
commit 13e02ecd2f
41 files changed
+352 -449

No files matched your search

@@ -37,23 +37,45 @@ import static org.junit.Assert.assertTrue;
public class DefaultPackBootstrapProvisionerTest {
@Test
public void defaultBetaSourcesArePinnedPerRequiredPack() {
Map<String, DefaultPackBootstrapProvisioner.PackSpec> packs = new LinkedHashMap<>();
for (DefaultPackBootstrapProvisioner.PackSpec pack : DefaultPackBootstrapProvisioner.defaultPacks()) {
packs.put(pack.key(), pack);
}
public void startupDoesNotRequireOrDownloadDefaultPacks() {
assertTrue(DefaultPackBootstrapProvisioner.defaultPacks().isEmpty());
}
assertEquals(2, packs.size());
assertEquals(
URI.create("https://github.com/IrisDimensions/overworld/releases/download/beta/overworld.zip"),
packs.get("overworld").source()
);
assertEquals("overworld", packs.get("overworld").requiredDimension());
assertEquals(
URI.create("https://github.com/IrisDimensions/underworld/releases/download/beta/underworld.zip"),
packs.get("underworld").source()
);
assertEquals("underworld", packs.get("underworld").requiredDimension());
@Test
public void emptyInstallPublishesValidDatapackWithoutNetworkRequests() throws Exception {
AtomicInteger requests = new AtomicInteger();
HttpServer server = server(packArchive("overworld", "unused"), requests);
Path root = Files.createTempDirectory("iris-bootstrap-empty");
try {
Path dataDirectory = root.resolve("plugins/Iris");
DefaultPackBootstrapProvisioner.ProvisionOptions options = new DefaultPackBootstrapProvisioner.ProvisionOptions(
List.of(),
HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(2)).build(),
Clock.fixed(Instant.parse("2026-07-12T12:00:00Z"), ZoneOffset.UTC),
Duration.ofHours(1),
Duration.ofSeconds(2),
1,
Duration.ZERO,
8L * 1024L * 1024L,
root
);
DefaultPackBootstrapProvisioner.ProvisionResult result = DefaultPackBootstrapProvisioner.provision(
dataDirectory,
ignored -> {
},
options
);
assertEquals(DefaultPackBootstrapProvisioner.ProvisionStatus.INSTALLED, result.status());
assertTrue(result.packRoots().isEmpty());
assertEquals(0, requests.get());
assertTrue(Files.isRegularFile(result.datapackRoot().resolve("pack.mcmeta")));
assertTrue(DefaultPackBootstrapProvisioner.isProvisioned(dataDirectory, root, List.of()));
} finally {
server.stop(0);
delete(root);
}
}
@Test
@@ -31,8 +31,10 @@ import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.Answers;
import com.sun.net.httpserver.HttpServer;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -44,6 +46,7 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -53,6 +56,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -71,6 +75,13 @@ public class PackDownloaderTest {
IrisPlatform platform = mock(IrisPlatform.class, Answers.CALLS_REAL_METHODS);
PlatformStructureHooks structureHooks = mock(PlatformStructureHooks.class, Answers.CALLS_REAL_METHODS);
when(platform.dataFolder()).thenReturn(temp.getRoot());
when(platform.dataFile(any(String[].class))).thenAnswer(invocation -> {
File file = temp.getRoot();
for (Object argument : invocation.getArguments()) {
file = new File(file, String.valueOf(argument));
}
return file;
});
when(platform.structureHooks()).thenReturn(structureHooks);
when(structureHooks.structureKeys()).thenReturn(List.of("minecraft:village"));
when(structureHooks.jigsawStructureKeys()).thenReturn(List.of("minecraft:village"));
@@ -90,23 +101,74 @@ public class PackDownloaderTest {
}
@Test
public void resolvesDefaultOverworldBetaRelease() {
assertEquals(
"https://github.com/IrisDimensions/overworld/releases/download/beta/overworld.zip",
PackDownloader.defaultOverworldReleaseUrl()
);
public void resolvesDefaultOverworldGitSource() {
assertEquals("IrisDimensions/overworld", PackDownloader.defaultOverworldRepository());
assertEquals("master", PackDownloader.defaultOverworldRef());
assertTrue(PackDownloader.isDefaultOverworld("overworld"));
assertTrue(PackDownloader.isManagedBetaPack("overworld"));
assertEquals(List.of("overworld", "underworld"), PackDownloader.managedBetaPacks());
assertTrue(PackDownloader.isManagedPack("overworld"));
assertEquals(List.of("overworld", "underworld"), PackDownloader.managedPacks());
}
@Test
public void resolvesUnderworldBetaRelease() {
assertEquals(
"https://github.com/IrisDimensions/underworld/releases/download/beta/underworld.zip",
PackDownloader.underworldReleaseUrl()
);
assertTrue(PackDownloader.isManagedBetaPack("underworld"));
public void resolvesUnderworldGitSource() {
assertEquals("IrisDimensions/underworld", PackDownloader.underworldRepository());
assertEquals("main", PackDownloader.underworldRef());
assertTrue(PackDownloader.isManagedPack("underworld"));
}
@Test
public void recognizesOnlyHttpZipUrlsAsDirectSources() {
assertTrue(PackDownloader.isDirectZipUrl("https://packs.example.test/overworld.zip"));
assertTrue(PackDownloader.isDirectZipUrl("http://127.0.0.1/pack.ZIP?token=value"));
assertFalse(PackDownloader.isDirectZipUrl("https://packs.example.test/overworld.tar.gz"));
assertFalse(PackDownloader.isDirectZipUrl("file:///tmp/overworld.zip"));
assertFalse(PackDownloader.isDirectZipUrl("not-a-url"));
}
@Test
public void directZipUrlInstallsSingleDimensionPackAndRequiresRestart() throws Exception {
Path archive = temp.newFile("direct-pack.zip").toPath();
Map<String, String> entries = new LinkedHashMap<>();
entries.put("README.md", "Direct pack");
entries.put("wrapped/dimensions/direct_pack.json", "{\"name\":\"Direct\",\"regions\":[\"local\"],"
+ "\"structures\":[{\"nativeStructures\":[{\"structure\":\"test:future_structure\"}]}],"
+ "\"logicalHeight\":256,\"dimensionHeight\":{\"min\":-64,\"max\":320}}");
entries.put("wrapped/regions/local.json", "{\"name\":\"Local\",\"landBiomes\":[\"local\"]}");
entries.put("wrapped/biomes/local.json", "{\"name\":\"Local\",\"derivative\":\"minecraft:plains\"}");
writeArchive(archive, entries);
byte[] response = Files.readAllBytes(archive);
AtomicInteger requests = new AtomicInteger();
HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
server.createContext("/direct-pack.zip", exchange -> {
requests.incrementAndGet();
exchange.sendResponseHeaders(200, response.length);
exchange.getResponseBody().write(response);
exchange.close();
});
server.start();
try {
File packsFolder = temp.newFolder("direct-url-packs");
String url = "http://127.0.0.1:" + server.getAddress().getPort() + "/direct-pack.zip";
PackDownloader.PackInstallResult result = PackDownloader.downloadUrl(
packsFolder,
url,
false,
ignored -> {
}
);
assertNotNull(result);
assertEquals("direct_pack", result.key());
assertTrue(result.changed());
assertTrue(result.restartRequired());
assertEquals(1, requests.get());
assertTrue(Files.isRegularFile(
packsFolder.toPath().resolve("direct_pack/dimensions/direct_pack.json")
));
} finally {
server.stop(0);
}
}
@Test
@@ -118,11 +180,11 @@ public class PackDownloaderTest {
writeDimension(packsFolder.toPath().resolve("underworld"), "underworld_roof");
assertTrue(PackDownloader.isPackPresent(packsFolder, "underworld"));
assertFalse(PackDownloader.isManagedBetaPackPresent(packsFolder, "underworld"));
assertFalse(PackDownloader.isManagedPackPresent(packsFolder, "underworld"));
writeDimension(packsFolder.toPath().resolve("underworld"), "underworld");
assertTrue(Files.isDirectory(dimensions));
assertTrue(PackDownloader.isManagedBetaPackPresent(packsFolder, "underworld"));
assertTrue(PackDownloader.isManagedPackPresent(packsFolder, "underworld"));
}
@Test
@@ -146,6 +208,7 @@ public class PackDownloaderTest {
assertNotNull(result);
assertTrue(result.changed());
assertTrue(result.restartRequired());
assertTrue(Files.isRegularFile(target.resolve("dimensions/underworld.json")));
assertTrue(Files.isRegularFile(target.resolve("dimensions/underworld_roof.json")));
assertFalse(Files.exists(target.resolve("partial.txt")));
@@ -157,9 +220,9 @@ public class PackDownloaderTest {
assertFalse(PackDownloader.isDefaultOverworld("theend"));
assertFalse(PackDownloader.isDefaultOverworld(""));
assertFalse(PackDownloader.isDefaultOverworld(null));
assertFalse(PackDownloader.isManagedBetaPack("theend"));
assertFalse(PackDownloader.isManagedBetaPack(""));
assertFalse(PackDownloader.isManagedBetaPack(null));
assertFalse(PackDownloader.isManagedPack("theend"));
assertFalse(PackDownloader.isManagedPack(""));
assertFalse(PackDownloader.isManagedPack(null));
}
@Test
@@ -299,7 +362,7 @@ public class PackDownloaderTest {
assertEquals(feedback.toString(), "replaceable", result.key());
assertTrue(result.changed());
assertFalse(result.restartRequired());
assertTrue(result.restartRequired());
assertEquals("new", Files.readString(target.toPath().resolve("state.txt"), StandardCharsets.UTF_8));
assertFalse(Files.exists(target.toPath().resolve("old-only.txt")));
assertTransactionStateClean(packsFolder);
@@ -1,78 +0,0 @@
/*
* Iris is a World Generator for Minecraft Servers
* Copyright (c) 2026 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.core.service;
import org.junit.Test;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Stream;
import static org.junit.Assert.assertEquals;
public class StudioSVCManagedBetaPackTest {
@Test
public void startupSelectsOnlyMissingManagedBetaPacks() throws IOException {
Path workspace = Files.createTempDirectory("iris-managed-beta-startup");
try {
assertEquals(
List.of("overworld", "underworld"),
StudioSVC.missingManagedBetaPacks(workspace.toFile())
);
createPack(workspace, "overworld");
assertEquals(
List.of("underworld"),
StudioSVC.missingManagedBetaPacks(workspace.toFile())
);
createDimension(workspace, "underworld", "underworld_roof");
assertEquals(
List.of("underworld"),
StudioSVC.missingManagedBetaPacks(workspace.toFile())
);
createPack(workspace, "underworld");
assertEquals(List.of(), StudioSVC.missingManagedBetaPacks(workspace.toFile()));
} finally {
deleteTree(workspace);
}
}
private static void createPack(Path workspace, String key) throws IOException {
createDimension(workspace, key, key);
}
private static void createDimension(Path workspace, String folder, String key) throws IOException {
Path dimensions = Files.createDirectories(workspace.resolve(folder).resolve("dimensions"));
Files.writeString(dimensions.resolve(key + ".json"), "{}", StandardCharsets.UTF_8);
}
private static void deleteTree(Path root) throws IOException {
try (Stream<Path> paths = Files.walk(root)) {
for (Path path : paths.sorted(Comparator.comparingInt(Path::getNameCount).reversed()).toList()) {
Files.deleteIfExists(path);
}
}
}
}
@@ -0,0 +1,24 @@
package art.arcane.iris.core.service;
import org.junit.Test;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class StudioSVCPackDownloadContractTest {
@Test
public void downloadsRequireManualRestartWithoutMutatingLiveDatapacks() throws Exception {
String source = Files.readString(Path.of("src/main/java/art/arcane/iris/core/service/StudioSVC.java"));
int mutationStart = source.indexOf("private void runPackMutation(");
int mutationEnd = source.indexOf("private boolean finishStandalonePackMutation(", mutationStart);
int finishEnd = source.indexOf("private void runOffPrimaryThread(", mutationEnd);
String downloadMutation = source.substring(mutationStart, finishEnd);
assertTrue(downloadMutation.contains("Restart the server before using the downloaded Iris pack."));
assertFalse(downloadMutation.contains("ServerConfigurator.restart()"));
assertFalse(downloadMutation.contains("installDataPacksIfChanged"));
}
}