fix(steam): sync owned apps Steam calls unvetted
Build, Test & Push Core Service / quality-check (push) Successful in 1m3s
Build, Test & Push Core Service / docker (push) Successful in 1m32s

GetOwnedGames leaves out "unvetted" apps unless skip_unvetted_apps=0
is set. So a released game owned through a developer license was
missing from the library.

Such an app can come without a name. The sync now keeps the stored
name, or uses "App <appid>" for a new game, so the NOT NULL name
column does not fail the whole sync.
This commit is contained in:
lobo committed 2026-09-26 10:04:23 +02:00
1 parent b350de1c79
commit 3224257325
4 files changed
+54 -2

No files matched your search

@@ -2,6 +2,7 @@ package core_service.modules.library.service;
import core_service.modules.library.domain.GameSource;
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.domain.UserSteamSync;
@@ -34,10 +35,12 @@ import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
@@ -415,4 +418,30 @@ class LibrarySyncWorkerTest extends AbstractUnitTest {
verify(timeToBeatService).refresh(List.of(220L, -1025L));
verify(moodService).refresh(List.of(220L, -1025L));
}
@Test
void runSync_GameWithoutName_GetsTheAppidAsName() {
when(userSteamSyncRepository.tryClaimRun(eq(USER_ID), eq(STEAM_ID), any())).thenReturn(1);
when(steamApiService.getOwnedGames(STEAM_ID)).thenReturn(List.of(
new SteamOwnedGamesResponse.Game(2000001, null, 12, null, null, null),
game(90, 0)));
worker.runSync(USER_ID, STEAM_ID);
verify(steamGameRepository).save(argThat((SteamGame g) -> g.getAppid() == 2000001L && "App 2000001".equals(g.getName())));
verify(steamGameRepository).save(argThat((SteamGame g) -> g.getAppid() == 220L));
}
@Test
void runSync_GameWithoutName_KeepsTheStoredName() {
SteamGame stored = new SteamGame(2000001L, "El Mucho", null, Instant.EPOCH);
when(userSteamSyncRepository.tryClaimRun(eq(USER_ID), eq(STEAM_ID), any())).thenReturn(1);
when(steamGameRepository.findById(2000001L)).thenReturn(Optional.of(stored));
when(steamApiService.getOwnedGames(STEAM_ID)).thenReturn(List.of(
new SteamOwnedGamesResponse.Game(2000001, " ", 12, null, null, null)));
worker.runSync(USER_ID, STEAM_ID);
assertThat(stored.getName()).isEqualTo("El Mucho");
}
}
@@ -124,6 +124,24 @@ class SteamApiServiceTest {
assertThat(result.getFirst().name()).isEqualTo("Counter-Strike");
}
@Test
void getOwnedGames_shouldIncludeUnvettedApps() {
server.expect(requestTo(containsString("/IPlayerService/GetOwnedGames/v1/")))
.andExpect(queryParam("skip_unvetted_apps", "0"))
.andRespond(withSuccess("{\"response\":{\"games\":[{\"appid\":10,\"name\":\"El Mucho\"}]}}", MediaType.APPLICATION_JSON));
assertThat(steamApiService.getOwnedGames("76561197960435530")).hasSize(1);
}
@Test
void getOwnedGamesCount_shouldCountUnvettedApps() {
server.expect(requestTo(containsString("/IPlayerService/GetOwnedGames/v1/")))
.andExpect(queryParam("skip_unvetted_apps", "0"))
.andRespond(withSuccess("{\"response\":{\"game_count\":1}}", MediaType.APPLICATION_JSON));
assertThat(steamApiService.getOwnedGamesCount("76561197960435530")).isEqualTo(1);
}
@Test
void getOwnedGames_shouldReturnGamesEvenWithNullValues() {
String steamId = "76561197960435530";