Public Access
feat(library): hide games from lists and stats
This commit is contained in:
1 parent
246e255058
commit
c2d17d0bc7
27 files changed
+559
-56
No files matched your search
@@ -5,6 +5,7 @@ import core_service.modules.library.domain.BrokenImageId;
|
||||
import core_service.modules.library.domain.ImageKind;
|
||||
import core_service.modules.library.domain.SteamGame;
|
||||
import core_service.modules.library.domain.UserGame;
|
||||
import core_service.modules.library.domain.UserGameId;
|
||||
import core_service.modules.library.repo.BrokenImageRepository;
|
||||
import core_service.modules.library.repo.SteamGameRepository;
|
||||
import core_service.modules.library.repo.UserGameRepository;
|
||||
@@ -71,6 +72,19 @@ class LibraryControllerBrokenImageTest extends AbstractIntegrationTest {
|
||||
assertThat(brokenImageRepository.count()).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
void reportBrokenImage_ForAHiddenGameStoresNothing() throws Exception {
|
||||
UserGame hidden = userGameRepository.findById(new UserGameId(USER_ID, APPID)).orElseThrow();
|
||||
hidden.setHiddenAt(Instant.now());
|
||||
userGameRepository.save(hidden);
|
||||
|
||||
mockMvc.perform(post("/api/library/broken-images").header(ClientHeaderFilter.CLIENT_HEADER, "test")
|
||||
.contentType(MediaType.APPLICATION_JSON).content("{\"appid\":220,\"kind\":\"COVER\"}"))
|
||||
.andExpect(status().isNoContent());
|
||||
|
||||
assertThat(brokenImageRepository.count()).isZero();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"{\"appid\":220,\"kind\":\"BANNER\"}", "{\"appid\":220}", "{\"kind\":\"COVER\"}"})
|
||||
void reportBrokenImage_BadBodyIs400(String body) throws Exception {
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
package core_service.modules.library.api;
|
||||
|
||||
import core_service.configuration.security.ClientHeaderFilter;
|
||||
import core_service.modules.library.domain.GameStatus;
|
||||
import core_service.modules.library.domain.SteamGame;
|
||||
import core_service.modules.library.domain.UserGame;
|
||||
import core_service.modules.library.domain.UserGameId;
|
||||
import core_service.modules.library.repo.SteamGameRepository;
|
||||
import core_service.modules.library.repo.UserGameRepository;
|
||||
import core_service.shared.test.AbstractIntegrationTest;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.ResultActions;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* The dev profile authenticates every request as user 1.
|
||||
*/
|
||||
@AutoConfigureMockMvc
|
||||
class LibraryControllerHiddenTest extends AbstractIntegrationTest {
|
||||
|
||||
private static final long USER_ID = 1L;
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Autowired
|
||||
private SteamGameRepository steamGameRepository;
|
||||
|
||||
@Autowired
|
||||
private UserGameRepository userGameRepository;
|
||||
|
||||
@BeforeEach
|
||||
void seed() {
|
||||
userGameRepository.deleteAll();
|
||||
steamGameRepository.save(new SteamGame(220L, "Half-Life 2", null, Instant.now()));
|
||||
steamGameRepository.save(new SteamGame(1000L, "Some Playtest", null, Instant.now()));
|
||||
steamGameRepository.save(new SteamGame(-1942L, "Zelda", null, Instant.now()));
|
||||
userGameRepository.save(new UserGame(USER_ID, 220L, 300, Instant.now(), Instant.now()));
|
||||
userGameRepository.save(new UserGame(USER_ID, 1000L, 0, Instant.now(), Instant.now()));
|
||||
userGameRepository.save(UserGame.manual(USER_ID, -1942L, "Switch", GameStatus.PLAYING, 60, Instant.now()));
|
||||
}
|
||||
|
||||
private ResultActions setHidden(long appid, String body) throws Exception {
|
||||
return mockMvc.perform(put("/api/library/games/" + appid + "/hidden").header(ClientHeaderFilter.CLIENT_HEADER, "test")
|
||||
.contentType(MediaType.APPLICATION_JSON).content(body));
|
||||
}
|
||||
|
||||
private UserGame stored(long appid) {
|
||||
return userGameRepository.findById(new UserGameId(USER_ID, appid)).orElseThrow();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hideAndUnhide_MoveTheGameBetweenTheLists() throws Exception {
|
||||
setHidden(1000L, "{\"hidden\":true}")
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.hidden").value(true));
|
||||
|
||||
mockMvc.perform(get("/api/library/games"))
|
||||
.andExpect(jsonPath("$.content", hasSize(2)))
|
||||
.andExpect(jsonPath("$.content[?(@.appid == 1000)]").isEmpty());
|
||||
mockMvc.perform(get("/api/library/games?hidden=true"))
|
||||
.andExpect(jsonPath("$.content", hasSize(1)))
|
||||
.andExpect(jsonPath("$.content[0].appid").value(1000))
|
||||
.andExpect(jsonPath("$.content[0].hidden").value(true));
|
||||
mockMvc.perform(get("/api/library/games-count")).andExpect(jsonPath("$.count").value(2));
|
||||
mockMvc.perform(get("/api/library/summary")).andExpect(jsonPath("$.total").value(2));
|
||||
mockMvc.perform(get("/api/library/games/1000"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.hidden").value(true));
|
||||
|
||||
setHidden(1000L, "{\"hidden\":false}").andExpect(status().isOk());
|
||||
|
||||
assertNull(stored(1000L).getHiddenAt());
|
||||
mockMvc.perform(get("/api/library/games-count")).andExpect(jsonPath("$.count").value(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hide_AHiddenGameKeepsTheFirstTime() throws Exception {
|
||||
Instant first = Instant.now().minus(3, ChronoUnit.DAYS).truncatedTo(ChronoUnit.MICROS);
|
||||
UserGame game = stored(1000L);
|
||||
game.setHiddenAt(first);
|
||||
userGameRepository.save(game);
|
||||
|
||||
setHidden(1000L, "{\"hidden\":true}").andExpect(status().isOk());
|
||||
|
||||
assertEquals(first, stored(1000L).getHiddenAt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void hide_UnknownGameIs404() throws Exception {
|
||||
setHidden(620L, "{\"hidden\":true}")
|
||||
.andExpect(status().isNotFound())
|
||||
.andExpect(jsonPath("$.code").value("GAME_NOT_FOUND"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hide_RemovedGameIs404() throws Exception {
|
||||
UserGame game = stored(1000L);
|
||||
game.setRemovedAt(Instant.now());
|
||||
userGameRepository.save(game);
|
||||
|
||||
setHidden(1000L, "{\"hidden\":true}").andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
@Test
|
||||
void hide_GameAddedByHandIs409() throws Exception {
|
||||
setHidden(-1942L, "{\"hidden\":true}")
|
||||
.andExpect(status().isConflict())
|
||||
.andExpect(jsonPath("$.code").value("MANUAL_GAME"));
|
||||
|
||||
assertNull(stored(-1942L).getHiddenAt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void unhide_WorksForAnyGame() throws Exception {
|
||||
UserGame manual = stored(-1942L);
|
||||
manual.setHiddenAt(Instant.now());
|
||||
userGameRepository.save(manual);
|
||||
|
||||
setHidden(-1942L, "{\"hidden\":false}").andExpect(status().isOk());
|
||||
|
||||
assertNull(stored(-1942L).getHiddenAt());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"{}", "{\"hidden\":null}", "{\"hidden\":\"maybe\"}"})
|
||||
void hide_BadBodyIs400(String body) throws Exception {
|
||||
setHidden(1000L, body).andExpect(status().isBadRequest());
|
||||
|
||||
assertNull(stored(1000L).getHiddenAt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void hide_WithoutClientHeaderIs403() throws Exception {
|
||||
mockMvc.perform(put("/api/library/games/1000/hidden")
|
||||
.contentType(MediaType.APPLICATION_JSON).content("{\"hidden\":true}"))
|
||||
.andExpect(status().isForbidden());
|
||||
|
||||
assertNull(stored(1000L).getHiddenAt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void hide_KeepsStatusAndNotes() throws Exception {
|
||||
UserGame game = stored(220L);
|
||||
game.setNotes("keep me");
|
||||
userGameRepository.save(game);
|
||||
|
||||
setHidden(220L, "{\"hidden\":true}").andExpect(status().isOk());
|
||||
|
||||
UserGame after = stored(220L);
|
||||
assertNotNull(after.getHiddenAt());
|
||||
assertEquals(GameStatus.PLAYING, after.getStatus());
|
||||
assertEquals("keep me", after.getNotes());
|
||||
}
|
||||
}
|
||||
@@ -174,6 +174,19 @@ class ManualGameControllerTest extends AbstractIntegrationTest {
|
||||
assertEquals(600, back.getPlaytimeMinutes());
|
||||
}
|
||||
|
||||
@Test
|
||||
void add_HiddenSteamGameRemovedBefore_ComesBackShown() throws Exception {
|
||||
steamGameRepository.save(new SteamGame(1245620L, "ELDEN RING", null, Instant.now()));
|
||||
UserGame refunded = new UserGame(USER_ID, 1245620L, 300, Instant.now(), Instant.now());
|
||||
refunded.setHiddenAt(Instant.now());
|
||||
refunded.setRemovedAt(Instant.now());
|
||||
userGameRepository.save(refunded);
|
||||
|
||||
add(119133, "PS5").andExpect(status().isCreated());
|
||||
|
||||
assertNull(stored(1245620).getHiddenAt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void add_UnknownIgdbGame_Is400() throws Exception {
|
||||
when(igdbApiService.getGame(anyLong())).thenReturn(Optional.empty());
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package core_service.modules.library.repo;
|
||||
|
||||
import core_service.modules.library.domain.DayMinutes;
|
||||
import core_service.modules.library.domain.SteamGame;
|
||||
import core_service.modules.library.domain.UserGame;
|
||||
import core_service.shared.test.AbstractPersistenceTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
@@ -18,6 +21,12 @@ class PlaytimeDayRepositoryTest extends AbstractPersistenceTest {
|
||||
@Autowired
|
||||
private PlaytimeDayRepository playtimeDayRepository;
|
||||
|
||||
@Autowired
|
||||
private SteamGameRepository steamGameRepository;
|
||||
|
||||
@Autowired
|
||||
private UserGameRepository userGameRepository;
|
||||
|
||||
@Test
|
||||
void addMinutes_SameGameAndDay_AddsUp() {
|
||||
playtimeDayRepository.addMinutes(USER_ID, 220L, DAY, 30);
|
||||
@@ -37,4 +46,25 @@ class PlaytimeDayRepositoryTest extends AbstractPersistenceTest {
|
||||
assertEquals(List.of(new DayMinutes(DAY, 50), new DayMinutes(DAY.plusDays(2), 10)),
|
||||
playtimeDayRepository.minutesPerDay(USER_ID, DAY));
|
||||
}
|
||||
|
||||
@Test
|
||||
void minutesPerDay_LeavesOutHiddenGames() {
|
||||
steamGameRepository.saveAll(List.of(new SteamGame(220L, "Half-Life 2", null, Instant.now()),
|
||||
new SteamGame(620L, "Portal 2", null, Instant.now())));
|
||||
UserGame hidden = new UserGame(USER_ID, 620L, 60, Instant.now(), Instant.now());
|
||||
hidden.setHiddenAt(Instant.now());
|
||||
userGameRepository.saveAll(List.of(new UserGame(USER_ID, 220L, 30, Instant.now(), Instant.now()), hidden,
|
||||
// Another user's hidden flag does not hide the game for this user.
|
||||
hiddenFor(2L, 220L)));
|
||||
playtimeDayRepository.addMinutes(USER_ID, 220L, DAY, 30);
|
||||
playtimeDayRepository.addMinutes(USER_ID, 620L, DAY, 60);
|
||||
|
||||
assertEquals(List.of(new DayMinutes(DAY, 30)), playtimeDayRepository.minutesPerDay(USER_ID, DAY));
|
||||
}
|
||||
|
||||
private static UserGame hiddenFor(long userId, long appid) {
|
||||
UserGame game = new UserGame(userId, appid, 0, Instant.now(), Instant.now());
|
||||
game.setHiddenAt(Instant.now());
|
||||
return game;
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,7 @@ class UserGameRepositoryTest extends AbstractPersistenceTest {
|
||||
}
|
||||
|
||||
private List<Long> appids(GameStatus status, String query) {
|
||||
return userGameRepository.findOwnedGames(USER_ID, status, UserGameRepository.containsPattern(query), null, PAGE)
|
||||
return userGameRepository.findOwnedGames(USER_ID, false, status, UserGameRepository.containsPattern(query), null, PAGE)
|
||||
.map(UserGame::getAppid).getContent();
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ class UserGameRepositoryTest extends AbstractPersistenceTest {
|
||||
|
||||
@Test
|
||||
void findOwnedGames_CountsAllMatchesAcrossPages() {
|
||||
var page = userGameRepository.findOwnedGames(USER_ID, null, UserGameRepository.containsPattern("half"), null,
|
||||
var page = userGameRepository.findOwnedGames(USER_ID, false, null, UserGameRepository.containsPattern("half"), null,
|
||||
PageRequest.of(0, 1, Sort.by("appid")));
|
||||
|
||||
assertEquals(List.of(220L), page.map(UserGame::getAppid).getContent());
|
||||
@@ -144,7 +144,7 @@ class UserGameRepositoryTest extends AbstractPersistenceTest {
|
||||
}
|
||||
|
||||
private List<Long> sortedAppids(Sort sort) {
|
||||
return userGameRepository.findOwnedGames(USER_ID, null, null, null, PageRequest.of(0, 20, UserGameRepository.querySort(sort)))
|
||||
return userGameRepository.findOwnedGames(USER_ID, false, null, null, null, PageRequest.of(0, 20, UserGameRepository.querySort(sort)))
|
||||
.map(UserGame::getAppid).getContent();
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ class UserGameRepositoryTest extends AbstractPersistenceTest {
|
||||
new GameTimeToBeat(620L, 300, 480, 900, Instant.now()),
|
||||
new GameTimeToBeat(380L, null, null, null, Instant.now())));
|
||||
|
||||
List<Long> shortGames = userGameRepository.findOwnedGames(USER_ID, null, null, 600, PAGE).map(UserGame::getAppid).getContent();
|
||||
List<Long> shortGames = userGameRepository.findOwnedGames(USER_ID, false, null, null, 600, PAGE).map(UserGame::getAppid).getContent();
|
||||
|
||||
assertEquals(List.of(620L), shortGames);
|
||||
}
|
||||
@@ -210,6 +210,65 @@ class UserGameRepositoryTest extends AbstractPersistenceTest {
|
||||
assertEquals(350, userGameRepository.sumBacklogMinutes(USER_ID));
|
||||
}
|
||||
|
||||
private void hide(long... appids) {
|
||||
for (long appid : appids) {
|
||||
userGameRepository.findById(new UserGameId(USER_ID, appid)).orElseThrow().setHiddenAt(Instant.now());
|
||||
}
|
||||
userGameRepository.flush();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hiddenGames_LeaveTheListAndShowOnlyUnderTheHiddenFilter() {
|
||||
hide(620L, 700L);
|
||||
|
||||
assertEquals(List.of(220L, 380L), appids(null, null));
|
||||
assertEquals(List.of(620L, 700L), userGameRepository.findOwnedGames(USER_ID, true, null, null, null,
|
||||
PageRequest.of(0, 20, Sort.by("appid"))).map(UserGame::getAppid).getContent());
|
||||
assertEquals(Set.of(620L, 700L), Set.copyOf(userGameRepository.findHiddenAppids(USER_ID)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hiddenGames_LeaveEveryCountAndSum() {
|
||||
gameTimeToBeatRepository.saveAll(List.of(
|
||||
new GameTimeToBeat(380L, 100, 200, 300, Instant.now()),
|
||||
new GameTimeToBeat(700L, 100, 150, 300, Instant.now())));
|
||||
userGameRepository.findById(new UserGameId(USER_ID, 220L)).orElseThrow().setPlaytime2Weeks(30);
|
||||
userGameRepository.findById(new UserGameId(USER_ID, 620L)).orElseThrow().setPlaytime2Weeks(60);
|
||||
hide(620L, 700L);
|
||||
|
||||
assertEquals(2, userGameRepository.countListed(USER_ID));
|
||||
assertEquals(300, userGameRepository.sumPlaytime(USER_ID));
|
||||
assertEquals(1, userGameRepository.countListedNeverPlayed(USER_ID));
|
||||
assertEquals(200, userGameRepository.sumBacklogMinutes(USER_ID));
|
||||
assertEquals(Set.of(new StatusCount(GameStatus.PLAYING, 1), new StatusCount(GameStatus.BACKLOG, 1)),
|
||||
Set.copyOf(userGameRepository.countByStatus(USER_ID)));
|
||||
assertEquals(List.of(380L), userGameRepository.findBacklogAppids(USER_ID));
|
||||
assertEquals(List.of(220L), userGameRepository.findListedPlayedInTwoWeeks(USER_ID, PAGE)
|
||||
.stream().map(UserGame::getAppid).toList());
|
||||
assertEquals(List.of(220L), userGameRepository.findListedByStatusIn(USER_ID, List.of(GameStatus.PLAYING), PAGE)
|
||||
.stream().map(UserGame::getAppid).toList());
|
||||
assertEquals(Set.of(220L, 380L), Set.copyOf(userGameRepository.findAchievementProgress(USER_ID)
|
||||
.stream().map(p -> p.appid()).toList()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hiddenGames_AreNeverPicked() {
|
||||
hide(700L);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
assertEquals(380L, userGameRepository.findRandomBacklogGame(USER_ID, -1).orElseThrow().getAppid());
|
||||
assertEquals(List.of(380L), userGameRepository.findPickerGames(USER_ID, null, null, List.of(), 10)
|
||||
.stream().map(UserGame::getAppid).toList());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void findActiveAppids_StillSeesHiddenGames() {
|
||||
hide(700L);
|
||||
|
||||
assertEquals(List.of(700L), userGameRepository.findActiveAppids(USER_ID, List.of(700L)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void containsPattern_BlankIsNoFilter() {
|
||||
assertNull(UserGameRepository.containsPattern(" "));
|
||||
@@ -235,7 +294,7 @@ class UserGameRepositoryTest extends AbstractPersistenceTest {
|
||||
userGameRepository.save(new UserGame(2L, 380L, 0, Instant.now(), Instant.now()));
|
||||
userGameRepository.flush();
|
||||
|
||||
assertEquals(1, userGameRepository.countByUserIdAndRemovedAtIsNullAndPlaytimeMinutes(USER_ID, 0));
|
||||
assertEquals(1, userGameRepository.countListedNeverPlayed(USER_ID));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -124,6 +124,17 @@ class AchievementSyncServiceTest extends AbstractUnitTest {
|
||||
verifyNoInteractions(steamApiService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void syncLibrary_HiddenGame_AsksSteamNothing() {
|
||||
UserGame hidden = game(620L, 120);
|
||||
hidden.setHiddenAt(Instant.now());
|
||||
library(List.of(hidden), List.of(), List.of());
|
||||
|
||||
service.syncLibrary(USER_ID, STEAM_ID);
|
||||
|
||||
verifyNoInteractions(steamApiService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void syncLibrary_ChangedPlaytime_StoresUnlocksAndThePlaytime() {
|
||||
library(List.of(game(620L, 150)), List.of(schema(620L, 10)), List.of(unlocks(620L, 3, 120)));
|
||||
|
||||
@@ -7,6 +7,7 @@ import core_service.modules.library.domain.SteamGame;
|
||||
import core_service.modules.library.domain.GameStatus;
|
||||
import core_service.modules.library.domain.UserGame;
|
||||
import core_service.modules.library.domain.UserGameAchievementMeta;
|
||||
import core_service.modules.library.domain.UserGameId;
|
||||
import core_service.modules.library.repo.GameAchievementSchemaMetaRepository;
|
||||
import core_service.modules.library.repo.SteamGameRepository;
|
||||
import core_service.modules.library.repo.GameAchievementSchemaRepository;
|
||||
@@ -104,6 +105,21 @@ class GameDetailServiceTest extends AbstractIntegrationTest {
|
||||
assertEquals(false, detail.hasAchievements());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getGameDetail_HiddenGame_ShowsStoredDataAndAsksNobody() {
|
||||
UserGame hidden = userGameRepository.findById(new UserGameId(USER_ID, 620L)).orElseThrow();
|
||||
hidden.setHiddenAt(Instant.now());
|
||||
userGameRepository.save(hidden);
|
||||
schemaMetaRepository.save(new GameAchievementSchemaMeta(620L, 1, true, Instant.now().minus(Duration.ofDays(31))));
|
||||
schemaRepository.save(new GameAchievementSchema(620L, "A", "A", null, "icon", "gray", false));
|
||||
|
||||
GameDetailDto detail = gameDetailService.getGameDetail(USER_ID, STEAM_ID, 620L).orElseThrow();
|
||||
|
||||
org.mockito.Mockito.verifyNoInteractions(steamApiService, speedrunService, timeToBeatService, moodService);
|
||||
assertEquals(true, detail.hidden());
|
||||
assertEquals(1, detail.achievementsTotal());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getGameDetail_GameAddedByHand_AsksForTheTimeToBeatAndMoodsAgain() {
|
||||
userGameRepository.save(UserGame.manual(USER_ID, 620L, "PS5", GameStatus.PLAYING, 60, Instant.now()));
|
||||
|
||||
@@ -80,7 +80,7 @@ class ImageRepairServiceTest extends AbstractUnitTest {
|
||||
|
||||
@Test
|
||||
void report_StoresAReportForAnOwnedSteamGame() {
|
||||
when(userGameRepository.findActiveAppids(1L, List.of(620L))).thenReturn(List.of(620L));
|
||||
when(userGameRepository.findListedAppids(1L, List.of(620L))).thenReturn(List.of(620L));
|
||||
|
||||
service.report(1L, 620L, ImageKind.COVER);
|
||||
|
||||
@@ -89,7 +89,7 @@ class ImageRepairServiceTest extends AbstractUnitTest {
|
||||
|
||||
@Test
|
||||
void report_DropsGamesTheUserDoesNotOwn() {
|
||||
when(userGameRepository.findActiveAppids(1L, List.of(620L))).thenReturn(List.of());
|
||||
when(userGameRepository.findListedAppids(1L, List.of(620L))).thenReturn(List.of());
|
||||
|
||||
service.report(1L, 620L, ImageKind.COVER);
|
||||
|
||||
|
||||
@@ -419,6 +419,31 @@ class LibrarySyncWorkerTest extends AbstractUnitTest {
|
||||
verify(moodService).refresh(List.of(220L, -1025L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void runSync_KeepsTheHiddenFlagAndUpdatesThePlaytime() {
|
||||
UserGame hidden = owned(60);
|
||||
Instant hiddenAt = Instant.parse("2026-09-01T10:00:00Z");
|
||||
hidden.setHiddenAt(hiddenAt);
|
||||
steamReturns(game(90, 30), STEAM_ID);
|
||||
|
||||
worker.runSync(USER_ID, STEAM_ID);
|
||||
|
||||
assertEquals(hiddenAt, hidden.getHiddenAt());
|
||||
assertEquals(90, hidden.getPlaytimeMinutes());
|
||||
verify(playtimeDayRepository).addMinutes(USER_ID, 220L, today(), 30);
|
||||
}
|
||||
|
||||
@Test
|
||||
void runSync_HiddenGamesGetNoTimeToBeatOrMoods() {
|
||||
steamReturns(game(0, 0), STEAM_ID);
|
||||
when(userGameRepository.findHiddenAppids(USER_ID)).thenReturn(List.of(220L));
|
||||
|
||||
worker.runSync(USER_ID, STEAM_ID);
|
||||
|
||||
verify(timeToBeatService).refresh(List.of());
|
||||
verify(moodService).refresh(List.of());
|
||||
}
|
||||
|
||||
@Test
|
||||
void runSync_GameWithoutName_GetsTheAppidAsName() {
|
||||
when(userSteamSyncRepository.tryClaimRun(eq(USER_ID), eq(STEAM_ID), any())).thenReturn(1);
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package core_service.modules.stats.repo;
|
||||
|
||||
import core_service.modules.library.domain.SteamGame;
|
||||
import core_service.modules.library.domain.UserGame;
|
||||
import core_service.modules.library.repo.SteamGameRepository;
|
||||
import core_service.modules.library.repo.UserGameRepository;
|
||||
import core_service.shared.test.AbstractPersistenceTest;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@Import(StatsRepository.class)
|
||||
class StatsRepositoryTest extends AbstractPersistenceTest {
|
||||
|
||||
@Autowired
|
||||
private StatsRepository statsRepository;
|
||||
|
||||
@Autowired
|
||||
private SteamGameRepository steamGameRepository;
|
||||
|
||||
@Autowired
|
||||
private UserGameRepository userGameRepository;
|
||||
|
||||
@BeforeEach
|
||||
void seed() {
|
||||
userGameRepository.deleteAll();
|
||||
steamGameRepository.saveAllAndFlush(List.of(new SteamGame(220L, "Half-Life 2", null, Instant.now()),
|
||||
new SteamGame(1000L, "Playtest", null, Instant.now())));
|
||||
UserGame hiddenPlaytest = new UserGame(1L, 1000L, 5000, Instant.now(), Instant.now());
|
||||
hiddenPlaytest.setHiddenAt(Instant.now());
|
||||
UserGame hiddenNeverPlayed = new UserGame(2L, 1000L, 0, Instant.now(), Instant.now());
|
||||
hiddenNeverPlayed.setHiddenAt(Instant.now());
|
||||
userGameRepository.saveAllAndFlush(List.of(new UserGame(1L, 220L, 300, Instant.now(), Instant.now()),
|
||||
hiddenPlaytest, hiddenNeverPlayed));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hiddenGames_LeaveEveryNumber() {
|
||||
assertEquals(1, statsRepository.countDistinctGames());
|
||||
assertEquals(1, statsRepository.countLibraryEntries());
|
||||
assertEquals(1, statsRepository.countGamesPlayed());
|
||||
assertEquals(300, statsRepository.sumPlaytimeMinutes());
|
||||
assertEquals(1, statsRepository.countUsersWithLibrary());
|
||||
assertEquals(1, statsRepository.biggestLibrary());
|
||||
assertEquals(0, statsRepository.biggestBacklog());
|
||||
}
|
||||
|
||||
@Test
|
||||
void hiddenGames_LeaveTheTopLists() {
|
||||
assertEquals(List.of(220L), appids(statsRepository.topOwnedGames(5)));
|
||||
assertEquals(List.of(220L), appids(statsRepository.topPlayedGames(5)));
|
||||
}
|
||||
|
||||
private static List<Long> appids(List<Map<String, Object>> rows) {
|
||||
return rows.stream().map(row -> ((Number) row.get("appid")).longValue()).toList();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user