mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-26 05:50:39 +00:00
Iris downloader / History update
- Allow people to download in place and update in place the media
This commit is contained in:
1 parent
b18681a3e2
commit
edadd14f4b
40 files changed
+836
-122
No files matched your search
@@ -1,11 +1,28 @@
|
||||
package art.arcane.iris.diagnostics.splash;
|
||||
|
||||
import art.arcane.iris.pack.BuiltInPackUpdates;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
public class IrisSplashComposerTest {
|
||||
@Test
|
||||
public void packLinesShowUpdatesAndKeepCustomPacks() {
|
||||
List<IrisSplashPackScanner.SplashPackMetadata> packs = List.of(
|
||||
new IrisSplashPackScanner.SplashPackMetadata("custom", "7"),
|
||||
new IrisSplashPackScanner.SplashPackMetadata("overworld", "4009"),
|
||||
new IrisSplashPackScanner.SplashPackMetadata("underworld", "1012"));
|
||||
assertEquals(List.of("Custom Dimensions: 3", " custom v7", " overworld v4009 -> v4010 available",
|
||||
" underworld v1012", "Update overworld: /iris download pack=overworld overwrite=true"),
|
||||
IrisSplashComposer.composePackLines(packs, Map.of(
|
||||
"overworld", new BuiltInPackUpdates.Update("4010"),
|
||||
"underworld", new BuiltInPackUpdates.Update("1012"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void composeInfoUsesCurrentVersionWithoutStaleReleaseTag() {
|
||||
String[] info = IrisSplashComposer.composeInfo("4.0.0-26.2", "Paper 26.2", IrisSplashComposer.InfoStyle.PLAIN);
|
||||
|
||||
@@ -18,6 +18,31 @@ public class AtomicDirectoryPublisherTest {
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
|
||||
@Test
|
||||
public void retainedBackupSurvivesCommitAndRemainsAvailableForRollback() throws Exception {
|
||||
Path root = temporaryFolder.getRoot().toPath();
|
||||
Path target = Files.createDirectory(root.resolve("target"));
|
||||
Files.writeString(target.resolve("value.txt"), "old");
|
||||
Path staged = Files.createDirectory(root.resolve("stage"));
|
||||
Files.writeString(staged.resolve("value.txt"), "new");
|
||||
Path retained = root.resolve("retained");
|
||||
|
||||
try (AtomicDirectoryPublisher.Publication publication = AtomicDirectoryPublisher.publish(staged, target)) {
|
||||
assertTrue(publication.retainBackup(retained));
|
||||
}
|
||||
|
||||
assertEquals("old", Files.readString(target.resolve("value.txt")));
|
||||
assertFalse(Files.exists(retained));
|
||||
Files.createDirectory(staged);
|
||||
Files.writeString(staged.resolve("value.txt"), "new");
|
||||
try (AtomicDirectoryPublisher.Publication publication = AtomicDirectoryPublisher.publish(staged, target)) {
|
||||
assertTrue(publication.retainBackup(retained));
|
||||
publication.commit();
|
||||
}
|
||||
assertEquals("old", Files.readString(retained.resolve("value.txt")));
|
||||
assertEquals("new", Files.readString(target.resolve("value.txt")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void commitPublishesStagedDirectoryAndRemovesBackup() throws Exception {
|
||||
Path root = temporaryFolder.getRoot().toPath();
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
package art.arcane.iris.pack;
|
||||
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class BuiltInPackUpdatesTest {
|
||||
@Test
|
||||
public void comparesNumericVersionsWithoutDowngradeNotices() {
|
||||
BuiltInPackUpdates.Update update = new BuiltInPackUpdates.Update("4010");
|
||||
assertEquals(" -> v4010 available", update.suffix("999"));
|
||||
assertEquals("", update.suffix("4010"));
|
||||
assertEquals("", update.suffix("5000"));
|
||||
assertEquals(" (latest v4010)", update.suffix("custom"));
|
||||
assertFalse(update.newerThan("custom"));
|
||||
assertEquals(" (update check unavailable)", BuiltInPackUpdates.Update.unavailable().suffix("4010"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checksOnlyBuiltInPacksAndKeepsSuccessfulResultsOnPartialFailure() throws Exception {
|
||||
AtomicInteger requests = new AtomicInteger();
|
||||
HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
|
||||
server.createContext("/", exchange -> {
|
||||
requests.incrementAndGet();
|
||||
boolean overworld = exchange.getRequestURI().getPath().equals("/overworld");
|
||||
byte[] body = (overworld ? release("v4010", "overworld.zip", false) : "rate limited")
|
||||
.getBytes(StandardCharsets.UTF_8);
|
||||
exchange.sendResponseHeaders(overworld ? 200 : 429, body.length);
|
||||
exchange.getResponseBody().write(body);
|
||||
exchange.close();
|
||||
});
|
||||
server.start();
|
||||
try (HttpClient client = HttpClient.newHttpClient()) {
|
||||
Map<String, BuiltInPackUpdates.Update> updates = BuiltInPackUpdates.check(
|
||||
List.of("overworld", "underworld", "custom", "overworld"), client,
|
||||
pack -> URI.create("http://127.0.0.1:" + server.getAddress().getPort() + "/" + pack))
|
||||
.get(10, TimeUnit.SECONDS);
|
||||
assertEquals(2, requests.get());
|
||||
assertEquals("4010", updates.get("overworld").version());
|
||||
assertEquals(BuiltInPackUpdates.Update.unavailable(), updates.get("underworld"));
|
||||
assertFalse(updates.containsKey("custom"));
|
||||
} finally {
|
||||
server.stop(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rejectsMalformedPrereleaseAndMissingArchiveMetadata() throws Exception {
|
||||
for (String body : List.of("invalid", "{}", release("4010", "source.zip", false),
|
||||
release("4010", "overworld.zip", true), release("not-a-version", "overworld.zip", false))) {
|
||||
assertEquals(BuiltInPackUpdates.Update.unavailable(), checkResponse(body));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void acceptsStableReleaseWithMatchingArchive() throws Exception {
|
||||
assertTrue(checkResponse(release("V+4010", "overworld.zip", false)).newerThan("4009"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void boundsReleaseMetadataSize() throws Exception {
|
||||
assertEquals(BuiltInPackUpdates.Update.unavailable(), checkResponse(" ".repeat(1024 * 1024 + 1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timesOutWithoutResponseHeaders() throws Exception {
|
||||
assertStalledResponseUnavailable(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timesOutWhenResponseBodyStalls() throws Exception {
|
||||
assertStalledResponseUnavailable(true);
|
||||
}
|
||||
|
||||
private void assertStalledResponseUnavailable(boolean sendHeaders) throws Exception {
|
||||
HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
|
||||
server.createContext("/", exchange -> {
|
||||
if (sendHeaders) {
|
||||
exchange.sendResponseHeaders(200, 100);
|
||||
exchange.getResponseBody().write('{');
|
||||
exchange.getResponseBody().flush();
|
||||
}
|
||||
});
|
||||
server.start();
|
||||
try (HttpClient client = HttpClient.newHttpClient()) {
|
||||
assertEquals(BuiltInPackUpdates.Update.unavailable(), BuiltInPackUpdates.check(
|
||||
List.of("overworld"), client,
|
||||
pack -> URI.create("http://127.0.0.1:" + server.getAddress().getPort() + "/"))
|
||||
.get(8, TimeUnit.SECONDS).get("overworld"));
|
||||
} finally {
|
||||
server.stop(0);
|
||||
}
|
||||
}
|
||||
|
||||
private BuiltInPackUpdates.Update checkResponse(String response) throws Exception {
|
||||
HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
|
||||
server.createContext("/", exchange -> {
|
||||
byte[] body = response.getBytes(StandardCharsets.UTF_8);
|
||||
exchange.sendResponseHeaders(200, body.length);
|
||||
exchange.getResponseBody().write(body);
|
||||
exchange.close();
|
||||
});
|
||||
server.start();
|
||||
try (HttpClient client = HttpClient.newHttpClient()) {
|
||||
return BuiltInPackUpdates.check(List.of("overworld"), client,
|
||||
pack -> URI.create("http://127.0.0.1:" + server.getAddress().getPort() + "/"))
|
||||
.get(10, TimeUnit.SECONDS).get("overworld");
|
||||
} finally {
|
||||
server.stop(0);
|
||||
}
|
||||
}
|
||||
|
||||
private static String release(String version, String asset, boolean prerelease) {
|
||||
return "{\"tag_name\":\"" + version + "\",\"draft\":false,\"prerelease\":" + prerelease
|
||||
+ ",\"assets\":[{\"name\":\"" + asset + "\"}]}";
|
||||
}
|
||||
}
|
||||
@@ -606,6 +606,13 @@ public class PackDownloaderTest {
|
||||
assertTrue(result.restartRequired());
|
||||
assertEquals("new", Files.readString(target.toPath().resolve("state.txt"), StandardCharsets.UTF_8));
|
||||
assertFalse(Files.exists(target.toPath().resolve("old-only.txt")));
|
||||
File[] backups = new File(packsFolder, ".backups").listFiles();
|
||||
assertNotNull(backups);
|
||||
assertEquals(1, backups.length);
|
||||
assertEquals("old", Files.readString(backups[0].toPath().resolve("state.txt")));
|
||||
assertEquals("old", Files.readString(backups[0].toPath().resolve("old-only.txt")));
|
||||
assertTrue(feedback.stream().anyMatch(message -> message.contains(backups[0].getPath())));
|
||||
assertFalse(PackDirectoryResolver.isVisiblePackDirectory(new File(packsFolder, ".backups")));
|
||||
assertTransactionStateClean(packsFolder);
|
||||
assertEquals(0, PackDownloader.downloadLockCount());
|
||||
}
|
||||
|
||||
@@ -7,7 +7,12 @@ import art.arcane.iris.pack.BrokenPackException;
|
||||
import art.arcane.iris.pack.PackValidationRegistry;
|
||||
import art.arcane.iris.pack.PackValidationResult;
|
||||
import art.arcane.iris.generation.runtime.PreservationRegistry;
|
||||
import art.arcane.iris.generation.terrain.IrisDimension;
|
||||
import art.arcane.iris.platform.bukkit.plugin.VolmitSender;
|
||||
import art.arcane.iris.spi.IrisLogging;
|
||||
import art.arcane.iris.spi.IrisServices;
|
||||
import art.arcane.iris.world.history.GenerationHistory;
|
||||
import art.arcane.iris.world.task.J;
|
||||
import art.arcane.iris.testsupport.PlatformLeakGuard;
|
||||
import org.junit.Assume;
|
||||
import org.junit.After;
|
||||
@@ -35,6 +40,7 @@ import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.CALLS_REAL_METHODS;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class StudioSVCWorldPackPublishTest {
|
||||
@ClassRule
|
||||
@@ -54,6 +60,34 @@ public class StudioSVCWorldPackPublishTest {
|
||||
IrisServices.remove(PreservationRegistry.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void brokenSourceIsRejectedBeforeOpeningOrChangingWorldHistory() throws Exception {
|
||||
Path source = temporaryFolder.newFolder("invalid-generation-update").toPath();
|
||||
writeValidPack(source);
|
||||
Files.writeString(source.resolve("biomes/biome.json"), "{");
|
||||
Path world = temporaryFolder.newFolder("untouched-generation-world").toPath();
|
||||
Files.writeString(world.resolve("level.dat"), "world-state");
|
||||
IrisData sourceData = mock(IrisData.class);
|
||||
when(sourceData.getDataFolder()).thenReturn(source.toFile());
|
||||
IrisDimension dimension = new IrisDimension();
|
||||
dimension.setLoadKey("main");
|
||||
dimension.setLoader(sourceData);
|
||||
VolmitSender sender = mock(VolmitSender.class);
|
||||
|
||||
try (MockedStatic<J> scheduler = mockStatic(J.class);
|
||||
MockedStatic<IrisLogging> logging = mockStatic(IrisLogging.class);
|
||||
MockedStatic<GenerationHistory> history = mockStatic(GenerationHistory.class);
|
||||
MockedStatic<ServerConfigurator> configurator = mockStatic(ServerConfigurator.class)) {
|
||||
assertNull(new StudioSVC().replaceIntoWorld(sender, dimension, world.toFile(), 42L));
|
||||
|
||||
history.verifyNoInteractions();
|
||||
configurator.verifyNoInteractions();
|
||||
assertFalse(Files.exists(world.resolve("iris")));
|
||||
assertEquals("world-state", Files.readString(world.resolve("level.dat")));
|
||||
assertTrue(PackValidationRegistry.isBroken(source));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void copiesToStageAndPublishesTheCompletePack() throws IOException {
|
||||
Path root = temporaryFolder.newFolder("world").toPath();
|
||||
|
||||
Reference in new issue
Block a user