You've already forked Arcturus-Morningstar-Extended
mirror of
https://github.com/duckietm/Arcturus-Morningstar-Extended.git
synced 2026-06-20 07:26:18 +00:00
fix(catalog): harden marketplace and inventory mutations
This commit is contained in:
+55
@@ -0,0 +1,55 @@
|
||||
package com.eu.habbo.habbohotel.catalog.marketplace;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class MarketPlaceListingPriceContractTest {
|
||||
|
||||
@Test
|
||||
void listingPriceMustBePositiveAndBounded() {
|
||||
assertFalse(MarketPlace.isValidListingPrice(Integer.MIN_VALUE));
|
||||
assertFalse(MarketPlace.isValidListingPrice(-1));
|
||||
assertFalse(MarketPlace.isValidListingPrice(0));
|
||||
assertTrue(MarketPlace.isValidListingPrice(1));
|
||||
assertTrue(MarketPlace.isValidListingPrice(MarketPlace.MAXIMUM_LISTING_PRICE));
|
||||
assertFalse(MarketPlace.isValidListingPrice(MarketPlace.MAXIMUM_LISTING_PRICE + 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void commissionCalculationDoesNotOverflow() {
|
||||
assertEquals(101, MarketPlace.calculateCommision(100));
|
||||
assertEquals(Integer.MAX_VALUE, MarketPlace.calculateCommision(Integer.MAX_VALUE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void marketplaceCoreRejectsInvalidListingPrices() throws Exception {
|
||||
String source = Files.readString(Path.of("src/main/java/com/eu/habbo/habbohotel/catalog/marketplace/MarketPlace.java"));
|
||||
int sellItemStart = source.indexOf("public static boolean sellItem");
|
||||
int eventCreation = source.indexOf("new MarketPlaceItemOfferedEvent", sellItemStart);
|
||||
int validation = source.indexOf("!isValidListingPrice(price)", sellItemStart);
|
||||
|
||||
assertTrue(sellItemStart > -1, "MarketPlace.sellItem must exist");
|
||||
assertTrue(validation > sellItemStart, "Marketplace listings must validate price before persisting");
|
||||
assertTrue(validation < eventCreation, "Invalid prices must be rejected before plugin events and DB writes");
|
||||
}
|
||||
|
||||
@Test
|
||||
void marketplaceBuyIgnoresInvalidPersistedPrices() throws Exception {
|
||||
String source = Files.readString(Path.of("src/main/java/com/eu/habbo/habbohotel/catalog/marketplace/MarketPlace.java"));
|
||||
int buyItemStart = source.indexOf("public static void buyItem");
|
||||
int rawPrice = source.indexOf("int rawPrice = set.getInt(\"price\")", buyItemStart);
|
||||
int validation = source.indexOf("!isValidListingPrice(rawPrice)", rawPrice);
|
||||
int commission = source.indexOf("MarketPlace.calculateCommision(rawPrice)", rawPrice);
|
||||
|
||||
assertTrue(buyItemStart > -1, "MarketPlace.buyItem must exist");
|
||||
assertTrue(rawPrice > buyItemStart, "Marketplace buy path should read the persisted raw price");
|
||||
assertTrue(validation > rawPrice, "Persisted marketplace prices must be validated before charging buyers");
|
||||
assertTrue(validation < commission, "Invalid persisted prices must not reach commission calculation");
|
||||
}
|
||||
}
|
||||
+25
@@ -13,6 +13,10 @@ class CatalogAdminOfferMutationContractTest {
|
||||
"src/main/java/com/eu/habbo/messages/incoming/catalog/catalogadmin/CatalogAdminCreateOfferEvent.java");
|
||||
private static final Path SAVE_SOURCE = Path.of(
|
||||
"src/main/java/com/eu/habbo/messages/incoming/catalog/catalogadmin/CatalogAdminSaveOfferEvent.java");
|
||||
private static final Path DELETE_SOURCE = Path.of(
|
||||
"src/main/java/com/eu/habbo/messages/incoming/catalog/catalogadmin/CatalogAdminDeleteOfferEvent.java");
|
||||
private static final Path MOVE_SOURCE = Path.of(
|
||||
"src/main/java/com/eu/habbo/messages/incoming/catalog/catalogadmin/CatalogAdminMoveOfferEvent.java");
|
||||
|
||||
@Test
|
||||
void createAndSaveValidatePayloadAndTargetPageBeforeWriting() throws IOException {
|
||||
@@ -40,4 +44,25 @@ class CatalogAdminOfferMutationContractTest {
|
||||
assertTrue(save.contains("statement.executeUpdate() == 0"));
|
||||
assertTrue(save.contains("Offer not found: "));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteOfferRejectsInvalidIdsAndReportsMissingRows() throws IOException {
|
||||
String delete = Files.readString(DELETE_SOURCE);
|
||||
|
||||
assertTrue(delete.contains("offerId <= 0"));
|
||||
assertTrue(delete.contains("Invalid offer id"));
|
||||
assertTrue(delete.contains("statement.executeUpdate() == 0"));
|
||||
assertTrue(delete.contains("Offer not found: "));
|
||||
}
|
||||
|
||||
@Test
|
||||
void moveOfferRejectsInvalidIdsClampsOrderAndReportsMissingRows() throws IOException {
|
||||
String move = Files.readString(MOVE_SOURCE);
|
||||
|
||||
assertTrue(move.contains("offerId <= 0"));
|
||||
assertTrue(move.contains("Invalid offer id"));
|
||||
assertTrue(move.contains("if (orderNumber < 0) orderNumber = 0;"));
|
||||
assertTrue(move.contains("statement.executeUpdate() == 0"));
|
||||
assertTrue(move.contains("Offer not found: "));
|
||||
}
|
||||
}
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package com.eu.habbo.messages.incoming.inventory;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class RequestInventoryItemsDeleteContractTest {
|
||||
|
||||
private static String source() throws Exception {
|
||||
return Files.readString(Path.of("src/main/java/com/eu/habbo/messages/incoming/inventory/RequestInventoryItemsDelete.java"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsInvalidDeleteAmountsBeforeMutatingInventory() throws Exception {
|
||||
String source = source();
|
||||
int amountRead = source.indexOf("int amount = this.packet.readInt()");
|
||||
int amountGuard = source.indexOf("amount <= 0 || amount > MAX_DELETE_AMOUNT", amountRead);
|
||||
int firstInventoryLookup = source.indexOf("getItemsComponent().getHabboItem", amountRead);
|
||||
|
||||
assertTrue(amountRead > -1, "Delete handler must read the client-provided amount");
|
||||
assertTrue(amountGuard > amountRead, "Delete handler must reject non-positive and oversized amounts");
|
||||
assertTrue(amountGuard < firstInventoryLookup,
|
||||
"Amount validation must happen before inventory lookup or mutation work starts");
|
||||
}
|
||||
|
||||
@Test
|
||||
void doesNotUseAbsoluteValueForClientProvidedAmount() throws Exception {
|
||||
String source = source();
|
||||
|
||||
assertFalse(source.contains("Math.abs(amount)"),
|
||||
"Delete amount must not use Math.abs because Integer.MIN_VALUE remains negative");
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteLoopUsesValidatedAmountDirectly() throws Exception {
|
||||
String source = source();
|
||||
|
||||
assertTrue(source.contains("for (int i = 0; i < amount; i++)"),
|
||||
"Delete loop should use the already validated positive amount");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user