Public Access
feat(picker): return rows of up to 3 games per source
Picker returns categories in fixed order: NEXT_UP, SIMILAR, LONGEST, RANDOM, each with up to 3 games. Empty rows are left out. Param `count` is removed; new optional `category` loads one row (reroll). Budget and exclude apply to all rows; mood only to RANDOM. SIMILAR takes the 3 most played games, one similar game each. Remove PickMix.
This commit is contained in:
1 parent
867dc4c4d1
commit
8dd398321c
9 files changed
+223
-305
No files matched your search
@@ -27,9 +27,12 @@ import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.hamcrest.Matchers.anyOf;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.everyItem;
|
||||
import static org.hamcrest.Matchers.lessThanOrEqualTo;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.hasItem;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -100,6 +103,17 @@ class LibraryControllerPickerTest extends AbstractMockedLibraryServicesTest {
|
||||
}
|
||||
}
|
||||
|
||||
private void putOrder(Long... appids) throws Exception {
|
||||
mockMvc.perform(put("/api/library/backlog").header(ClientHeaderFilter.CLIENT_HEADER, "test")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("{\"appids\":" + Arrays.toString(appids) + "}"))
|
||||
.andExpect(status().isNoContent());
|
||||
}
|
||||
|
||||
private static String row(String source) {
|
||||
return "$.categories[?(@.source == '" + source + "')].games[*].appid";
|
||||
}
|
||||
|
||||
@Test
|
||||
void picker_WithMood_PicksOnlyGamesKnownToHaveIt() throws Exception {
|
||||
game(10L, GameStatus.BACKLOG, null, null);
|
||||
@@ -110,11 +124,11 @@ class LibraryControllerPickerTest extends AbstractMockedLibraryServicesTest {
|
||||
gameMoodsRepository.upsert(20L, new String[]{"ACTION"}, new String[0], false, Instant.now());
|
||||
gameMoodsRepository.upsert(40L, new String[]{"CHILL"}, new String[0], false, Instant.now());
|
||||
|
||||
mockMvc.perform(get("/api/library/picker").param("mood", "CHILL"))
|
||||
mockMvc.perform(get("/api/library/picker").param("category", "RANDOM").param("mood", "CHILL"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.games[*].appid", containsInAnyOrder(10)));
|
||||
mockMvc.perform(get("/api/library/picker"))
|
||||
.andExpect(jsonPath("$.games[*].appid", containsInAnyOrder(10, 20, 30)));
|
||||
.andExpect(jsonPath("$.categories[0].games[*].appid", containsInAnyOrder(10)));
|
||||
mockMvc.perform(get("/api/library/picker").param("category", "RANDOM"))
|
||||
.andExpect(jsonPath("$.categories[0].games[*].appid", containsInAnyOrder(10, 20, 30)));
|
||||
verify(moodService).refreshBacklogInBackground(USER_ID);
|
||||
}
|
||||
|
||||
@@ -126,9 +140,9 @@ class LibraryControllerPickerTest extends AbstractMockedLibraryServicesTest {
|
||||
gameSpeedrunRepository.upsert(10L, "o1y9j9v6", "https://www.speedrun.com/celeste", Instant.now());
|
||||
gameSpeedrunRepository.upsert(20L, null, null, Instant.now());
|
||||
|
||||
mockMvc.perform(get("/api/library/picker").param("mood", "SPEEDRUN"))
|
||||
mockMvc.perform(get("/api/library/picker").param("category", "RANDOM").param("mood", "SPEEDRUN"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.games[*].appid", containsInAnyOrder(10)));
|
||||
.andExpect(jsonPath("$.categories[0].games[*].appid", containsInAnyOrder(10)));
|
||||
verify(speedrunService).refreshBacklogInBackground(USER_ID);
|
||||
verifyNoInteractions(moodService);
|
||||
}
|
||||
@@ -140,8 +154,9 @@ class LibraryControllerPickerTest extends AbstractMockedLibraryServicesTest {
|
||||
gameMoodsRepository.upsert(10L, new String[]{"STORY"}, new String[0], false, Instant.now());
|
||||
gameMoodsRepository.upsert(20L, new String[]{"STORY"}, new String[0], false, Instant.now());
|
||||
|
||||
mockMvc.perform(get("/api/library/picker").param("mood", "STORY").param("maxMinutes", "900"))
|
||||
.andExpect(jsonPath("$.games[*].appid", containsInAnyOrder(10)));
|
||||
mockMvc.perform(get("/api/library/picker").param("category", "RANDOM").param("mood", "STORY")
|
||||
.param("maxMinutes", "900"))
|
||||
.andExpect(jsonPath("$.categories[0].games[*].appid", containsInAnyOrder(10)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -151,12 +166,18 @@ class LibraryControllerPickerTest extends AbstractMockedLibraryServicesTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void picker_EmptyBacklog_ReturnsNoGames() throws Exception {
|
||||
void picker_UnknownCategory_Is400() throws Exception {
|
||||
mockMvc.perform(get("/api/library/picker").param("category", "FAVOURITES"))
|
||||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
void picker_EmptyBacklog_ReturnsNoCategories() throws Exception {
|
||||
game(10L, GameStatus.PLAYING, null, 60);
|
||||
|
||||
mockMvc.perform(get("/api/library/picker"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.games", hasSize(0)));
|
||||
.andExpect(jsonPath("$.categories", hasSize(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -166,10 +187,10 @@ class LibraryControllerPickerTest extends AbstractMockedLibraryServicesTest {
|
||||
game(30L, GameStatus.FINISHED, null, 60);
|
||||
game(40L, GameStatus.DROPPED, null, 60);
|
||||
|
||||
mockMvc.perform(get("/api/library/picker"))
|
||||
mockMvc.perform(get("/api/library/picker").param("category", "RANDOM"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.games[*].appid", containsInAnyOrder(10, 20)))
|
||||
.andExpect(jsonPath("$.games[?(@.appid == 10)].timeToBeatMinutes").value(60));
|
||||
.andExpect(jsonPath("$.categories[0].games[*].appid", containsInAnyOrder(10, 20)))
|
||||
.andExpect(jsonPath("$.categories[0].games[?(@.appid == 10)].timeToBeatMinutes").value(60));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -178,9 +199,9 @@ class LibraryControllerPickerTest extends AbstractMockedLibraryServicesTest {
|
||||
game(20L, GameStatus.BACKLOG, null, 301);
|
||||
game(30L, GameStatus.BACKLOG, null, null);
|
||||
|
||||
mockMvc.perform(get("/api/library/picker").param("maxMinutes", "300"))
|
||||
mockMvc.perform(get("/api/library/picker").param("category", "RANDOM").param("maxMinutes", "300"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.games[*].appid", containsInAnyOrder(10)));
|
||||
.andExpect(jsonPath("$.categories[0].games[*].appid", containsInAnyOrder(10)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -188,8 +209,8 @@ class LibraryControllerPickerTest extends AbstractMockedLibraryServicesTest {
|
||||
game(10L, GameStatus.BACKLOG, null, 300);
|
||||
game(20L, GameStatus.BACKLOG, null, null);
|
||||
|
||||
mockMvc.perform(get("/api/library/picker").param("maxMinutes", "0"))
|
||||
.andExpect(jsonPath("$.games[*].appid", containsInAnyOrder(10, 20)));
|
||||
mockMvc.perform(get("/api/library/picker").param("category", "RANDOM").param("maxMinutes", "0"))
|
||||
.andExpect(jsonPath("$.categories[0].games[*].appid", containsInAnyOrder(10, 20)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -198,80 +219,129 @@ class LibraryControllerPickerTest extends AbstractMockedLibraryServicesTest {
|
||||
game(20L, GameStatus.BACKLOG, null, 60);
|
||||
game(30L, GameStatus.BACKLOG, null, 60);
|
||||
|
||||
mockMvc.perform(get("/api/library/picker").param("exclude", "10", "30"))
|
||||
mockMvc.perform(get("/api/library/picker").param("category", "RANDOM").param("exclude", "10", "30"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.games[*].appid", containsInAnyOrder(20)));
|
||||
.andExpect(jsonPath("$.categories[0].games[*].appid", containsInAnyOrder(20)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void picker_FillsSlotsByPriorityFirst() throws Exception {
|
||||
void picker_RandomRow_FillsByPriorityFirst() throws Exception {
|
||||
game(10L, GameStatus.BACKLOG, null, 60);
|
||||
game(20L, GameStatus.BACKLOG, GamePriority.LOW, 60);
|
||||
game(30L, GameStatus.BACKLOG, GamePriority.HIGH, 60);
|
||||
game(40L, GameStatus.BACKLOG, GamePriority.MEDIUM, 60);
|
||||
game(50L, GameStatus.BACKLOG, null, 60);
|
||||
|
||||
mockMvc.perform(get("/api/library/picker").param("count", "2"))
|
||||
mockMvc.perform(get("/api/library/picker").param("category", "RANDOM"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.games[*].appid", containsInAnyOrder(30, 40)))
|
||||
.andExpect(jsonPath("$.games[0].appid").value(30));
|
||||
.andExpect(jsonPath("$.categories[0].games", hasSize(3)))
|
||||
.andExpect(jsonPath("$.categories[0].games[0].appid").value(30))
|
||||
.andExpect(jsonPath("$.categories[0].games[1].appid").value(40));
|
||||
}
|
||||
|
||||
@Test
|
||||
void picker_ThreeSlots_OneGamePerSourceWithReason() throws Exception {
|
||||
void picker_AllRows_InFixedOrderEachAtMostThree() throws Exception {
|
||||
game(1L, GameStatus.PLAYING, null, null, 600, SEEN);
|
||||
game(10L, GameStatus.BACKLOG, null, null, 0, Instant.parse("2020-01-01T00:00:00Z"));
|
||||
game(20L, GameStatus.BACKLOG, null, null, 0, SEEN);
|
||||
game(30L, GameStatus.BACKLOG, null, null, 0, SEEN);
|
||||
for (long appid = 10; appid <= 15; appid++) {
|
||||
game(appid, GameStatus.BACKLOG, null, null);
|
||||
}
|
||||
gameMoodsRepository.upsert(1L, new String[]{"STORY"}, new String[0], false, Instant.now());
|
||||
gameMoodsRepository.upsert(30L, new String[]{"STORY"}, new String[0], false, Instant.now());
|
||||
gameMoodsRepository.upsert(10L, new String[]{"STORY"}, new String[0], false, Instant.now());
|
||||
|
||||
mockMvc.perform(get("/api/library/picker"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.games", hasSize(3)))
|
||||
.andExpect(jsonPath("$.games[*].reason.source", contains("SIMILAR", "LONGEST", "RANDOM")))
|
||||
.andExpect(jsonPath("$.games[0].appid").value(30))
|
||||
.andExpect(jsonPath("$.games[0].reason.anchorAppid").value(1))
|
||||
.andExpect(jsonPath("$.games[0].reason.anchorName").value("Game 1"))
|
||||
.andExpect(jsonPath("$.games[0].reason.anchorPlaytimeMinutes").value(600))
|
||||
.andExpect(jsonPath("$.games[1].appid").value(10))
|
||||
.andExpect(jsonPath("$.games[1].reason.firstSeenAt").exists())
|
||||
.andExpect(jsonPath("$.games[2].appid").value(20));
|
||||
.andExpect(jsonPath("$.categories[*].source", contains("NEXT_UP", "SIMILAR", "LONGEST", "RANDOM")))
|
||||
.andExpect(jsonPath("$.categories[*].games.length()", everyItem(lessThanOrEqualTo(3))))
|
||||
.andExpect(jsonPath("$.categories[0].games[*].reason.source", everyItem(is("NEXT_UP"))))
|
||||
.andExpect(jsonPath("$.categories[2].games[0].reason.firstSeenAt").exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
void picker_NoPlaytime_SimilarSlotFallsBackToRandom() throws Exception {
|
||||
game(10L, GameStatus.BACKLOG, null, null, 0, Instant.parse("2020-01-01T00:00:00Z"));
|
||||
game(20L, GameStatus.BACKLOG, null, null);
|
||||
game(30L, GameStatus.BACKLOG, null, null);
|
||||
void picker_UpNext_FirstThreeOfOwnOrderInOrder() throws Exception {
|
||||
for (long appid = 1; appid <= 7; appid++) {
|
||||
game(appid, GameStatus.BACKLOG, null, null);
|
||||
}
|
||||
putOrder(7L, 6L, 5L, 4L, 3L, 2L, 1L);
|
||||
|
||||
mockMvc.perform(get("/api/library/picker"))
|
||||
.andExpect(jsonPath("$.games", hasSize(3)))
|
||||
.andExpect(jsonPath("$.games[*].reason.source", containsInAnyOrder("LONGEST", "RANDOM", "RANDOM")));
|
||||
mockMvc.perform(get("/api/library/picker").param("category", "NEXT_UP"))
|
||||
.andExpect(jsonPath("$.categories[0].games[*].appid", contains(7, 6, 5)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void picker_FewerBacklogGamesThanCount_NoDuplicates() throws Exception {
|
||||
game(10L, GameStatus.BACKLOG, null, null);
|
||||
game(20L, GameStatus.BACKLOG, null, null);
|
||||
|
||||
mockMvc.perform(get("/api/library/picker").param("count", "3"))
|
||||
.andExpect(jsonPath("$.games", hasSize(2)))
|
||||
.andExpect(jsonPath("$.games[*].appid", containsInAnyOrder(10, 20)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void picker_DefaultsToThreeAndCapsTheCount() throws Exception {
|
||||
for (long appid = 1; appid <= 12; appid++) {
|
||||
game(appid, GameStatus.BACKLOG, null, 60);
|
||||
void picker_BecauseYouPlayed_OneSimilarGamePerTopPlayedGame() throws Exception {
|
||||
game(1L, GameStatus.PLAYING, null, null, 900, SEEN);
|
||||
game(2L, GameStatus.PLAYING, null, null, 800, SEEN);
|
||||
game(3L, GameStatus.PLAYING, null, null, 700, SEEN);
|
||||
game(11L, GameStatus.BACKLOG, null, null);
|
||||
game(12L, GameStatus.BACKLOG, null, null);
|
||||
game(13L, GameStatus.BACKLOG, null, null);
|
||||
String[] moods = {"STORY", "COOP", "ACTION"};
|
||||
for (int i = 0; i < 3; i++) {
|
||||
gameMoodsRepository.upsert(1L + i, new String[]{moods[i]}, new String[0], false, Instant.now());
|
||||
gameMoodsRepository.upsert(11L + i, new String[]{moods[i]}, new String[0], false, Instant.now());
|
||||
}
|
||||
|
||||
mockMvc.perform(get("/api/library/picker").param("category", "SIMILAR"))
|
||||
.andExpect(jsonPath("$.categories[0].games", hasSize(3)))
|
||||
.andExpect(jsonPath("$.categories[0].games[*].appid", contains(11, 12, 13)))
|
||||
.andExpect(jsonPath("$.categories[0].games[*].reason.anchorAppid", contains(1, 2, 3)))
|
||||
.andExpect(jsonPath("$.categories[0].games[0].reason.anchorName").value("Game 1"))
|
||||
.andExpect(jsonPath("$.categories[0].games[0].reason.anchorPlaytimeMinutes").value(900));
|
||||
}
|
||||
|
||||
@Test
|
||||
void picker_BecauseYouPlayed_SkipsAnchorWithoutCandidateAndNeverRepeats() throws Exception {
|
||||
game(1L, GameStatus.PLAYING, null, null, 900, SEEN);
|
||||
game(2L, GameStatus.PLAYING, null, null, 800, SEEN);
|
||||
game(3L, GameStatus.PLAYING, null, null, 700, SEEN);
|
||||
game(11L, GameStatus.BACKLOG, null, null);
|
||||
gameMoodsRepository.upsert(1L, new String[]{"STORY"}, new String[0], false, Instant.now());
|
||||
gameMoodsRepository.upsert(2L, new String[]{"STORY"}, new String[0], false, Instant.now());
|
||||
gameMoodsRepository.upsert(11L, new String[]{"STORY"}, new String[0], false, Instant.now());
|
||||
|
||||
mockMvc.perform(get("/api/library/picker").param("category", "SIMILAR"))
|
||||
.andExpect(jsonPath("$.categories[0].games", hasSize(1)))
|
||||
.andExpect(jsonPath("$.categories[0].games[0].reason.anchorAppid").value(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void picker_NoPlayedGames_LeavesOutBecauseYouPlayed() throws Exception {
|
||||
game(10L, GameStatus.BACKLOG, null, null);
|
||||
|
||||
mockMvc.perform(get("/api/library/picker"))
|
||||
.andExpect(jsonPath("$.games", hasSize(3)));
|
||||
mockMvc.perform(get("/api/library/picker").param("count", "500"))
|
||||
.andExpect(jsonPath("$.games", hasSize(10)));
|
||||
mockMvc.perform(get("/api/library/picker").param("count", "0"))
|
||||
.andExpect(jsonPath("$.games", hasSize(1)));
|
||||
.andExpect(jsonPath("$.categories[*].source", contains("NEXT_UP", "LONGEST", "RANDOM")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void picker_Mood_AppliesOnlyToMoodRow() throws Exception {
|
||||
game(10L, GameStatus.BACKLOG, null, null, 0, Instant.parse("2020-01-01T00:00:00Z"));
|
||||
game(20L, GameStatus.BACKLOG, null, null);
|
||||
gameMoodsRepository.upsert(20L, new String[]{"CHILL"}, new String[0], false, Instant.now());
|
||||
|
||||
mockMvc.perform(get("/api/library/picker").param("mood", "CHILL"))
|
||||
.andExpect(jsonPath(row("RANDOM"), contains(20)))
|
||||
.andExpect(jsonPath(row("LONGEST"), hasItem(10)))
|
||||
.andExpect(jsonPath(row("NEXT_UP"), hasItem(10)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void picker_BudgetAndExclude_ApplyToAllRows() throws Exception {
|
||||
game(10L, GameStatus.BACKLOG, null, 60);
|
||||
game(20L, GameStatus.BACKLOG, null, 3000);
|
||||
game(30L, GameStatus.BACKLOG, null, 60);
|
||||
|
||||
mockMvc.perform(get("/api/library/picker").param("maxMinutes", "300").param("exclude", "30"))
|
||||
.andExpect(jsonPath("$.categories[*].games[*].appid", everyItem(is(10))))
|
||||
.andExpect(jsonPath("$.categories[*].games[*].appid", not(hasItem(20))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void picker_Category_ReturnsOnlyThatRow() throws Exception {
|
||||
game(10L, GameStatus.BACKLOG, null, null);
|
||||
|
||||
mockMvc.perform(get("/api/library/picker").param("category", "LONGEST"))
|
||||
.andExpect(jsonPath("$.categories", hasSize(1)))
|
||||
.andExpect(jsonPath("$.categories[0].source").value("LONGEST"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -282,8 +352,8 @@ class LibraryControllerPickerTest extends AbstractMockedLibraryServicesTest {
|
||||
removed.setRemovedAt(Instant.now());
|
||||
userGameRepository.save(removed);
|
||||
|
||||
mockMvc.perform(get("/api/library/picker"))
|
||||
.andExpect(jsonPath("$.games[*].appid", containsInAnyOrder(10)));
|
||||
mockMvc.perform(get("/api/library/picker").param("category", "RANDOM"))
|
||||
.andExpect(jsonPath("$.categories[0].games[*].appid", containsInAnyOrder(10)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -292,7 +362,7 @@ class LibraryControllerPickerTest extends AbstractMockedLibraryServicesTest {
|
||||
userGameRepository.save(new UserGame(2L, 99L, 0, Instant.now(), Instant.now()));
|
||||
|
||||
mockMvc.perform(get("/api/library/picker"))
|
||||
.andExpect(jsonPath("$.games", hasSize(0)));
|
||||
.andExpect(jsonPath("$.categories", hasSize(0)));
|
||||
}
|
||||
|
||||
// The dev profile's user has a Steam ID.
|
||||
@@ -310,44 +380,13 @@ class LibraryControllerPickerTest extends AbstractMockedLibraryServicesTest {
|
||||
game(20L, GameStatus.BACKLOG, null, null);
|
||||
gameReleaseRepository.upsert(20L, berlinToday.plusDays(1), false, Instant.now());
|
||||
|
||||
mockMvc.perform(get("/api/library/picker"))
|
||||
.andExpect(jsonPath("$.games[*].appid", containsInAnyOrder(10)));
|
||||
mockMvc.perform(get("/api/library/picker").param("category", "RANDOM"))
|
||||
.andExpect(jsonPath("$.categories[0].games[*].appid", containsInAnyOrder(10)));
|
||||
|
||||
gameReleaseRepository.upsert(20L, berlinToday, false, Instant.now());
|
||||
|
||||
mockMvc.perform(get("/api/library/picker"))
|
||||
.andExpect(jsonPath("$.games[*].appid", containsInAnyOrder(10, 20)));
|
||||
}
|
||||
|
||||
private void putOrder(Long... appids) throws Exception {
|
||||
mockMvc.perform(put("/api/library/backlog").header(ClientHeaderFilter.CLIENT_HEADER, "test")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("{\"appids\":" + Arrays.toString(appids) + "}"))
|
||||
.andExpect(status().isNoContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void picker_FourSlots_NextUpComesFromTopFiveWithReason() throws Exception {
|
||||
for (long appid = 1; appid <= 7; appid++) {
|
||||
game(appid, GameStatus.BACKLOG, null, null);
|
||||
}
|
||||
putOrder(7L, 6L, 5L, 4L, 3L, 2L, 1L);
|
||||
|
||||
mockMvc.perform(get("/api/library/picker").param("count", "4"))
|
||||
.andExpect(jsonPath("$.games", hasSize(4)))
|
||||
.andExpect(jsonPath("$.games[3].reason.source").value("NEXT_UP"))
|
||||
.andExpect(jsonPath("$.games[3].appid", anyOf(is(7), is(6), is(5), is(4), is(3))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void picker_NextUp_SkipsExcludedAndFilteredGames() throws Exception {
|
||||
for (long appid = 1; appid <= 12; appid++) {
|
||||
game(appid, GameStatus.BACKLOG, null, 60);
|
||||
}
|
||||
|
||||
mockMvc.perform(get("/api/library/picker").param("count", "4").param("exclude", "1", "2", "3", "4", "5"))
|
||||
.andExpect(jsonPath("$.games[3].reason.source").value("NEXT_UP"))
|
||||
.andExpect(jsonPath("$.games[3].appid", anyOf(is(6), is(7), is(8), is(9), is(10))));
|
||||
mockMvc.perform(get("/api/library/picker").param("category", "RANDOM"))
|
||||
.andExpect(jsonPath("$.categories[0].games[*].appid", containsInAnyOrder(10, 20)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -357,8 +396,8 @@ class LibraryControllerPickerTest extends AbstractMockedLibraryServicesTest {
|
||||
game(20L, GameStatus.BACKLOG, null, null);
|
||||
gameReleaseRepository.upsert(10L, berlinToday.plusDays(1), false, Instant.now());
|
||||
|
||||
mockMvc.perform(get("/api/library/picker").param("count", "4"))
|
||||
.andExpect(jsonPath("$.games", hasSize(1)))
|
||||
.andExpect(jsonPath("$.games[0].appid").value(20));
|
||||
mockMvc.perform(get("/api/library/picker").param("category", "NEXT_UP"))
|
||||
.andExpect(jsonPath("$.categories[0].games", hasSize(1)))
|
||||
.andExpect(jsonPath("$.categories[0].games[0].appid").value(20));
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package core_service.modules.library.domain;
|
||||
|
||||
import core_service.modules.library.domain.PickMix.Pick;
|
||||
import core_service.shared.test.AbstractUnitTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static core_service.modules.library.domain.PickSource.LONGEST;
|
||||
import static core_service.modules.library.domain.PickSource.NEXT_UP;
|
||||
import static core_service.modules.library.domain.PickSource.RANDOM;
|
||||
import static core_service.modules.library.domain.PickSource.SIMILAR;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class PickMixTest extends AbstractUnitTest {
|
||||
|
||||
private static UserGame game(long appid) {
|
||||
return new UserGame(1L, appid, 0, Instant.now(), Instant.now());
|
||||
}
|
||||
|
||||
private static List<UserGame> games(long... appids) {
|
||||
return Arrays.stream(appids).mapToObj(PickMixTest::game).toList();
|
||||
}
|
||||
|
||||
private static List<PickSource> sources(List<Pick> picks) {
|
||||
return picks.stream().map(Pick::source).toList();
|
||||
}
|
||||
|
||||
private static List<Long> appids(List<Pick> picks) {
|
||||
return picks.stream().map(p -> p.game().getAppid()).toList();
|
||||
}
|
||||
|
||||
@Test
|
||||
void mix_ThreeSources_OneGameEachInSlotOrder() {
|
||||
List<Pick> picks = PickMix.mix(games(1), games(2), games(3), List.of(), 3);
|
||||
|
||||
assertEquals(List.of(SIMILAR, LONGEST, RANDOM), sources(picks));
|
||||
assertEquals(List.of(1L, 2L, 3L), appids(picks));
|
||||
}
|
||||
|
||||
@Test
|
||||
void mix_MoreSlotsThanSources_RotationContinues() {
|
||||
List<Pick> picks = PickMix.mix(games(1, 5, 9), games(2, 6), games(3, 7), games(4, 8), 9);
|
||||
|
||||
assertEquals(List.of(SIMILAR, LONGEST, RANDOM, NEXT_UP, SIMILAR, LONGEST, RANDOM, NEXT_UP, SIMILAR),
|
||||
sources(picks));
|
||||
assertEquals(List.of(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L), appids(picks));
|
||||
}
|
||||
|
||||
@Test
|
||||
void mix_SameGameFirstInAllLists_AppearsOnce() {
|
||||
List<Pick> picks = PickMix.mix(games(1, 4), games(1, 5), games(1, 6), List.of(), 3);
|
||||
|
||||
assertEquals(List.of(1L, 5L, 6L), appids(picks));
|
||||
assertEquals(List.of(SIMILAR, LONGEST, RANDOM), sources(picks));
|
||||
}
|
||||
|
||||
@Test
|
||||
void mix_EmptySimilar_SlotComesFromRandom() {
|
||||
List<Pick> picks = PickMix.mix(List.of(), games(2), games(3, 4), List.of(), 3);
|
||||
|
||||
assertEquals(List.of(RANDOM, LONGEST, RANDOM), sources(picks));
|
||||
assertEquals(List.of(3L, 2L, 4L), appids(picks));
|
||||
}
|
||||
|
||||
@Test
|
||||
void mix_ListsShorterThanCount_ReturnsAllDistinctGames() {
|
||||
List<Pick> picks = PickMix.mix(games(1), games(1, 2), games(2, 3), List.of(), 10);
|
||||
|
||||
assertEquals(List.of(1L, 2L, 3L), appids(picks).stream().sorted().toList());
|
||||
assertEquals(3, picks.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void mix_AllEmpty_ReturnsNothing() {
|
||||
assertTrue(PickMix.mix(List.of(), List.of(), List.of(), List.of(), 3).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void mix_FourSources_NextUpIsTheFourthSlot() {
|
||||
List<Pick> picks = PickMix.mix(games(1), games(2), games(3), games(4), 4);
|
||||
|
||||
assertEquals(List.of(SIMILAR, LONGEST, RANDOM, NEXT_UP), sources(picks));
|
||||
assertEquals(List.of(1L, 2L, 3L, 4L), appids(picks));
|
||||
}
|
||||
|
||||
@Test
|
||||
void mix_EmptyNextUp_SlotComesFromRandom() {
|
||||
List<Pick> picks = PickMix.mix(games(1), games(2), games(3, 4), List.of(), 4);
|
||||
|
||||
assertEquals(List.of(SIMILAR, LONGEST, RANDOM, RANDOM), sources(picks));
|
||||
assertEquals(List.of(1L, 2L, 3L, 4L), appids(picks));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user