feat(library): weight the backlog suggestion by priority
Build, Test & Push Core Service / quality-check (push) Successful in 51s
Build, Test & Push Core Service / docker (push) Successful in 1m19s

A HIGH game is 4 times as likely as a LOW or unset one, a
MEDIUM game twice. Not HIGH first like the picker: the
dashboard shuffles by excluding only the current game, so a
strict order would swap between two games. The picker now
starts a stale Steam sync like the library does.
This commit is contained in:
lobo committed 2026-09-26 07:05:57 +02:00
1 parent 1cac7de836
commit 5e7455db05
4 files changed
+46 -2

No files matched your search

@@ -9,11 +9,13 @@ import core_service.modules.library.domain.UserGameId;
import core_service.modules.library.repo.GameTimeToBeatRepository;
import core_service.modules.library.repo.SteamGameRepository;
import core_service.modules.library.repo.UserGameRepository;
import core_service.modules.library.service.LibrarySyncService;
import core_service.shared.test.AbstractIntegrationTest;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;
import java.time.Instant;
@@ -21,6 +23,7 @@ import java.util.List;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.hasSize;
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -45,6 +48,9 @@ class LibraryControllerPickerTest extends AbstractIntegrationTest {
@Autowired
private GameTimeToBeatRepository gameTimeToBeatRepository;
@MockitoBean
private LibrarySyncService syncService;
@BeforeEach
void clean() {
userGameRepository.deleteAll();
@@ -163,4 +169,12 @@ class LibraryControllerPickerTest extends AbstractIntegrationTest {
mockMvc.perform(get("/api/library/picker"))
.andExpect(jsonPath("$.games", hasSize(0)));
}
// The dev profile's user has a Steam ID.
@Test
void picker_StartsAStaleSyncLikeTheLibrary() throws Exception {
mockMvc.perform(get("/api/library/picker")).andExpect(status().isOk());
verify(syncService).syncIfStale(USER_ID, "76561198000000001");
}
}
@@ -241,6 +241,24 @@ class UserGameRepositoryTest extends AbstractPersistenceTest {
assertTrue(userGameRepository.findRandomBacklogGame(99L, -1).isEmpty());
}
@Test
void findRandomBacklogGame_PicksHighPriorityGamesMoreOftenButNotOnly() {
UserGame high = userGameRepository.findById(new UserGameId(USER_ID, 700L)).orElseThrow();
high.setPriority(GamePriority.HIGH);
userGameRepository.flush();
int highPicks = 0;
for (int i = 0; i < 200; i++) {
if (userGameRepository.findRandomBacklogGame(USER_ID, UserGameRepository.NO_GAME).orElseThrow().getAppid() == 700L) {
highPicks++;
}
}
// HIGH weighs 4 against 1: about 160 of 200. Without weights about 100; 130 is far from both.
assertTrue(highPicks > 130, "HIGH picked " + highPicks + " of 200");
assertTrue(highPicks < 200, "the other backlog game was never picked");
}
@Test
void findRandomBacklogGame_SkipsTheExcludedGame() {
for (int i = 0; i < 5; i++) {