Public Access
feat(rewards): unlock items by progress and skins
Five progress tracks: games finished, completed, weekly finish streaks, pile-down months and play-more months. Settle books UNLOCK rows once, also retroactively, when a track reaches an item's target. 12 unlock-only items (incl. the Steam skin) and 3 skins to buy. The wallet returns progress; buying an unlock item answers NOT_FOR_SALE.
This commit is contained in:
1 parent
bb8341dfef
commit
8eab98df74
19 files changed
+633
-31
No files matched your search
@@ -318,6 +318,28 @@ class GameMergeUndoFlowTest extends AbstractIntegrationTest {
|
||||
assertThat(count("select count(*) from reward_ledger where ref = ?", String.valueOf(STEAM))).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
void unlockRowAndFinishCount_SurviveMergeAndUndo_AndSettleBooksNoSecondUnlock() throws Exception {
|
||||
manualRow(AUDITOR_ID, MANUAL, GameStatus.PLAYING);
|
||||
for (long ref = 7_000_000; ref < 7_000_008; ref++) {
|
||||
jdbc.update("insert into reward_ledger (user_id, kind, ref, points, event_at) values (?, 'FINISHED', ?, 30, now())",
|
||||
AUDITOR_ID, String.valueOf(ref));
|
||||
}
|
||||
jdbc.update("insert into reward_ledger (user_id, kind, ref, points, event_at) values "
|
||||
+ "(?, 'FINISHED_MANUAL', ?, 15, now()), (?, 'FINISHED', ?, 30, now()), "
|
||||
+ "(?, 'UNLOCK', 'BADGE_FINISH_10', 0, now())",
|
||||
AUDITOR_ID, String.valueOf(MANUAL), AUDITOR_ID, String.valueOf(STEAM), AUDITOR_ID);
|
||||
String finishes = "select count(*) from reward_ledger where user_id = ? and kind in ('FINISHED', 'FINISHED_MANUAL')";
|
||||
int before = count(finishes, AUDITOR_ID);
|
||||
|
||||
merge(MANUAL, STEAM).andExpect(status().isOk());
|
||||
undo(lastMerge()).andExpect(status().isNoContent());
|
||||
rewardService.settleAll();
|
||||
|
||||
assertThat(count(finishes, AUDITOR_ID)).isEqualTo(before);
|
||||
assertThat(count("select count(*) from reward_ledger where user_id = ? and kind = 'UNLOCK'", AUDITOR_ID)).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void movedRowTakenOverBySteam_StaysWhereItIs() throws Exception {
|
||||
manualRow(AUDITOR_ID, MANUAL, GameStatus.PLAYING);
|
||||
|
||||
@@ -16,6 +16,8 @@ import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.ResultActions;
|
||||
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
@@ -153,6 +155,18 @@ class RewardControllerTest extends AbstractIntegrationTest {
|
||||
userId, points);
|
||||
}
|
||||
|
||||
private void finishes(int count) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
jdbc.update("insert into reward_ledger (user_id, kind, ref, points, event_at) values (?, 'FINISHED', ?, 30, now())",
|
||||
USER, String.valueOf(5_000_000 + i));
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> unlocks(long userId) {
|
||||
return jdbc.queryForList("select ref from reward_ledger where user_id = ? and kind = 'UNLOCK' order by ref",
|
||||
String.class, userId);
|
||||
}
|
||||
|
||||
private static MockHttpServletRequestBuilder asAdmin(
|
||||
MockHttpServletRequestBuilder request) {
|
||||
return request.header("Authorization", "Bearer " + ContractTokens.loginToken(ADMIN, Role.PLATFORM_ADMIN));
|
||||
@@ -225,4 +239,96 @@ class RewardControllerTest extends AbstractIntegrationTest {
|
||||
.andExpect(jsonPath("$.owned").isEmpty())
|
||||
.andExpect(jsonPath("$.equipped").isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void wallet_TenFinishes_UnlockTheBadgeOnceAndKeepTheBalance() throws Exception {
|
||||
finishes(10);
|
||||
|
||||
mockMvc.perform(get("/api/rewards/wallet"))
|
||||
.andExpect(jsonPath("$.progress.GAMES_FINISHED").value(10))
|
||||
.andExpect(jsonPath("$.balance").value(300))
|
||||
.andExpect(jsonPath("$.owned[0]").value("BADGE_FINISH_10"));
|
||||
wallet();
|
||||
rewardService.settleAll();
|
||||
|
||||
assertThat(unlocks(USER)).containsExactly("BADGE_FINISH_10");
|
||||
assertThat(ledgerSum()).isEqualTo(300);
|
||||
}
|
||||
|
||||
@Test
|
||||
void wallet_ThirtyFinishes_UnlockEveryReachedLevelAtOnce() throws Exception {
|
||||
finishes(30);
|
||||
|
||||
wallet();
|
||||
|
||||
assertThat(unlocks(USER)).containsExactly("BADGE_FINISH_10", "BORDER_FINISH_25");
|
||||
}
|
||||
|
||||
@Test
|
||||
void wallet_NineFinishes_StayLockedWithTheirProgress_StarterGamesCountTwenty() throws Exception {
|
||||
finishes(9);
|
||||
|
||||
mockMvc.perform(get("/api/rewards/wallet"))
|
||||
.andExpect(jsonPath("$.progress.GAMES_FINISHED").value(9))
|
||||
.andExpect(jsonPath("$.owned").isEmpty());
|
||||
points(USER, 40);
|
||||
jdbc.update("delete from reward_ledger where user_id = ? and kind = 'FINISHED' and ref = '5000000'", USER);
|
||||
finishes(0);
|
||||
jdbc.update("insert into reward_ledger (user_id, kind, ref, points, event_at) values (?, 'FINISHED', '5000100', 30, now())", USER);
|
||||
|
||||
mockMvc.perform(get("/api/rewards/wallet")).andExpect(jsonPath("$.progress.GAMES_FINISHED").value(11));
|
||||
assertThat(unlocks(USER)).containsExactly("BADGE_FINISH_10");
|
||||
}
|
||||
|
||||
@Test
|
||||
void buy_UnlockOnlyItem_Answers409AndWritesNoRow() throws Exception {
|
||||
points(USER, 5000);
|
||||
|
||||
buy("SKIN_STEAM").andExpect(status().isConflict()).andExpect(jsonPath("$.code").value("NOT_FOR_SALE"));
|
||||
|
||||
assertThat(rows("BUY")).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
void equip_UnlockOnlySkin_NeedsTheUnlock() throws Exception {
|
||||
equip("SKIN", "SKIN_STEAM").andExpect(status().isConflict()).andExpect(jsonPath("$.code").value("NOT_OWNED"));
|
||||
|
||||
finishes(100);
|
||||
wallet();
|
||||
|
||||
equip("SKIN", "SKIN_STEAM").andExpect(status().isOk()).andExpect(jsonPath("$.equipped.SKIN").value("SKIN_STEAM"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void skin_CanBeBoughtEquippedAndTakenOff() throws Exception {
|
||||
points(USER, 600);
|
||||
|
||||
buy("SKIN_PASTEL").andExpect(status().isOk()).andExpect(jsonPath("$.balance").value(0));
|
||||
equip("SKIN", "SKIN_PASTEL").andExpect(jsonPath("$.equipped.SKIN").value("SKIN_PASTEL"));
|
||||
equip("SKIN", null).andExpect(jsonPath("$.equipped.SKIN").doesNotExist());
|
||||
}
|
||||
|
||||
@Test
|
||||
void admin_OwnsEverythingWithoutAnUnlockRow_AndAUserWearsNothingUnearned() throws Exception {
|
||||
mockMvc.perform(asAdmin(get("/api/rewards/wallet")))
|
||||
.andExpect(jsonPath("$.owned", org.hamcrest.Matchers.hasItem("SKIN_STEAM")));
|
||||
assertThat(unlocks(ADMIN)).isEmpty();
|
||||
mockMvc.perform(asAdmin(put("/api/rewards/equipped/SKIN")).header(ClientHeaderFilter.CLIENT_HEADER, "test")
|
||||
.contentType(MediaType.APPLICATION_JSON).content("{\"item\":\"SKIN_STEAM\"}")).andExpect(status().isOk());
|
||||
|
||||
mockMvc.perform(get("/api/rewards/wallet").header("Authorization", "Bearer " + ContractTokens.loginToken(ADMIN)))
|
||||
.andExpect(jsonPath("$.equipped").isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void wallet_BlockedUser_GetsNoUnlock() throws Exception {
|
||||
finishes(10);
|
||||
jdbc.update("insert into blocked_user (user_id) values (?)", USER);
|
||||
try {
|
||||
wallet();
|
||||
assertThat(unlocks(USER)).isEmpty();
|
||||
} finally {
|
||||
jdbc.update("delete from blocked_user where user_id = ?", USER);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,12 @@ class LooksTest extends AbstractUnitTest {
|
||||
assertThat(Looks.owned(Set.of(), Role.PLATFORM_ADMIN)).isEqualTo(ShopItem.codes());
|
||||
}
|
||||
|
||||
@Test
|
||||
void owned_AdminOwnsSkinsAndEveryUnlockItem() {
|
||||
assertThat(Looks.owned(Set.of(), Role.PLATFORM_ADMIN)).contains("SKIN_STEAM", "SKIN_PASTEL", "BN_EQUALIZER",
|
||||
"BADGE_FINISH_10");
|
||||
}
|
||||
|
||||
@Test
|
||||
void owned_UserOwnsOnlyWhatIsBought() {
|
||||
assertThat(Looks.owned(Set.of("BORDER_GOLD"), Role.USER)).containsExactly("BORDER_GOLD");
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package core_service.modules.rewards.domain;
|
||||
|
||||
import core_service.shared.test.AbstractUnitTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.YearMonth;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class UnlocksTest extends AbstractUnitTest {
|
||||
|
||||
@Test
|
||||
void longestWeekRun_AGapBreaksTheRun_DuplicatesCountOnce() {
|
||||
LocalDate monday = LocalDate.of(2026, 9, 7);
|
||||
|
||||
assertThat(Unlocks.longestWeekRun(List.of(monday, monday, monday.plusWeeks(1), monday.plusWeeks(3),
|
||||
monday.plusWeeks(4), monday.plusWeeks(5)))).isEqualTo(3);
|
||||
assertThat(Unlocks.longestWeekRun(List.of())).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
void longestWeekRun_RunsAcrossTheIsoYearEnd() {
|
||||
assertThat(Unlocks.longestWeekRun(List.of(LocalDate.of(2026, 12, 28), LocalDate.of(2027, 1, 4)))).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void longestMonthRun_RunsFromDecemberToJanuary() {
|
||||
assertThat(Unlocks.longestMonthRun(List.of(YearMonth.of(2026, 12), YearMonth.of(2027, 1), YearMonth.of(2027, 3))))
|
||||
.isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void playMoreMonths_NeedsStrictlyMoreThanANonEmptyMonthBefore_AndNeverTheRunningMonth() {
|
||||
YearMonth lastFull = YearMonth.of(2026, 9);
|
||||
Map<YearMonth, Long> minutes = Map.of(
|
||||
YearMonth.of(2026, 5), 100L,
|
||||
YearMonth.of(2026, 6), 100L,
|
||||
YearMonth.of(2026, 7), 101L,
|
||||
YearMonth.of(2026, 9), 500L,
|
||||
YearMonth.of(2026, 10), 900L);
|
||||
|
||||
assertThat(Unlocks.playMoreMonths(minutes, lastFull)).containsExactly(YearMonth.of(2026, 7));
|
||||
}
|
||||
|
||||
@Test
|
||||
void reached_ExactTargetReaches_OneBelowDoesNot_BuyableItemsNever() {
|
||||
assertThat(Unlocks.reached(Map.of(Track.GAMES_FINISHED, 10)))
|
||||
.containsExactly(ShopItem.BADGE_FINISH_10);
|
||||
assertThat(Unlocks.reached(Map.of(Track.GAMES_FINISHED, 9))).isEmpty();
|
||||
assertThat(Unlocks.reached(Map.of())).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void catalog_UnlockOnlyItemsAreFreeWithATargetAndEachLevelIsUnique() {
|
||||
List<ShopItem> unlock = Arrays.stream(ShopItem.values()).filter(ShopItem::unlockOnly).toList();
|
||||
|
||||
assertThat(unlock).hasSize(12).allSatisfy(item -> {
|
||||
assertThat(item.price()).isZero();
|
||||
assertThat(item.target()).isPositive();
|
||||
});
|
||||
assertThat(unlock.stream().map(item -> item.track() + ":" + item.target()).collect(Collectors.toSet()))
|
||||
.hasSameSizeAs(unlock);
|
||||
}
|
||||
}
|
||||
@@ -219,6 +219,20 @@ class RewardFactsRepositoryTest extends AbstractPersistenceTest {
|
||||
assertThat(repository.pileFirstAndLast(USER, month)).hasValueSatisfying(pile -> assertThat(pile).containsExactly(10, 8));
|
||||
}
|
||||
|
||||
@Test
|
||||
void minutesPerMonth_SumsPerMonthIncludingHiddenGames() {
|
||||
jdbc.update("insert into steam_game (appid, name) values (221, 'Portal') on conflict do nothing");
|
||||
jdbc.update("insert into user_game (user_id, appid, status, playtime_minutes, hidden_at) values (?, 221, 'BACKLOG', 0, now())", USER);
|
||||
game("BACKLOG", 0);
|
||||
playedOn("2026-08-31");
|
||||
playedOn("2026-09-01");
|
||||
playedOn("2026-09-30");
|
||||
jdbc.update("insert into user_game_playtime_day (user_id, day, appid, minutes) values (?, '2026-09-02', 221, 500)", USER);
|
||||
|
||||
assertThat(repository.minutesPerMonth(USER)).containsOnly(
|
||||
java.util.Map.entry(YearMonth.of(2026, 8), 30L), java.util.Map.entry(YearMonth.of(2026, 9), 560L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isActive_FalseForDeletedAndBlockedUsers() {
|
||||
game("BACKLOG", 0);
|
||||
|
||||
@@ -55,7 +55,37 @@ class RewardLedgerRepositoryTest extends AbstractPersistenceTest {
|
||||
|
||||
assertThat(repository.balance(USER)).isEqualTo(30);
|
||||
assertThat(repository.balance(99L)).isZero();
|
||||
assertThat(repository.bought(USER)).containsExactly("BORDER_GOLD");
|
||||
assertThat(repository.itemCodes(USER)).containsExactly("BORDER_GOLD");
|
||||
}
|
||||
|
||||
@Test
|
||||
void itemCodes_AreBuyAndUnlockRefsNotMonths() {
|
||||
repository.add(USER, new RewardEntry(RewardKind.BUY, "BORDER_GOLD", -50, JAN_1));
|
||||
repository.add(USER, new RewardEntry(RewardKind.UNLOCK, "BADGE_FINISH_10", 0, JAN_1));
|
||||
repository.add(USER, new RewardEntry(RewardKind.MONTH, "2026-09", 40, JAN_1));
|
||||
|
||||
assertThat(repository.itemCodes(USER)).containsExactlyInAnyOrder("BORDER_GOLD", "BADGE_FINISH_10");
|
||||
}
|
||||
|
||||
@Test
|
||||
void countKindsSumPointsAndRefs_ReadOneUsersRowsOfTheKinds() {
|
||||
repository.add(USER, new RewardEntry(RewardKind.FINISHED, "220", 30, JAN_1));
|
||||
repository.add(USER, new RewardEntry(RewardKind.FINISHED_MANUAL, "-1942", 15, JAN_1));
|
||||
repository.add(USER, new RewardEntry(RewardKind.STARTER, "STARTER", 60, JAN_1));
|
||||
repository.add(USER, new RewardEntry(RewardKind.MONTH, "2026-09", 40, JAN_1));
|
||||
repository.add(USER + 1, new RewardEntry(RewardKind.FINISHED, "220", 30, JAN_1));
|
||||
|
||||
assertThat(repository.countKinds(USER, RewardKind.FINISH_KINDS)).isEqualTo(2);
|
||||
assertThat(repository.sumPoints(USER, RewardKind.STARTER)).isEqualTo(60);
|
||||
assertThat(repository.sumPoints(99L, RewardKind.STARTER)).isZero();
|
||||
assertThat(repository.refs(USER, RewardKind.MONTH)).containsExactly("2026-09");
|
||||
}
|
||||
|
||||
@Test
|
||||
void equip_ASkinRoundTrips() {
|
||||
repository.equip(USER, LookSlot.SKIN, "SKIN_RETRO_CRT");
|
||||
|
||||
assertThat(repository.equipped(USER)).containsOnly(Map.entry(LookSlot.SKIN, "SKIN_RETRO_CRT"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
package core_service.modules.rewards.service;
|
||||
|
||||
import core_service.shared.security.Role;
|
||||
import core_service.shared.test.AbstractIntegrationTest;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.LocalDate;
|
||||
import java.time.YearMonth;
|
||||
import java.time.ZoneId;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class RewardServiceUnlockTest extends AbstractIntegrationTest {
|
||||
|
||||
private static final long USER = 14L;
|
||||
private static final long BIG_GAME = 990001L;
|
||||
private static final long SMALL_GAME = 990002L;
|
||||
private static final ZoneId BERLIN = ZoneId.of("Europe/Berlin");
|
||||
|
||||
@Autowired
|
||||
private RewardService rewardService;
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbc;
|
||||
|
||||
@BeforeEach
|
||||
@AfterEach
|
||||
void clean() {
|
||||
jdbc.update("delete from reward_ledger where user_id = ?", USER);
|
||||
jdbc.update("delete from user_game_playtime_day where user_id = ?", USER);
|
||||
jdbc.update("delete from user_game where user_id = ? and appid in (?, ?)", USER, BIG_GAME, SMALL_GAME);
|
||||
}
|
||||
|
||||
private List<String> unlocks() {
|
||||
return jdbc.queryForList("select ref from reward_ledger where user_id = ? and kind = 'UNLOCK' order by ref",
|
||||
String.class, USER);
|
||||
}
|
||||
|
||||
private void ledger(String kind, String ref, LocalDate day) {
|
||||
jdbc.update("insert into reward_ledger (user_id, kind, ref, points, event_at) values (?, ?, ?, 0, ?)",
|
||||
USER, kind, ref, Timestamp.from(day.atTime(12, 0).atZone(BERLIN).toInstant()));
|
||||
}
|
||||
|
||||
private void played(YearMonth month, int minutes) {
|
||||
jdbc.update("insert into user_game_playtime_day (user_id, day, appid, minutes) values (?, ?, 1, ?)",
|
||||
USER, java.sql.Date.valueOf(month.atDay(5)), minutes);
|
||||
}
|
||||
|
||||
@Test
|
||||
void wallet_FourWeeksInARowWithAFinish_UnlockTheStreakBadge() {
|
||||
LocalDate monday = LocalDate.now(BERLIN).with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).minusWeeks(5);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ledger("FINISHED", "60000" + i, monday.plusWeeks(i));
|
||||
}
|
||||
|
||||
rewardService.wallet(USER, Role.USER);
|
||||
|
||||
assertThat(unlocks()).containsExactly("BADGE_STREAK_4");
|
||||
}
|
||||
|
||||
@Test
|
||||
void wallet_FiveCompletedRows_UnlockTheCompletionistBadge_FourDoNot() {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ledger("COMPLETED", "70000" + i, LocalDate.now(BERLIN).minusDays(10));
|
||||
}
|
||||
rewardService.wallet(USER, Role.USER);
|
||||
assertThat(unlocks()).isEmpty();
|
||||
|
||||
ledger("COMPLETED", "700004", LocalDate.now(BERLIN).minusDays(10));
|
||||
rewardService.wallet(USER, Role.USER);
|
||||
|
||||
assertThat(unlocks()).containsExactly("BADGE_PERFECT_5");
|
||||
}
|
||||
|
||||
@Test
|
||||
void wallet_ThreeMonthsInARowSmallerPile_UnlockTheFrame() {
|
||||
YearMonth month = YearMonth.now(BERLIN).minusMonths(4);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
ledger("MONTH", month.plusMonths(i).toString(), month.atDay(1));
|
||||
}
|
||||
|
||||
rewardService.wallet(USER, Role.USER);
|
||||
|
||||
assertThat(unlocks()).containsExactly("FRAME_PILE_DOWN_3");
|
||||
}
|
||||
|
||||
@Test
|
||||
void wallet_PlaytimeRisingOverFullMonths_UnlocksPlayMore() {
|
||||
YearMonth first = YearMonth.now(BERLIN).minusMonths(4);
|
||||
played(first, 60);
|
||||
played(first.plusMonths(1), 120);
|
||||
played(first.plusMonths(2), 180);
|
||||
played(first.plusMonths(3), 240);
|
||||
|
||||
rewardService.wallet(USER, Role.USER);
|
||||
|
||||
assertThat(unlocks()).containsExactly("BADGE_PLAY_MORE_1", "BORDER_PLAY_MORE_3");
|
||||
}
|
||||
|
||||
@Test
|
||||
void wallet_MoreMinutesThanAnEmptyMonthBefore_UnlocksNothing() {
|
||||
played(YearMonth.now(BERLIN).minusMonths(2), 600);
|
||||
|
||||
rewardService.wallet(USER, Role.USER);
|
||||
|
||||
assertThat(unlocks()).isEmpty();
|
||||
}
|
||||
|
||||
// Hiding is free and reversible, so hidden games count for this track.
|
||||
@Test
|
||||
void hidingAGameForOneSettle_DoesNotUnlockPlayMore() {
|
||||
YearMonth first = YearMonth.now(BERLIN).minusMonths(3);
|
||||
for (long appid : List.of(BIG_GAME, SMALL_GAME)) {
|
||||
jdbc.update("insert into steam_game (appid, name) values (?, 'Security Game') on conflict do nothing", appid);
|
||||
jdbc.update("insert into user_game (user_id, appid, status, playtime_minutes) values (?, ?, 'BACKLOG', 0)",
|
||||
USER, appid);
|
||||
}
|
||||
// All games: 610 then 20 minutes, so the user played less, not more.
|
||||
playedGame(BIG_GAME, first, 600);
|
||||
playedGame(SMALL_GAME, first, 10);
|
||||
playedGame(SMALL_GAME, first.plusMonths(1), 20);
|
||||
|
||||
jdbc.update("update user_game set hidden_at = now() where user_id = ? and appid = ?", USER, BIG_GAME);
|
||||
rewardService.wallet(USER, Role.USER);
|
||||
jdbc.update("update user_game set hidden_at = null where user_id = ? and appid = ?", USER, BIG_GAME);
|
||||
rewardService.wallet(USER, Role.USER);
|
||||
|
||||
assertThat(unlocks()).isEmpty();
|
||||
}
|
||||
|
||||
private void playedGame(long appid, YearMonth month, int minutes) {
|
||||
jdbc.update("insert into user_game_playtime_day (user_id, day, appid, minutes) values (?, ?, ?, ?)",
|
||||
USER, java.sql.Date.valueOf(month.atDay(5)), appid, minutes);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user