Mise en place de la possibilité de supprimer des lores / campagnes d'un seul coup
This commit is contained in:
@@ -1,7 +1,15 @@
|
||||
package com.loremind.application.campaigncontext;
|
||||
|
||||
import com.loremind.domain.campaigncontext.Arc;
|
||||
import com.loremind.domain.campaigncontext.Campaign;
|
||||
import com.loremind.domain.campaigncontext.Chapter;
|
||||
import com.loremind.domain.campaigncontext.Character;
|
||||
import com.loremind.domain.campaigncontext.Scene;
|
||||
import com.loremind.domain.campaigncontext.ports.ArcRepository;
|
||||
import com.loremind.domain.campaigncontext.ports.CampaignRepository;
|
||||
import com.loremind.domain.campaigncontext.ports.ChapterRepository;
|
||||
import com.loremind.domain.campaigncontext.ports.CharacterRepository;
|
||||
import com.loremind.domain.campaigncontext.ports.SceneRepository;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -27,6 +35,14 @@ public class CampaignServiceTest {
|
||||
|
||||
@Mock
|
||||
private CampaignRepository campaignRepository;
|
||||
@Mock
|
||||
private ArcRepository arcRepository;
|
||||
@Mock
|
||||
private ChapterRepository chapterRepository;
|
||||
@Mock
|
||||
private SceneRepository sceneRepository;
|
||||
@Mock
|
||||
private CharacterRepository characterRepository;
|
||||
|
||||
@InjectMocks
|
||||
private CampaignService campaignService;
|
||||
@@ -196,15 +212,75 @@ public class CampaignServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeleteCampaign() {
|
||||
// Arrange
|
||||
doNothing().when(campaignRepository).deleteById("campaign-1");
|
||||
|
||||
void testDeleteCampaign_EmptyCampaign() {
|
||||
// Arrange : aucune dépendance ; Mockito renvoie List.of() par défaut.
|
||||
// Act
|
||||
campaignService.deleteCampaign("campaign-1");
|
||||
|
||||
// Assert
|
||||
verify(campaignRepository, times(1)).deleteById("campaign-1");
|
||||
verify(arcRepository, never()).deleteById(anyString());
|
||||
verify(chapterRepository, never()).deleteById(anyString());
|
||||
verify(sceneRepository, never()).deleteById(anyString());
|
||||
verify(characterRepository, never()).deleteById(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeleteCampaign_CascadesArcsChaptersScenes() {
|
||||
// Arrange : campagne avec 1 arc → 1 chapitre → 2 scènes.
|
||||
Arc arc = Arc.builder().id("arc-1").campaignId("campaign-1").name("Arc 1").build();
|
||||
Chapter chapter = Chapter.builder().id("chap-1").arcId("arc-1").name("Chap 1").build();
|
||||
Scene scene1 = Scene.builder().id("scene-1").chapterId("chap-1").name("Scene 1").build();
|
||||
Scene scene2 = Scene.builder().id("scene-2").chapterId("chap-1").name("Scene 2").build();
|
||||
|
||||
when(arcRepository.findByCampaignId("campaign-1")).thenReturn(List.of(arc));
|
||||
when(chapterRepository.findByArcId("arc-1")).thenReturn(List.of(chapter));
|
||||
when(sceneRepository.findByChapterId("chap-1")).thenReturn(List.of(scene1, scene2));
|
||||
|
||||
// Act
|
||||
campaignService.deleteCampaign("campaign-1");
|
||||
|
||||
// Assert : tout disparaît, dans l'ordre feuilles → racine.
|
||||
verify(sceneRepository).deleteById("scene-1");
|
||||
verify(sceneRepository).deleteById("scene-2");
|
||||
verify(chapterRepository).deleteById("chap-1");
|
||||
verify(arcRepository).deleteById("arc-1");
|
||||
verify(campaignRepository).deleteById("campaign-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeleteCampaign_CascadesCharacters() {
|
||||
Character pc = Character.builder().id("char-1").campaignId("campaign-1").name("Alric").build();
|
||||
when(characterRepository.findByCampaignId("campaign-1")).thenReturn(List.of(pc));
|
||||
|
||||
campaignService.deleteCampaign("campaign-1");
|
||||
|
||||
verify(characterRepository).deleteById("char-1");
|
||||
verify(campaignRepository).deleteById("campaign-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDeletionImpact() {
|
||||
Arc arc = Arc.builder().id("arc-1").campaignId("campaign-1").name("Arc 1").build();
|
||||
Chapter c1 = Chapter.builder().id("chap-1").arcId("arc-1").name("C1").build();
|
||||
Chapter c2 = Chapter.builder().id("chap-2").arcId("arc-1").name("C2").build();
|
||||
Scene s1 = Scene.builder().id("s-1").chapterId("chap-1").name("S1").build();
|
||||
Scene s2 = Scene.builder().id("s-2").chapterId("chap-2").name("S2").build();
|
||||
Scene s3 = Scene.builder().id("s-3").chapterId("chap-2").name("S3").build();
|
||||
Character pc = Character.builder().id("char-1").campaignId("campaign-1").name("Alric").build();
|
||||
|
||||
when(arcRepository.findByCampaignId("campaign-1")).thenReturn(List.of(arc));
|
||||
when(chapterRepository.findByArcId("arc-1")).thenReturn(List.of(c1, c2));
|
||||
when(sceneRepository.findByChapterId("chap-1")).thenReturn(List.of(s1));
|
||||
when(sceneRepository.findByChapterId("chap-2")).thenReturn(List.of(s2, s3));
|
||||
when(characterRepository.findByCampaignId("campaign-1")).thenReturn(List.of(pc));
|
||||
|
||||
CampaignService.DeletionImpact impact = campaignService.getDeletionImpact("campaign-1");
|
||||
|
||||
assertEquals(1, impact.arcs());
|
||||
assertEquals(2, impact.chapters());
|
||||
assertEquals(3, impact.scenes());
|
||||
assertEquals(1, impact.characters());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.loremind.application.lorecontext;
|
||||
|
||||
import com.loremind.domain.lorecontext.LoreNode;
|
||||
import com.loremind.domain.lorecontext.Page;
|
||||
import com.loremind.domain.lorecontext.ports.LoreNodeRepository;
|
||||
import com.loremind.domain.lorecontext.ports.PageRepository;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -15,6 +17,7 @@ import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
@@ -26,6 +29,7 @@ import static org.mockito.Mockito.*;
|
||||
public class LoreNodeServiceTest {
|
||||
|
||||
@Mock private LoreNodeRepository loreNodeRepository;
|
||||
@Mock private PageRepository pageRepository;
|
||||
|
||||
@InjectMocks private LoreNodeService loreNodeService;
|
||||
|
||||
@@ -118,8 +122,66 @@ public class LoreNodeServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDelete() {
|
||||
void testDelete_LeafFolder() {
|
||||
// Aucun descendant, aucune page : seul le dossier est supprimé.
|
||||
loreNodeService.deleteLoreNode("n-1");
|
||||
verify(loreNodeRepository).deleteById("n-1");
|
||||
verify(pageRepository, never()).deleteById(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDelete_CascadesPagesOfRoot() {
|
||||
Page p1 = Page.builder().id("p-1").nodeId("n-1").title("P1").build();
|
||||
Page p2 = Page.builder().id("p-2").nodeId("n-1").title("P2").build();
|
||||
when(pageRepository.findByNodeId("n-1")).thenReturn(List.of(p1, p2));
|
||||
|
||||
loreNodeService.deleteLoreNode("n-1");
|
||||
|
||||
verify(pageRepository).deleteById("p-1");
|
||||
verify(pageRepository).deleteById("p-2");
|
||||
verify(loreNodeRepository).deleteById("n-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDelete_CascadesSubfoldersRecursive() {
|
||||
// n-1 → n-1a → n-1a1 ; chaque feuille a une page.
|
||||
LoreNode mid = LoreNode.builder().id("n-1a").parentId("n-1").loreId("lore-1").name("mid").build();
|
||||
LoreNode leaf = LoreNode.builder().id("n-1a1").parentId("n-1a").loreId("lore-1").name("leaf").build();
|
||||
Page pageOnLeaf = Page.builder().id("p-leaf").nodeId("n-1a1").title("P").build();
|
||||
|
||||
when(loreNodeRepository.findByParentId("n-1")).thenReturn(List.of(mid));
|
||||
when(loreNodeRepository.findByParentId("n-1a")).thenReturn(List.of(leaf));
|
||||
when(pageRepository.findByNodeId("n-1a1")).thenReturn(List.of(pageOnLeaf));
|
||||
|
||||
loreNodeService.deleteLoreNode("n-1");
|
||||
|
||||
// Feuilles d'abord (pages puis dossier leaf), puis mid, puis la racine.
|
||||
verify(pageRepository).deleteById("p-leaf");
|
||||
verify(loreNodeRepository).deleteById("n-1a1");
|
||||
verify(loreNodeRepository).deleteById("n-1a");
|
||||
verify(loreNodeRepository).deleteById("n-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDeletionImpact_CountsSubfoldersAndPages() {
|
||||
LoreNode sub1 = LoreNode.builder().id("s-1").parentId("n-1").loreId("lore-1").name("s1").build();
|
||||
LoreNode sub2 = LoreNode.builder().id("s-2").parentId("n-1").loreId("lore-1").name("s2").build();
|
||||
LoreNode subsub = LoreNode.builder().id("s-1a").parentId("s-1").loreId("lore-1").name("s1a").build();
|
||||
Page p1 = Page.builder().id("p-1").nodeId("n-1").title("P1").build();
|
||||
Page p2 = Page.builder().id("p-2").nodeId("s-1").title("P2").build();
|
||||
Page p3 = Page.builder().id("p-3").nodeId("s-1a").title("P3").build();
|
||||
|
||||
when(loreNodeRepository.findByParentId("n-1")).thenReturn(List.of(sub1, sub2));
|
||||
when(loreNodeRepository.findByParentId("s-1")).thenReturn(List.of(subsub));
|
||||
when(pageRepository.findByNodeId("n-1")).thenReturn(List.of(p1));
|
||||
when(pageRepository.findByNodeId("s-1")).thenReturn(List.of(p2));
|
||||
when(pageRepository.findByNodeId("s-2")).thenReturn(List.of());
|
||||
when(pageRepository.findByNodeId("s-1a")).thenReturn(List.of(p3));
|
||||
|
||||
LoreNodeService.DeletionImpact impact = loreNodeService.getDeletionImpact("n-1");
|
||||
|
||||
// 3 sous-dossiers (sub1, sub2, subsub) — on ne compte pas la racine n-1.
|
||||
assertEquals(3, impact.folders());
|
||||
assertEquals(3, impact.pages());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
package com.loremind.application.lorecontext;
|
||||
|
||||
import com.loremind.domain.campaigncontext.Campaign;
|
||||
import com.loremind.domain.campaigncontext.ports.CampaignRepository;
|
||||
import com.loremind.domain.lorecontext.Lore;
|
||||
import com.loremind.domain.lorecontext.LoreNode;
|
||||
import com.loremind.domain.lorecontext.Page;
|
||||
import com.loremind.domain.lorecontext.Template;
|
||||
import com.loremind.domain.lorecontext.ports.LoreNodeRepository;
|
||||
import com.loremind.domain.lorecontext.ports.LoreRepository;
|
||||
import com.loremind.domain.lorecontext.ports.PageRepository;
|
||||
import com.loremind.domain.lorecontext.ports.TemplateRepository;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -17,6 +23,7 @@ import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
@@ -30,6 +37,8 @@ public class LoreServiceTest {
|
||||
@Mock private LoreRepository loreRepository;
|
||||
@Mock private LoreNodeRepository loreNodeRepository;
|
||||
@Mock private PageRepository pageRepository;
|
||||
@Mock private TemplateRepository templateRepository;
|
||||
@Mock private CampaignRepository campaignRepository;
|
||||
|
||||
@InjectMocks private LoreService loreService;
|
||||
|
||||
@@ -134,8 +143,67 @@ public class LoreServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeleteLore_DelegatesToRepository() {
|
||||
void testDeleteLore_EmptyLore() {
|
||||
// Aucun dossier / page / template / campagne : seul le Lore est supprimé.
|
||||
loreService.deleteLore("lore-1");
|
||||
verify(loreRepository).deleteById("lore-1");
|
||||
verify(loreNodeRepository, never()).deleteById(anyString());
|
||||
verify(pageRepository, never()).deleteById(anyString());
|
||||
verify(templateRepository, never()).deleteById(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeleteLore_CascadesFoldersPagesTemplates() {
|
||||
LoreNode node = LoreNode.builder().id("n-1").loreId("lore-1").name("F").build();
|
||||
Page page = Page.builder().id("p-1").loreId("lore-1").nodeId("n-1").title("P").build();
|
||||
Template template = Template.builder().id("t-1").loreId("lore-1").name("T").build();
|
||||
|
||||
when(pageRepository.findByLoreId("lore-1")).thenReturn(List.of(page));
|
||||
when(loreNodeRepository.findByLoreId("lore-1")).thenReturn(List.of(node));
|
||||
when(templateRepository.findByLoreId("lore-1")).thenReturn(List.of(template));
|
||||
|
||||
loreService.deleteLore("lore-1");
|
||||
|
||||
verify(pageRepository).deleteById("p-1");
|
||||
verify(loreNodeRepository).deleteById("n-1");
|
||||
verify(templateRepository).deleteById("t-1");
|
||||
verify(loreRepository).deleteById("lore-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeleteLore_DetachesCampaignsInsteadOfDeleting() {
|
||||
Campaign attached = Campaign.builder().id("c-1").loreId("lore-1").name("C1").build();
|
||||
Campaign other = Campaign.builder().id("c-2").loreId("lore-other").name("C2").build();
|
||||
Campaign orphan = Campaign.builder().id("c-3").loreId(null).name("C3").build();
|
||||
when(campaignRepository.findAll()).thenReturn(List.of(attached, other, orphan));
|
||||
when(campaignRepository.save(any(Campaign.class))).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
loreService.deleteLore("lore-1");
|
||||
|
||||
// Seule la campagne attachée est re-sauvegardée (avec loreId=null).
|
||||
ArgumentCaptor<Campaign> captor = ArgumentCaptor.forClass(Campaign.class);
|
||||
verify(campaignRepository, times(1)).save(captor.capture());
|
||||
assertEquals("c-1", captor.getValue().getId());
|
||||
assertNull(captor.getValue().getLoreId());
|
||||
// Aucune campagne n'est supprimée.
|
||||
verify(campaignRepository, never()).deleteById(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDeletionImpact() {
|
||||
Template t1 = Template.builder().id("t-1").loreId("lore-1").name("T").build();
|
||||
Campaign attached = Campaign.builder().id("c-1").loreId("lore-1").name("C").build();
|
||||
Campaign unrelated = Campaign.builder().id("c-2").loreId("lore-other").name("C2").build();
|
||||
when(loreNodeRepository.countByLoreId("lore-1")).thenReturn(4L);
|
||||
when(pageRepository.countByLoreId("lore-1")).thenReturn(12L);
|
||||
when(templateRepository.findByLoreId("lore-1")).thenReturn(List.of(t1));
|
||||
when(campaignRepository.findAll()).thenReturn(List.of(attached, unrelated));
|
||||
|
||||
LoreService.DeletionImpact impact = loreService.getDeletionImpact("lore-1");
|
||||
|
||||
assertEquals(4, impact.folders());
|
||||
assertEquals(12, impact.pages());
|
||||
assertEquals(1, impact.templates());
|
||||
assertEquals(1, impact.detachedCampaigns());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user