Public Access
refactor(library): own IgdbGame record, one owned rule, layer rules
Stored import candidates keep their JSON shape, so READY imports from before still read.
This commit is contained in:
1 parent
1abc03d2c1
commit
c1472c7d59
9 files changed
+126
-51
No files matched your search
@@ -1,6 +1,7 @@
|
||||
package core_service.architecture;
|
||||
|
||||
import core_service.shared.test.AbstractArchitectureTest;
|
||||
import com.tngtech.archunit.base.DescribedPredicate;
|
||||
import com.tngtech.archunit.core.domain.JavaClass;
|
||||
import com.tngtech.archunit.core.importer.ImportOption;
|
||||
import com.tngtech.archunit.junit.AnalyzeClasses;
|
||||
@@ -59,6 +60,33 @@ class ArchitectureTest extends AbstractArchitectureTest {
|
||||
.should().dependOnClassesThat().resideInAPackage("..api..")
|
||||
.because("Persistence layer must be independent of the API layer.");
|
||||
|
||||
@ArchTest
|
||||
static final ArchRule domain_should_not_depend_on_other_layers = noClasses()
|
||||
.that().resideInAPackage("core_service.modules..domain..")
|
||||
.should().dependOnClassesThat().resideInAnyPackage("..api..", "..service..", "..repo..",
|
||||
"core_service.modules.steam_adapter..", "core_service.modules.igdb_adapter..",
|
||||
"core_service.modules.speedrun_adapter..")
|
||||
.because("Entities and stored shapes must not change when a controller, service or external API changes.");
|
||||
|
||||
// The listed services build their module's response DTOs; the list only gets shorter.
|
||||
@ArchTest
|
||||
static final ArchRule services_should_not_depend_on_apis = noClasses()
|
||||
.that().resideInAPackage("core_service.modules..service..")
|
||||
.and().doNotHaveFullyQualifiedName("core_service.modules.library.service.BacklogService")
|
||||
.and().doNotHaveFullyQualifiedName("core_service.modules.library.service.GameDetailService")
|
||||
.and().doNotHaveFullyQualifiedName("core_service.modules.library.service.GameImportService")
|
||||
.and().doNotHaveFullyQualifiedName("core_service.modules.library.service.LibraryQueryService")
|
||||
.and().doNotHaveFullyQualifiedName("core_service.modules.library.service.ManualGameService")
|
||||
.and().doNotHaveFullyQualifiedName("core_service.modules.library.service.SteamProfileService")
|
||||
.and().doNotHaveFullyQualifiedName("core_service.modules.library.service.UserStatsService")
|
||||
.and().doNotHaveFullyQualifiedName("core_service.modules.stats.service.PublicStatsService")
|
||||
.should().dependOnClassesThat(JavaClass.Predicates.resideInAPackage("..api..")
|
||||
.and(DescribedPredicate.not(JavaClass.Predicates.resideInAnyPackage(
|
||||
"core_service.modules.steam_adapter.api.dto.response..",
|
||||
"core_service.modules.igdb_adapter.api.dto.response..",
|
||||
"core_service.modules.speedrun_adapter.api.dto.response.."))))
|
||||
.because("Services must not depend on the HTTP layer; the adapters' response DTOs are the external APIs' shapes.");
|
||||
|
||||
@ArchTest
|
||||
static final ArchRule repos_should_be_interfaces_named_repository = classes()
|
||||
.that().resideInAPackage("..repo..")
|
||||
|
||||
@@ -146,6 +146,23 @@ class GameImportControllerTest extends AbstractIntegrationTest {
|
||||
USER_ID, appid);
|
||||
}
|
||||
|
||||
@Test
|
||||
void get_ReadyImportStoredBeforeIgdbGame_ReadsItsCandidates() throws Exception {
|
||||
long id = insertImport(USER_ID, "READY", "total = 1");
|
||||
// The JSON the import wrote with IgdbApiService.Game, before the own record IgdbGame.
|
||||
jdbc.update("insert into game_import_row (import_id, line_no, title, matched_at, exact, candidates) "
|
||||
+ "values (?, 1, 'Zelda', now(), true, cast(? as jsonb))", id,
|
||||
"[{\"igdbId\":119388,\"name\":\"Zelda: Tears of the Kingdom\",\"year\":2023,"
|
||||
+ "\"imageUrl\":null,\"platforms\":[\"Switch\"],\"steamAppid\":null}]");
|
||||
|
||||
mockMvc.perform(get("/api/library/imports/" + id))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.rows[0].candidates[0].igdbId").value(119388))
|
||||
.andExpect(jsonPath("$.rows[0].candidates[0].name").value("Zelda: Tears of the Kingdom"))
|
||||
.andExpect(jsonPath("$.rows[0].candidates[0].year").value(2023))
|
||||
.andExpect(jsonPath("$.rows[0].candidates[0].platforms[0]").value("Switch"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void create_MatchesInTheBackground_SameNameFirstAndMarksGamesInTheLibrary() throws Exception {
|
||||
jdbc.update("insert into steam_game (appid, name) values (1245620, 'ELDEN RING') on conflict do nothing");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package core_service.modules.library.service;
|
||||
|
||||
import core_service.modules.igdb_adapter.service.IgdbApiService;
|
||||
import core_service.modules.library.domain.IgdbGame;
|
||||
import core_service.modules.library.domain.GameImport;
|
||||
import core_service.modules.library.repo.GameImportRepository;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -20,8 +20,8 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
class GameImportWorkerTest {
|
||||
|
||||
private static IgdbApiService.Game game(long igdbId, String name, String... platforms) {
|
||||
return new IgdbApiService.Game(igdbId, name, null, null, List.of(platforms), null);
|
||||
private static IgdbGame game(long igdbId, String name, String... platforms) {
|
||||
return new IgdbGame(igdbId, name, null, null, List.of(platforms), null);
|
||||
}
|
||||
|
||||
private static GameImport.Line line(String title, String platform) {
|
||||
@@ -37,12 +37,12 @@ class GameImportWorkerTest {
|
||||
|
||||
@Test
|
||||
void rank_SameNameFirst_ThenTheLinesPlatform_ThenIgdbsOrder_AtMostFive() {
|
||||
List<IgdbApiService.Game> found = List.of(game(1, "Doom Eternal", "PC"), game(2, "DOOM", "PC"),
|
||||
List<IgdbGame> found = List.of(game(1, "Doom Eternal", "PC"), game(2, "DOOM", "PC"),
|
||||
game(3, "Doom", "Switch"), game(4, "Doom 3"), game(5, "Doom 64"), game(6, "Doom II"));
|
||||
|
||||
assertThat(GameImportWorker.rank(line("Doom", "switch"), found)).extracting(IgdbApiService.Game::igdbId)
|
||||
assertThat(GameImportWorker.rank(line("Doom", "switch"), found)).extracting(IgdbGame::igdbId)
|
||||
.containsExactly(3L, 2L, 1L, 4L, 5L);
|
||||
assertThat(GameImportWorker.rank(line("Doom", null), found)).extracting(IgdbApiService.Game::igdbId)
|
||||
assertThat(GameImportWorker.rank(line("Doom", null), found)).extracting(IgdbGame::igdbId)
|
||||
.startsWith(2L, 3L);
|
||||
assertThat(GameImportWorker.rank(line("x", null), IntStream.range(0, 9)
|
||||
.mapToObj(i -> game(i, "Game " + i)).toList())).hasSize(GameImportWorker.MAX_CANDIDATES);
|
||||
@@ -56,7 +56,7 @@ class GameImportWorkerTest {
|
||||
.thenReturn(Optional.of(7L), Optional.empty());
|
||||
when(repository.findStalled(any())).thenReturn(Map.of());
|
||||
GameImportWorker worker = new GameImportWorker(repository, mock(ManualGameService.class),
|
||||
mock(IgdbApiService.class), mock(TimeToBeatService.class), mock(MoodService.class), 5);
|
||||
mock(TimeToBeatService.class), mock(MoodService.class), 5);
|
||||
try {
|
||||
worker.wakeMatching();
|
||||
verify(repository, timeout(2000)).findNext(GameImportWorker.MATCHING_QUEUE);
|
||||
|
||||
Reference in new issue
Block a user