Public Access
feat(library): soft delete removed games and add restore
Removed games could not come back (#39). Remove now sets removed_at and restore clears it. V46 brings back PSN games removed before.
This commit is contained in:
1 parent
696e06e7fe
commit
4d49b0f023
16 files changed
+620
-20
No files matched your search
@@ -0,0 +1,52 @@
|
||||
package core_service.modules.follow.repo;
|
||||
|
||||
import core_service.modules.follow.domain.Digest;
|
||||
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 org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@Import(FollowDigestRepository.class)
|
||||
class FollowDigestRepositoryTest extends AbstractPersistenceTest {
|
||||
|
||||
private static final long USER = 1L;
|
||||
private static final long LISTED = 10L;
|
||||
private static final long REMOVED = 11L;
|
||||
|
||||
@Autowired
|
||||
private FollowDigestRepository repository;
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbc;
|
||||
|
||||
@BeforeEach
|
||||
void seed() {
|
||||
jdbc.update("insert into sync_result (user_id, sync_started_at, started_by, imported_games) values (?, now(), 'AUTO', 0)", USER);
|
||||
for (long appid : new long[]{LISTED, REMOVED}) {
|
||||
jdbc.update("insert into steam_game (appid, name) values (?, 'Game')", appid);
|
||||
jdbc.update("insert into user_game (user_id, appid, status, source, platform, status_set_by_user) "
|
||||
+ "values (?, ?, 'BACKLOG', 'MANUAL', 'PC', true)", USER, appid);
|
||||
jdbc.update("insert into sync_change (user_id, kind, appid, amount) values (?, 'ADDED', ?, null)", USER, appid);
|
||||
jdbc.update("insert into sync_change (user_id, kind, appid, amount) values (?, 'ACHIEVEMENTS', ?, 3)", USER, appid);
|
||||
jdbc.update("insert into sync_change (user_id, kind, appid, from_status, to_status) "
|
||||
+ "values (?, 'STATUS', ?, 'BACKLOG', 'PLAYING')", USER, appid);
|
||||
}
|
||||
jdbc.update("update user_game set removed_at = now() where appid = ?", REMOVED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void countSync_SkipsRemovedGames() {
|
||||
assertThat(repository.countSync(USER)).isEqualTo(new Digest(1, 3, 1, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void countHand_SkipsRemovedGames() {
|
||||
Digest digest = repository.countHand(USER, 0, Long.MAX_VALUE);
|
||||
|
||||
assertThat(digest.newGames()).isEqualTo(1);
|
||||
}
|
||||
}
|
||||
@@ -313,8 +313,9 @@ class ManualGameControllerTest extends AbstractMockedAdaptersTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void remove_DeletesAGameAddedByHandButNoSteamGame() throws Exception {
|
||||
void remove_KeepsTheRowWithItsNotesAndHidesItButRemovesNoSteamGame() throws Exception {
|
||||
add(119388, "Switch");
|
||||
jdbc.update("update user_game set notes = 'keep me' where appid = -119388");
|
||||
steamGameRepository.save(new SteamGame(220L, "Half-Life 2", null, Instant.now()));
|
||||
userGameRepository.save(new UserGame(USER_ID, 220L, 300, Instant.now(), Instant.now()));
|
||||
|
||||
@@ -325,10 +326,147 @@ class ManualGameControllerTest extends AbstractMockedAdaptersTest {
|
||||
mockMvc.perform(delete("/api/library/games/-1").header(ClientHeaderFilter.CLIENT_HEADER, "test"))
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
assertTrue(userGameRepository.findById(new UserGameId(USER_ID, -119388L)).isEmpty());
|
||||
UserGame removed = stored(-119388);
|
||||
assertTrue(removed.getRemovedAt() != null);
|
||||
assertEquals("keep me", removed.getNotes());
|
||||
mockMvc.perform(get("/api/library/games/-119388")).andExpect(status().isNotFound());
|
||||
mockMvc.perform(get("/api/library/games").param("q", "zelda"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.content").isEmpty());
|
||||
assertNull(stored(220).getRemovedAt());
|
||||
}
|
||||
|
||||
private void remove(long appid) throws Exception {
|
||||
mockMvc.perform(delete("/api/library/games/" + appid).header(ClientHeaderFilter.CLIENT_HEADER, "test"))
|
||||
.andExpect(status().isNoContent());
|
||||
}
|
||||
|
||||
private org.springframework.test.web.servlet.ResultActions restore(long appid) throws Exception {
|
||||
return mockMvc.perform(post("/api/library/games/" + appid + "/restore")
|
||||
.header(ClientHeaderFilter.CLIENT_HEADER, "test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void removedGames_ListsOnlyTheCallersManualAndPsnRows() throws Exception {
|
||||
add(119388, "Switch");
|
||||
add(119133, "PS5");
|
||||
steamGameRepository.save(new SteamGame(220L, "Half-Life 2", null, Instant.now()));
|
||||
steamGameRepository.save(new SteamGame(221L, "Other", null, Instant.now()));
|
||||
UserGame syncRemoved = new UserGame(USER_ID, 220L, 300, Instant.now(), Instant.now());
|
||||
syncRemoved.setRemovedAt(Instant.now());
|
||||
userGameRepository.save(syncRemoved);
|
||||
UserGame other = new UserGame(2L, 221L, 300, Instant.now(), Instant.now());
|
||||
other.setSource(GameSource.MANUAL);
|
||||
other.setRemovedAt(Instant.now());
|
||||
userGameRepository.save(other);
|
||||
remove(-119388);
|
||||
|
||||
mockMvc.perform(get("/api/library/removed-games"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.results.length()").value(1))
|
||||
.andExpect(jsonPath("$.results[0].appid").value(-119388))
|
||||
.andExpect(jsonPath("$.results[0].name").value("Zelda: Tears of the Kingdom"))
|
||||
.andExpect(jsonPath("$.results[0].source").value("MANUAL"))
|
||||
.andExpect(jsonPath("$.results[0].platform").value("Switch"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void restore_BringsBackStatusRatingNotesAndListsTheGame() throws Exception {
|
||||
add(119388, "Switch");
|
||||
jdbc.update("update user_game set status = 'FINISHED', rating = 5, notes = 'loved it' where appid = -119388");
|
||||
remove(-119388);
|
||||
|
||||
restore(-119388).andExpect(status().isNoContent());
|
||||
|
||||
mockMvc.perform(get("/api/library/games/-119388"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.status").value("FINISHED"))
|
||||
.andExpect(jsonPath("$.rating").value(5))
|
||||
.andExpect(jsonPath("$.notes").value("loved it"));
|
||||
mockMvc.perform(get("/api/library/removed-games")).andExpect(jsonPath("$.results").isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void restore_Twice_Is204AndKeepsOneRow() throws Exception {
|
||||
add(119388, "Switch");
|
||||
remove(-119388);
|
||||
|
||||
restore(-119388).andExpect(status().isNoContent());
|
||||
restore(-119388).andExpect(status().isNoContent());
|
||||
|
||||
assertEquals(1, userGameRepository.count());
|
||||
}
|
||||
|
||||
@Test
|
||||
void restore_GameOfAnotherUser_Is404() throws Exception {
|
||||
steamGameRepository.save(new SteamGame(221L, "Other", null, Instant.now()));
|
||||
UserGame other = new UserGame(2L, 221L, 300, Instant.now(), Instant.now());
|
||||
other.setSource(GameSource.MANUAL);
|
||||
other.setRemovedAt(Instant.now());
|
||||
userGameRepository.save(other);
|
||||
|
||||
restore(221).andExpect(status().isNotFound());
|
||||
|
||||
assertTrue(userGameRepository.findById(new UserGameId(2L, 221L)).orElseThrow().getRemovedAt() != null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void restore_SyncRemovedSteamGame_Is409() throws Exception {
|
||||
steamGameRepository.save(new SteamGame(220L, "Half-Life 2", null, Instant.now()));
|
||||
UserGame game = new UserGame(USER_ID, 220L, 300, Instant.now(), Instant.now());
|
||||
game.setRemovedAt(Instant.now());
|
||||
userGameRepository.save(game);
|
||||
|
||||
restore(220).andExpect(status().isConflict());
|
||||
|
||||
assertTrue(stored(220).getRemovedAt() != null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void restore_PsnGame_TakesTheStoredTitlePlaytime() throws Exception {
|
||||
add(119388, "PS5");
|
||||
jdbc.update("update user_game set source = 'PSN' where appid = -119388");
|
||||
jdbc.update("""
|
||||
insert into user_psn_title (user_id, psn_key, state, appid, platform, playtime_minutes, last_played_at)
|
||||
values (?, 'c1', 'LINKED', -119388, 'PS5', 90, now()), (?, 't1', 'LINKED', -119388, 'PS4', 30, null)""",
|
||||
USER_ID, USER_ID);
|
||||
remove(-119388);
|
||||
|
||||
restore(-119388).andExpect(status().isNoContent());
|
||||
|
||||
UserGame game = stored(-119388);
|
||||
assertNull(game.getRemovedAt());
|
||||
assertEquals(120, game.getPlaytimeMinutes());
|
||||
assertTrue(game.getLastPlayedAt() != null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void restore_ManualGame_KeepsItsTypedHours() throws Exception {
|
||||
add(119388, "Switch");
|
||||
remove(-119388);
|
||||
|
||||
restore(-119388).andExpect(status().isNoContent());
|
||||
|
||||
assertEquals(600, stored(-119388).getPlaytimeMinutes());
|
||||
}
|
||||
|
||||
@Test
|
||||
void restore_DoesNotPayPointsTwice() throws Exception {
|
||||
add(119388, "Switch");
|
||||
jdbc.update("update user_game set status = 'FINISHED' where appid = -119388");
|
||||
jdbc.update("insert into reward_ledger (user_id, kind, ref, points, event_at) values (?, 'FINISHED', '-119388', 50, now())",
|
||||
USER_ID);
|
||||
int logBefore = jdbc.queryForObject("select count(*) from user_game_status_change where appid = -119388", Integer.class);
|
||||
remove(-119388);
|
||||
|
||||
restore(-119388).andExpect(status().isNoContent());
|
||||
|
||||
assertEquals(1, jdbc.queryForObject("select count(*) from reward_ledger where user_id = ? and kind = 'FINISHED' "
|
||||
+ "and ref = '-119388'", Integer.class, USER_ID));
|
||||
assertEquals(logBefore, jdbc.queryForObject("select count(*) from user_game_status_change where appid = -119388",
|
||||
Integer.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void gameAddedByHand_ShowsUpInTheLibraryAndItsDetail() throws Exception {
|
||||
add(119388, "Switch");
|
||||
|
||||
@@ -310,7 +310,8 @@ class PsnAccountFlowTest extends AbstractMockedAdaptersTest {
|
||||
|
||||
worker.runSyncNow(1L, null, SyncTrigger.AUTO);
|
||||
|
||||
assertThat(jdbc.queryForObject("select count(*) from user_game where appid = -301", Integer.class)).isZero();
|
||||
assertThat(jdbc.queryForObject("select count(*) from user_game where appid = -301 and removed_at is null",
|
||||
Integer.class)).isZero();
|
||||
assertThat(jdbc.queryForObject("select count(*) from game_import", Integer.class)).isEqualTo(1);
|
||||
}
|
||||
|
||||
@@ -414,14 +415,15 @@ class PsnAccountFlowTest extends AbstractMockedAdaptersTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void psnRow_CanBeDeletedByHand() throws Exception {
|
||||
void psnRow_CanBeRemovedByHand() throws Exception {
|
||||
jdbc.update("insert into steam_game (appid, name) values (-301, 'X') on conflict do nothing");
|
||||
jdbc.update("insert into user_game (user_id, appid, playtime_minutes, first_seen_at, last_seen_at, source, platform, status, "
|
||||
+ "status_set_by_user) values (1, -301, 0, now(), now(), 'PSN', 'PS5', 'BACKLOG', true)");
|
||||
|
||||
mockMvc.perform(delete("/api/library/games/-301").header(ClientHeaderFilter.CLIENT_HEADER, "test"))
|
||||
.andExpect(status().isNoContent());
|
||||
assertThat(jdbc.queryForObject("select count(*) from user_game where appid = -301", Integer.class)).isZero();
|
||||
assertThat(jdbc.queryForObject("select count(*) from user_game where appid = -301 and removed_at is not null",
|
||||
Integer.class)).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
package core_service.modules.library.repo;
|
||||
|
||||
import core_service.modules.follow.repo.FollowDigestRepository;
|
||||
import core_service.shared.test.AbstractPersistenceTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.jdbc.core.ConnectionCallback;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.init.ScriptUtils;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* V46 runs on a database that already ran it once: the test seeds the old shapes and runs the file again.
|
||||
*/
|
||||
class RemovedPsnGamesMigrationTest extends AbstractPersistenceTest {
|
||||
|
||||
private static final long USER = 7L;
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbc;
|
||||
|
||||
private void catalog(long appid) {
|
||||
jdbc.update("insert into steam_game (appid, name) values (?, 'Game') on conflict do nothing", appid);
|
||||
}
|
||||
|
||||
private void title(long user, String key, String state, Long appid, Integer minutes) {
|
||||
jdbc.update("""
|
||||
insert into user_psn_title (user_id, psn_key, state, appid, platform, playtime_minutes, last_played_at)
|
||||
values (?, ?, ?, ?, 'PS5', ?, case when ?::int is null then null else now() end)""",
|
||||
user, key, state, appid, minutes, minutes);
|
||||
}
|
||||
|
||||
private void row(long user, long appid, String source, String status) {
|
||||
catalog(appid);
|
||||
jdbc.update("insert into user_game (user_id, appid, status, source, platform, playtime_minutes) "
|
||||
+ "values (?, ?, ?, ?, 'Switch', 5)", user, appid, status, source);
|
||||
}
|
||||
|
||||
private void migrate() throws SQLException {
|
||||
// The JdbcTemplate connection is the one of the test transaction; a pool connection would not see the seed.
|
||||
jdbc.execute((ConnectionCallback<Void>) connection -> {
|
||||
ScriptUtils.executeSqlScript(connection, new ClassPathResource("db/migration/V46__Removed_Psn_Games.sql"));
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> rows(long user) {
|
||||
return jdbc.queryForList("select * from user_game where user_id = ? order by appid", user);
|
||||
}
|
||||
|
||||
@Test
|
||||
void orphanTitles_BecomeOneRemovedPsnRowPerGame() throws SQLException {
|
||||
catalog(-10);
|
||||
title(USER, "c1", "LINKED", -10L, 30);
|
||||
title(USER, "t1", "LINKED", -10L, 45);
|
||||
|
||||
migrate();
|
||||
|
||||
assertThat(rows(USER)).singleElement().satisfies(game -> {
|
||||
assertThat(game.get("source")).isEqualTo("PSN");
|
||||
assertThat(game.get("removed_at")).isNotNull();
|
||||
assertThat(game.get("playtime_minutes")).isEqualTo(75);
|
||||
assertThat(game.get("status_set_by_user")).isEqualTo(false);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void orphanTitleWithPlaytimeIsPlaying_WithoutIsBacklog() throws SQLException {
|
||||
catalog(-10);
|
||||
catalog(-11);
|
||||
title(USER, "c1", "LINKED", -10L, 30);
|
||||
title(USER, "c2", "LINKED", -11L, 0);
|
||||
|
||||
migrate();
|
||||
|
||||
assertThat(jdbc.queryForObject("select status from user_game where appid = -10", String.class)).isEqualTo("PLAYING");
|
||||
assertThat(jdbc.queryForObject("select status from user_game where appid = -11", String.class)).isEqualTo("BACKLOG");
|
||||
}
|
||||
|
||||
@Test
|
||||
void aliasedTitle_UsesTheKeptKey() throws SQLException {
|
||||
catalog(-10);
|
||||
catalog(-20);
|
||||
jdbc.update("insert into game_alias (from_appid, to_appid) values (-10, -20)");
|
||||
title(USER, "c1", "LINKED", -10L, 30);
|
||||
|
||||
migrate();
|
||||
|
||||
assertThat(rows(USER)).extracting(game -> game.get("appid")).containsExactly(-20L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletedUser_IsSkipped() throws SQLException {
|
||||
catalog(-10);
|
||||
jdbc.update("insert into deleted_user (user_id) values (?)", USER);
|
||||
title(USER, "c1", "LINKED", -10L, 30);
|
||||
|
||||
migrate();
|
||||
|
||||
assertThat(rows(USER)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void titlesThatAreNotLinked_OrHaveNoCatalogEntry_AddNothing() throws SQLException {
|
||||
catalog(-10);
|
||||
title(USER, "c1", "DISMISSED", null, 30);
|
||||
title(USER, "c2", "STEAM_OWNED", -10L, 30);
|
||||
title(USER, "c3", "LINKED", -99L, 30);
|
||||
|
||||
migrate();
|
||||
|
||||
assertThat(rows(USER)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void existingPsnOrSteamRow_IsUntouched() throws SQLException {
|
||||
row(USER, -10, "PSN", "FINISHED");
|
||||
row(USER, -11, "STEAM", "BACKLOG");
|
||||
title(USER, "c1", "LINKED", -10L, 30);
|
||||
title(USER, "c2", "LINKED", -11L, 30);
|
||||
|
||||
migrate();
|
||||
|
||||
assertThat(rows(USER)).hasSize(2).allSatisfy(game -> {
|
||||
assertThat(game.get("removed_at")).isNull();
|
||||
assertThat(game.get("playtime_minutes")).isEqualTo(5);
|
||||
});
|
||||
assertThat(jdbc.queryForObject("select status from user_game where appid = -10", String.class)).isEqualTo("FINISHED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void removedRow_GetsNoBacklogOrder() throws SQLException {
|
||||
catalog(-10);
|
||||
title(USER, "c1", "LINKED", -10L, 0);
|
||||
|
||||
migrate();
|
||||
|
||||
assertThat(jdbc.queryForObject("select count(*) from backlog_order where user_id = ?", Integer.class, USER)).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
void reAddedManualRow_BecomesPsnAndKeepsUserData() throws SQLException {
|
||||
row(USER, -10, "MANUAL", "FINISHED");
|
||||
row(USER, -11, "MANUAL", "BACKLOG");
|
||||
jdbc.update("update user_game set rating = 4, notes = 'great', priority = 'HIGH' where appid = -10");
|
||||
jdbc.update("insert into user_game_split (user_id, appid) values (?, -10)", USER);
|
||||
title(USER, "c1", "LINKED", -10L, 120);
|
||||
int logBefore = jdbc.queryForObject("select count(*) from user_game_status_change where appid = -10", Integer.class);
|
||||
|
||||
migrate();
|
||||
|
||||
Map<String, Object> game = jdbc.queryForMap("select * from user_game where user_id = ? and appid = -10", USER);
|
||||
assertThat(game).containsEntry("source", "PSN").containsEntry("playtime_minutes", 120)
|
||||
.containsEntry("status", "FINISHED").containsEntry("rating", 4).containsEntry("notes", "great")
|
||||
.containsEntry("priority", "HIGH").containsEntry("platform", "Switch");
|
||||
assertThat(game.get("last_played_at")).isNotNull();
|
||||
assertThat(rows(USER)).hasSize(2);
|
||||
assertThat(jdbc.queryForObject("select count(*) from user_game_split where appid = -10", Integer.class)).isEqualTo(1);
|
||||
assertThat(jdbc.queryForObject("select count(*) from user_game_status_change where appid = -10", Integer.class))
|
||||
.isEqualTo(logBefore);
|
||||
assertThat(jdbc.queryForObject("select source from user_game where appid = -11", String.class)).isEqualTo("MANUAL");
|
||||
}
|
||||
|
||||
@Test
|
||||
void reAddedManualRowOnAliasedKey_BecomesPsn() throws SQLException {
|
||||
row(USER, -20, "MANUAL", "BACKLOG");
|
||||
jdbc.update("insert into game_alias (from_appid, to_appid) values (-10, -20)");
|
||||
title(USER, "c1", "LINKED", -10L, 60);
|
||||
|
||||
migrate();
|
||||
|
||||
assertThat(rows(USER)).singleElement().satisfies(game -> {
|
||||
assertThat(game.get("source")).isEqualTo("PSN");
|
||||
assertThat(game.get("playtime_minutes")).isEqualTo(60);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void manualRowOfAnotherUserOrWithoutLinkedTitle_IsUntouched() throws SQLException {
|
||||
row(USER, -10, "MANUAL", "BACKLOG");
|
||||
row(USER, -11, "MANUAL", "BACKLOG");
|
||||
row(USER, -12, "MANUAL", "BACKLOG");
|
||||
title(USER + 1, "c1", "LINKED", -10L, 60);
|
||||
title(USER, "c2", "DISMISSED", null, 60);
|
||||
title(USER, "c3", "STEAM_OWNED", -12L, 60);
|
||||
|
||||
migrate();
|
||||
|
||||
assertThat(rows(USER)).extracting(game -> game.get("source")).containsOnly("MANUAL");
|
||||
}
|
||||
|
||||
@Test
|
||||
void runTwice_ChangesNothing() throws SQLException {
|
||||
row(USER, -11, "MANUAL", "BACKLOG");
|
||||
catalog(-10);
|
||||
title(USER, "c1", "LINKED", -10L, 30);
|
||||
title(USER, "c2", "LINKED", -11L, 30);
|
||||
migrate();
|
||||
List<Map<String, Object>> once = rows(USER);
|
||||
|
||||
migrate();
|
||||
|
||||
assertThat(rows(USER)).isEqualTo(once);
|
||||
}
|
||||
|
||||
@Test
|
||||
void migratedOrphan_IsNoNewGameInTheFollowDigest() throws SQLException {
|
||||
catalog(-10);
|
||||
title(USER, "c1", "LINKED", -10L, 30);
|
||||
|
||||
migrate();
|
||||
|
||||
assertThat(new FollowDigestRepository(new NamedParameterJdbcTemplate(jdbc)).countHand(USER, 0, Long.MAX_VALUE).newGames())
|
||||
.isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
void migratedOrphanInASet_KeepsTheAutoStatusOfTheListedSteamRow() throws SQLException {
|
||||
row(USER, 500, "STEAM", "BACKLOG");
|
||||
catalog(-10);
|
||||
jdbc.update("insert into game_link (appid, set_id) values (500, 1), (-10, 1)");
|
||||
title(USER, "c1", "LINKED", -10L, 30);
|
||||
|
||||
migrate();
|
||||
|
||||
assertThat(jdbc.queryForObject("select status_set_by_user from user_game where user_id = ? and appid = 500",
|
||||
Boolean.class, USER)).isFalse();
|
||||
}
|
||||
}
|
||||
@@ -189,6 +189,16 @@ class SyncChangeRepositoryTest extends AbstractPersistenceTest {
|
||||
assertThat(rows()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void find_LeavesOutTheOtherChangesOfARemovedGame() {
|
||||
claim(USER_ID, FIRST_RUN);
|
||||
repository.replace(USER_ID, FIRST_RUN, SyncTrigger.AUTO, 0,
|
||||
List.of(SyncChange.added(HALF_LIFE), SyncChange.added(REMOVED), SyncChange.removed(REMOVED)));
|
||||
|
||||
assertThat(stored().changes()).extracting(e -> e.change())
|
||||
.containsExactlyInAnyOrder(SyncChange.added(HALF_LIFE), SyncChange.removed(REMOVED));
|
||||
}
|
||||
|
||||
@Test
|
||||
void findStatusMovesOfThisTransaction_ReturnsOnlyMovesOfTheSync() {
|
||||
// Same transaction as the seed: the inserts (from null) are in the log with this now() too.
|
||||
|
||||
@@ -2,6 +2,7 @@ package core_service.modules.library.repo;
|
||||
|
||||
import core_service.modules.library.domain.GamePriority;
|
||||
import core_service.modules.library.domain.GameSource;
|
||||
import core_service.modules.library.domain.RemovedGame;
|
||||
import core_service.modules.library.domain.GameTimeToBeat;
|
||||
import core_service.modules.library.domain.GameStatus;
|
||||
import core_service.modules.library.domain.Mood;
|
||||
@@ -93,6 +94,25 @@ class UserGameRepositoryTest extends AbstractPersistenceTest {
|
||||
.map(UserGame::getAppid).getContent();
|
||||
}
|
||||
|
||||
@Test
|
||||
void findUserRemoved_OrdersNewestFirstAndSkipsSteamAndOtherUsers() {
|
||||
Instant now = Instant.now();
|
||||
removed(USER_ID, 220L, GameSource.MANUAL, now.minusSeconds(60));
|
||||
removed(USER_ID, 380L, GameSource.PSN, now);
|
||||
removed(USER_ID, 620L, GameSource.STEAM, now);
|
||||
removed(2L, 220L, GameSource.PSN, now);
|
||||
userGameRepository.flush();
|
||||
entityManager.clear();
|
||||
|
||||
assertEquals(List.of(380L, 220L), userGameRepository.findUserRemoved(USER_ID).stream().map(RemovedGame::appid).toList());
|
||||
}
|
||||
|
||||
private void removed(long userId, long appid, GameSource source, Instant at) {
|
||||
UserGame game = userGameRepository.findById(new UserGameId(userId, appid)).orElseThrow();
|
||||
game.setSource(source);
|
||||
game.setRemovedAt(at);
|
||||
}
|
||||
|
||||
@Test
|
||||
void playFromPileIfUntouched_OnlyUntouchedListedBacklogGames() {
|
||||
userGameRepository.findById(new UserGameId(USER_ID, 220L)).orElseThrow().setStatus(GameStatus.BACKLOG);
|
||||
|
||||
@@ -7,6 +7,7 @@ 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.GameMergeRepository;
|
||||
import core_service.modules.library.repo.PsnTitleRepository;
|
||||
import core_service.modules.library.repo.SteamGameRepository;
|
||||
import core_service.modules.library.repo.UserGameRepository;
|
||||
import core_service.shared.test.AbstractUnitTest;
|
||||
@@ -61,6 +62,9 @@ class ManualGameServiceTest extends AbstractUnitTest {
|
||||
@Mock
|
||||
private ReleaseDateService releaseDateService;
|
||||
|
||||
@Mock
|
||||
private PsnTitleRepository psnTitleRepository;
|
||||
|
||||
@Mock
|
||||
private PlatformTransactionManager transactionManager;
|
||||
|
||||
@@ -69,7 +73,7 @@ class ManualGameServiceTest extends AbstractUnitTest {
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
service = new ManualGameService(igdbApiService, userGameRepository, steamGameRepository, mergeRepository, timeToBeatService,
|
||||
moodService, gameEditionService, gameCreditService, releaseDateService, transactionManager);
|
||||
moodService, gameEditionService, gameCreditService, releaseDateService, psnTitleRepository, transactionManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in new issue
Block a user