feat(rewards): add completed, background and banner effect items
Build, Test & Push Core Service / quality-check (push) Successful in 1m4s
Build, Test & Push Core Service / docker (push) Successful in 1m31s

This commit is contained in:
lobo committed 2026-10-01 15:09:25 +02:00
1 parent 349fa40272
commit a12ed20705
7 files changed
+42 -8

No files matched your search

@@ -1,5 +1,5 @@
package core_service.modules.rewards.domain;
public enum LookSlot {
AVATAR_BORDER, BADGE, ACCENT, BANNER_FRAME
AVATAR_BORDER, BADGE, ACCENT, BANNER_FRAME, COMPLETED_EFFECT, PAGE_BACKGROUND, BANNER_BACKGROUND
}
@@ -6,6 +6,8 @@ import java.util.stream.Collectors;
/**
* The whitelist of looks. The frontend maps a code to its visuals; nothing here is ever uploaded CSS.
* {@code FX_} effects show on the cover of a COMPLETED game and on its details page, {@code BG_} on the page
* background, {@code BN_} on the dashboard and profile banner.
*/
public enum ShopItem {
BORDER_GOLD(LookSlot.AVATAR_BORDER, 150),
@@ -15,7 +17,27 @@ public enum ShopItem {
ACCENT_GOLD(LookSlot.ACCENT, 200),
ACCENT_TEAL(LookSlot.ACCENT, 200),
FRAME_GOLD(LookSlot.BANNER_FRAME, 150),
FRAME_NEON(LookSlot.BANNER_FRAME, 200);
FRAME_NEON(LookSlot.BANNER_FRAME, 200),
FX_GOLD_SHINE(LookSlot.COMPLETED_EFFECT, 300),
FX_NEON_PULSE(LookSlot.COMPLETED_EFFECT, 300),
FX_HOLO(LookSlot.COMPLETED_EFFECT, 350),
FX_EMBER(LookSlot.COMPLETED_EFFECT, 300),
BG_TRIANGLES(LookSlot.PAGE_BACKGROUND, 400),
BG_STARFIELD(LookSlot.PAGE_BACKGROUND, 400),
BG_PIXELS(LookSlot.PAGE_BACKGROUND, 400),
BG_AURORA(LookSlot.PAGE_BACKGROUND, 450),
BG_MESH(LookSlot.PAGE_BACKGROUND, 450),
BG_GRID(LookSlot.PAGE_BACKGROUND, 450),
BG_WAVES(LookSlot.PAGE_BACKGROUND, 400),
BG_BOKEH(LookSlot.PAGE_BACKGROUND, 400),
BN_TRIANGLES(LookSlot.BANNER_BACKGROUND, 300),
BN_STARFIELD(LookSlot.BANNER_BACKGROUND, 300),
BN_PIXELS(LookSlot.BANNER_BACKGROUND, 300),
BN_AURORA(LookSlot.BANNER_BACKGROUND, 300),
BN_MESH(LookSlot.BANNER_BACKGROUND, 300),
BN_GRID(LookSlot.BANNER_BACKGROUND, 300),
BN_WAVES(LookSlot.BANNER_BACKGROUND, 300),
BN_BOKEH(LookSlot.BANNER_BACKGROUND, 300);
private final LookSlot slot;
private final int price;
@@ -0,0 +1,2 @@
-- BANNER_BACKGROUND is 17 characters.
ALTER TABLE reward_equipped ALTER COLUMN slot TYPE VARCHAR(32);
@@ -1,6 +1,7 @@
package core_service.modules.rewards.api;
import core_service.configuration.security.ClientHeaderFilter;
import core_service.modules.rewards.domain.ShopItem;
import core_service.modules.rewards.service.RewardService;
import core_service.shared.security.Role;
import core_service.shared.test.AbstractIntegrationTest;
@@ -81,7 +82,7 @@ class RewardControllerTest extends AbstractIntegrationTest {
.andExpect(jsonPath("$.balance").value(30))
.andExpect(jsonPath("$.history[0].kind").value("FINISHED"))
.andExpect(jsonPath("$.history[0].gameName").value("Game " + GAME))
.andExpect(jsonPath("$.catalog.length()").value(8))
.andExpect(jsonPath("$.catalog.length()").value(ShopItem.values().length))
.andExpect(jsonPath("$.owned").isEmpty());
setStatus(GAME, "BACKLOG");
@@ -212,7 +213,7 @@ class RewardControllerTest extends AbstractIntegrationTest {
@Test
void admin_OwnsEveryItem_NeverBuysAndFallsBackToDefaultAsAUser() throws Exception {
mockMvc.perform(asAdmin(get("/api/rewards/wallet")))
.andExpect(jsonPath("$.owned.length()").value(8));
.andExpect(jsonPath("$.owned.length()").value(ShopItem.values().length));
mockMvc.perform(asAdmin(post("/api/rewards/purchases")).header(ClientHeaderFilter.CLIENT_HEADER, "test")
.contentType(MediaType.APPLICATION_JSON).content("{\"item\":\"FRAME_GOLD\"}"))
.andExpect(status().isConflict()).andExpect(jsonPath("$.code").value("ALREADY_OWNED"));
@@ -25,9 +25,10 @@ class LooksTest extends AbstractUnitTest {
@Test
void wearable_DropsLooksThatAreNotOwnedOrNotInTheCatalog() {
Map<LookSlot, String> equipped = Map.of(LookSlot.AVATAR_BORDER, "BORDER_GOLD", LookSlot.BADGE, "BADGE_PILE_SLAYER",
LookSlot.ACCENT, "ACCENT_NEON_PINK");
LookSlot.ACCENT, "ACCENT_NEON_PINK", LookSlot.BANNER_BACKGROUND, "BN_GRID");
assertThat(Looks.wearable(equipped, Set.of("BORDER_GOLD", "ACCENT_NEON_PINK")))
.containsOnly(Map.entry(LookSlot.AVATAR_BORDER, "BORDER_GOLD"));
assertThat(Looks.wearable(equipped, Set.of("BORDER_GOLD", "ACCENT_NEON_PINK", "BN_GRID")))
.containsOnly(Map.entry(LookSlot.AVATAR_BORDER, "BORDER_GOLD"),
Map.entry(LookSlot.BANNER_BACKGROUND, "BN_GRID"));
}
}
@@ -99,4 +99,11 @@ class RewardLedgerRepositoryTest extends AbstractPersistenceTest {
assertThat(repository.equipped(USER)).containsOnlyKeys(LookSlot.ACCENT);
}
@Test
void equip_TheLongestSlotNameFits() {
repository.equip(USER, LookSlot.BANNER_BACKGROUND, "BN_GRID");
assertThat(repository.equipped(USER)).containsOnly(Map.entry(LookSlot.BANNER_BACKGROUND, "BN_GRID"));
}
}