Files
purpur-fantasy-stick/src/nativeTest/java/games/dmg/fantasystick/AdventureParticipationModeNativeTest.java
T
dmg 618a6a2776
Release / release (push) Failing after 6m11s
feat(dungeons): persist kill and trophy rewards
2026-09-23 18:57:54 -04:00

246 lines
17 KiB
Java

package games.dmg.fantasystick;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
import games.dmg.fantasystick.adventure.AdventureQuestController;
import games.dmg.fantasystick.adventure.AdventureQuestDirectory;
import games.dmg.fantasystick.adventure.AdventureQuestState;
import games.dmg.fantasystick.dungeon.DungeonDirectory;
import java.nio.file.Path;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
final class AdventureParticipationModeNativeTest {
@TempDir Path data;
@BeforeAll static void bootstrap() throws Exception { NativeRuntime.bootstrap(); }
@Test void registeredSiteAndDungeonCommandsPersistReportUpdateAndCompleteModes() throws Exception {
java.nio.file.Files.writeString(data.resolve("config.yml"), "adventure-daily-site-count: 0\n");
try (var f = new FantasyStickLifecycleNativeTest.Fixture(data)) {
f.plugin.setEnabled(true);
var admin = new FantasyStickLifecycleNativeTest.Actor(data);
f.players.put(admin.core.id, admin.player);
doReturn(true).when(admin.player).hasPermission("fantasystick.admin");
when(admin.world.getUID()).thenReturn(UUID.randomUUID());
assertEquals(List.of("--mode"), f.command.tabComplete(admin.player, "fantasystick",
new String[]{"adventure-site", "place", "camp", "--"}));
assertEquals(List.of("individual-only"), f.command.tabComplete(admin.player, "fantasystick",
new String[]{"adventure-site", "place", "camp", "--mode", "i"}));
var adventures = (AdventureQuestDirectory) FantasyStickLifecycleNativeTest.Fixture.field(f.plugin,
"adventureQuests");
f.command.execute(admin.player, "fantasystick", new String[]{"adventure-site", "place", "camp",
"--mode", "individual-only", "10", "Solo Camp"});
await(f, () -> !adventures.snapshot().sites().isEmpty());
var site = adventures.snapshot().sites().getFirst();
assertEquals(AdventureQuestState.ParticipationMode.INDIVIDUAL_ONLY, site.participationMode());
assertTrue(admin.messages.getLast().contains("mode=individual-only"));
f.command.execute(admin.player, "fantasystick", new String[]{"adventure-site", "list"});
assertTrue(admin.messages.getLast().contains("mode=individual-only"));
assertEquals(List.of(site.id().toString()), f.command.tabComplete(admin.player, "fantasystick",
new String[]{"adventure-site", "mode", ""}));
assertEquals(List.of("party-available", "party-required"), f.command.tabComplete(admin.player,
"fantasystick", new String[]{"adventure-site", "mode", site.id().toString(), "party-"}));
f.command.execute(admin.player, "fantasystick", new String[]{"adventure-site", "mode",
site.id().toString()});
assertTrue(admin.messages.getLast().contains("individual-only"));
f.command.execute(admin.player, "fantasystick", new String[]{"adventure-site", "mode",
site.id().toString(), "party-required"});
drain(f);
assertEquals(AdventureQuestState.ParticipationMode.PARTY_REQUIRED,
adventures.findSite(site.id()).orElseThrow().participationMode());
int count = adventures.snapshot().sites().size();
f.command.execute(admin.player, "fantasystick", new String[]{"adventure-site", "place", "camp",
"--mode", "invalid", "Broken"});
assertEquals(count, adventures.snapshot().sites().size());
assertEquals(List.of("--mode"), f.command.tabComplete(admin.player, "fantasystick",
new String[]{"dungeon", "create", "--"}));
var dungeons = (DungeonDirectory) FantasyStickLifecycleNativeTest.Fixture.field(f.plugin, "dungeons");
f.command.execute(admin.player, "fantasystick", new String[]{"dungeon", "create", "--mode",
"party-required", "Mode Vault"});
await(f, () -> !dungeons.snapshot().dungeons().isEmpty());
var dungeon = dungeons.snapshot().dungeons().getFirst();
assertEquals(AdventureQuestState.ParticipationMode.PARTY_REQUIRED, dungeon.participationMode());
assertEquals(List.of(dungeon.id().toString()), f.command.tabComplete(admin.player, "fantasystick",
new String[]{"dungeon", "mode", ""}));
f.command.execute(admin.player, "fantasystick", new String[]{"dungeon", "mode",
dungeon.id().toString(), "party-available"});
drain(f);
assertEquals(AdventureQuestState.ParticipationMode.PARTY_AVAILABLE,
dungeons.find(dungeon.id()).orElseThrow().participationMode());
f.command.execute(admin.player, "fantasystick", new String[]{"dungeon", "mode",
dungeon.id().toString(), "party-required"}); drain(f);
var room = dungeons.addRadiusRoom(dungeon.id(), "Boss", 0, 64, 0, 2, 3)
.get(5, TimeUnit.SECONDS);
dungeons.setBoss(dungeon.id(), room.id(), org.bukkit.entity.EntityType.HUSK, "Keeper",
0, 65, 0, 0, 0).get(5, TimeUnit.SECONDS);
dungeons.setCompletionReward(dungeon.id(), java.math.BigInteger.ZERO, java.math.BigInteger.ZERO)
.get(5, TimeUnit.SECONDS);
f.command.execute(admin.player, "fantasystick", new String[]{"dungeon", "enable",
dungeon.id().toString()}); drain(f);
f.command.execute(admin.player, "fantasystick", new String[]{"dungeon", "open",
dungeon.id().toString()}); drain(f);
assertEquals(AdventureQuestState.ParticipationMode.PARTY_REQUIRED,
adventures.findSite(dungeon.id()).orElseThrow().participationMode());
}
}
@Test void questGiverLabelsAndExplicitlyAcceptsPersonalOrPartyForm() throws Exception {
java.nio.file.Files.writeString(data.resolve("config.yml"), "adventure-daily-site-count: 3\n");
try (var f = new FantasyStickLifecycleNativeTest.Fixture(data)) {
f.plugin.setEnabled(true);
var leader = new FantasyStickLifecycleNativeTest.Actor(data);
var member = new FantasyStickLifecycleNativeTest.Actor(data);
f.players.put(leader.core.id, leader.player); f.players.put(member.core.id, member.player);
UUID world = UUID.randomUUID(); when(leader.world.getUID()).thenReturn(world);
member.core.location.setWorld(leader.world);
f.activate(leader); f.activate(member);
var parties = PartyLifecycleNativeTest.parties(f);
var party = parties.create(leader.core.id).orElseThrow();
assertTrue(parties.accept(member.core.id,
parties.invite(leader.core.id, party.id(), member.core.id).orElseThrow().id()));
var adventures = (AdventureQuestDirectory) FantasyStickLifecycleNativeTest.Fixture.field(f.plugin,
"adventureQuests");
var personal = adventures.placeSite(AdventureQuestState.SiteType.CAMP, "Flexible Camp", world,
0, 64, 0, null, AdventureQuestState.ParticipationMode.PARTY_AVAILABLE).get(5, TimeUnit.SECONDS);
var required = adventures.placeSite(AdventureQuestState.SiteType.CHAMPION, "Party Summit", world,
20, 64, 0, null, AdventureQuestState.ParticipationMode.PARTY_REQUIRED).get(5, TimeUnit.SECONDS);
var individual = adventures.placeSite(AdventureQuestState.SiteType.CAMP, "Solo Camp", world,
40, 64, 0, null, AdventureQuestState.ParticipationMode.INDIVIDUAL_ONLY).get(5, TimeUnit.SECONDS);
adventures.ensureToday().get(5, TimeUnit.SECONDS);
var controller = (AdventureQuestController) FantasyStickLifecycleNativeTest.Fixture.field(f.plugin,
"adventureQuestController");
controller.open(member.player); drain(f);
var memberMenu = member.inventories.getLast();
var personalItem = java.util.stream.IntStream.range(0, memberMenu.getSize())
.mapToObj(memberMenu::getItem).filter(java.util.Objects::nonNull)
.filter(item -> PlainTextComponentSerializer.plainText().serialize(item.getItemMeta().displayName())
.equals("Flexible Camp")).findFirst().orElseThrow();
var lore = personalItem.getItemMeta().lore().stream()
.map(PlainTextComponentSerializer.plainText()::serialize).toList();
assertTrue(lore.contains("Mode: party-available"));
assertTrue(lore.stream().anyMatch(line -> line.contains("Left click: Accept individually")));
var personalClick = mock(InventoryClickEvent.class);
when(personalClick.getWhoClicked()).thenReturn(member.player);
when(personalClick.getInventory()).thenReturn(memberMenu);
when(personalClick.getCurrentItem()).thenReturn(personalItem);
controller.click(personalClick); drain(f);
var personalQuest = adventures.activeFor(member.core.record.id(), party.id()).orElseThrow();
assertEquals(AdventureQuestState.UnitType.SOLO, personalQuest.unitType());
assertEquals(Set.of(member.core.record.id()), personalQuest.snapshot());
assertTrue(adventures.abandon(personalQuest.id(), personalQuest.unit()).get(5, TimeUnit.SECONDS));
controller.open(member.player); drain(f);
var memberIndividualMenu = member.inventories.getLast();
var individualItem = offer(memberIndividualMenu, "Solo Camp");
click(controller, member, memberIndividualMenu, individualItem, false); drain(f);
var memberIndividual = adventures.activeFor(member.core.record.id(), party.id()).orElseThrow();
assertEquals(individual.id(), memberIndividual.site());
assertEquals(AdventureQuestState.UnitType.SOLO, memberIndividual.unitType());
assertTrue(adventures.abandon(memberIndividual.id(), memberIndividual.unit()).get(5, TimeUnit.SECONDS));
click(controller, member, memberIndividualMenu, individualItem, true);
assertTrue(member.messages.getLast().contains("individual-only"));
var memberRequiredItem = offer(memberIndividualMenu, "Party Summit");
click(controller, member, memberIndividualMenu, memberRequiredItem, false);
assertTrue(member.messages.getLast().contains("party-required"));
click(controller, member, memberIndividualMenu, memberRequiredItem, true);
assertTrue(member.messages.getLast().contains("Only the active party leader"));
var partyless = new FantasyStickLifecycleNativeTest.Actor(data);
partyless.core.location.setWorld(leader.world); f.players.put(partyless.core.id, partyless.player);
f.activate(partyless); controller.open(partyless.player); drain(f);
var partylessMenu = partyless.inventories.getLast();
click(controller, partyless, partylessMenu, offer(partylessMenu, "Solo Camp"), false); drain(f);
var partylessQuest = adventures.activeFor(partyless.core.record.id(), null).orElseThrow();
assertEquals(AdventureQuestState.UnitType.SOLO, partylessQuest.unitType());
assertTrue(adventures.abandon(partylessQuest.id(), partylessQuest.unit()).get(5, TimeUnit.SECONDS));
var partylessRequired = offer(partylessMenu, "Party Summit");
click(controller, partyless, partylessMenu, partylessRequired, false);
assertTrue(partyless.messages.getLast().contains("party-required"));
click(controller, partyless, partylessMenu, partylessRequired, true);
assertTrue(partyless.messages.getLast().contains("Only the active party leader"));
controller.open(leader.player); drain(f);
var leaderIndividualMenu = leader.inventories.getLast();
var leaderIndividualItem = offer(leaderIndividualMenu, "Solo Camp");
click(controller, leader, leaderIndividualMenu, leaderIndividualItem, false); drain(f);
var leaderIndividual = adventures.activeFor(leader.core.record.id(), party.id()).orElseThrow();
assertEquals(AdventureQuestState.UnitType.SOLO, leaderIndividual.unitType());
assertTrue(adventures.abandon(leaderIndividual.id(), leaderIndividual.unit()).get(5, TimeUnit.SECONDS));
click(controller, leader, leaderIndividualMenu, leaderIndividualItem, true);
assertTrue(leader.messages.getLast().contains("individual-only"));
controller.open(leader.player); drain(f);
var leaderMenu = leader.inventories.getLast();
var requiredItem = java.util.stream.IntStream.range(0, leaderMenu.getSize())
.mapToObj(leaderMenu::getItem).filter(java.util.Objects::nonNull)
.filter(item -> PlainTextComponentSerializer.plainText().serialize(item.getItemMeta().displayName())
.equals("Party Summit")).findFirst().orElseThrow();
var requiredLore = requiredItem.getItemMeta().lore().stream()
.map(PlainTextComponentSerializer.plainText()::serialize).toList();
assertTrue(requiredLore.contains("Mode: party-required"));
assertTrue(requiredLore.stream().anyMatch(line -> line.contains("Right click: Accept for party")));
var partyClick = mock(InventoryClickEvent.class);
when(partyClick.getWhoClicked()).thenReturn(leader.player);
when(partyClick.getInventory()).thenReturn(leaderMenu);
when(partyClick.getCurrentItem()).thenReturn(requiredItem);
when(partyClick.isRightClick()).thenReturn(true);
controller.click(partyClick);
assertTrue(parties.disband(leader.core.id, party.id()), "party changes before acceptance publication");
await(f, () -> leader.messages.stream().anyMatch(message -> message.contains("character or party changed")));
assertTrue(adventures.active().isEmpty());
party = parties.create(leader.core.id).orElseThrow();
assertTrue(parties.accept(member.core.id,
parties.invite(leader.core.id, party.id(), member.core.id).orElseThrow().id()));
controller.click(partyClick);
UUID acceptedParty = party.id();
await(f, () -> adventures.activeFor(leader.core.record.id(), acceptedParty).isPresent());
var partyQuest = adventures.activeFor(leader.core.record.id(), acceptedParty).orElseThrow();
assertEquals(AdventureQuestState.UnitType.PARTY, partyQuest.unitType());
assertEquals(Set.of(leader.core.record.id(), member.core.record.id()), partyQuest.snapshot());
assertEquals(AdventureQuestState.ParticipationMode.PARTY_REQUIRED,
partyQuest.participationMode());
}
}
private static org.bukkit.inventory.ItemStack offer(org.bukkit.inventory.Inventory menu, String name) {
return java.util.stream.IntStream.range(0, menu.getSize()).mapToObj(menu::getItem)
.filter(java.util.Objects::nonNull)
.filter(item -> PlainTextComponentSerializer.plainText().serialize(item.getItemMeta().displayName())
.equals(name)).findFirst().orElseThrow();
}
private static void click(AdventureQuestController controller, FantasyStickLifecycleNativeTest.Actor actor,
org.bukkit.inventory.Inventory menu, org.bukkit.inventory.ItemStack item,
boolean right) {
var event = mock(InventoryClickEvent.class);
when(event.getWhoClicked()).thenReturn(actor.player); when(event.getInventory()).thenReturn(menu);
when(event.getCurrentItem()).thenReturn(item); when(event.isRightClick()).thenReturn(right);
controller.click(event);
}
private static void drain(FantasyStickLifecycleNativeTest.Fixture f) throws Exception {
for (int attempt = 0; attempt < 20; attempt++) {
var task = f.tasks.poll(50, TimeUnit.MILLISECONDS);
if (task == null) break;
task.run();
}
}
private static void await(FantasyStickLifecycleNativeTest.Fixture f,
java.util.function.BooleanSupplier condition) throws Exception {
for (int attempt = 0; attempt < 50 && !condition.getAsBoolean(); attempt++) {
drain(f); Thread.sleep(5);
}
assertTrue(condition.getAsBoolean());
}
}