Refonte du système JDR + système de personnage joueurs / non joueurs :
- Système de templating dans le game system : en effet, les templates sont liés au game system car les fiches personnages ne sont pas forcément les même selon les jeux (perso Dnd possède + de compétences que Nimble par exemple) - changement des fiches personnages pour adapter le templating au niveau des campagnes et remplir des pages de perso
This commit is contained in:
@@ -11,6 +11,7 @@ import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
@@ -38,7 +39,7 @@ public class NpcServiceTest {
|
||||
testNpc = Npc.builder()
|
||||
.id("npc-1")
|
||||
.name("Borin le forgeron")
|
||||
.markdownContent("# Borin\nForgeron nain")
|
||||
.values(new java.util.HashMap<>(Map.of("Notes", "Forgeron nain")))
|
||||
.campaignId("camp-1")
|
||||
.order(1)
|
||||
.build();
|
||||
@@ -49,7 +50,8 @@ public class NpcServiceTest {
|
||||
when(npcRepository.save(any(Npc.class))).thenReturn(testNpc);
|
||||
|
||||
Npc result = npcService.createNpc(
|
||||
new NpcService.NpcData("Borin le forgeron", "# Borin", "camp-1", 5));
|
||||
new NpcService.NpcData("Borin le forgeron", null, null,
|
||||
Map.of("Notes", "Borin"), null, "camp-1", 5));
|
||||
|
||||
assertNotNull(result);
|
||||
ArgumentCaptor<Npc> captor = ArgumentCaptor.forClass(Npc.class);
|
||||
@@ -65,7 +67,7 @@ public class NpcServiceTest {
|
||||
when(npcRepository.findByCampaignId("camp-1")).thenReturn(List.of(a, b));
|
||||
when(npcRepository.save(any(Npc.class))).thenReturn(testNpc);
|
||||
|
||||
npcService.createNpc(new NpcService.NpcData("Nouveau", null, "camp-1", null));
|
||||
npcService.createNpc(new NpcService.NpcData("Nouveau", null, null, null, null, "camp-1", null));
|
||||
|
||||
ArgumentCaptor<Npc> captor = ArgumentCaptor.forClass(Npc.class);
|
||||
verify(npcRepository).save(captor.capture());
|
||||
@@ -77,7 +79,7 @@ public class NpcServiceTest {
|
||||
when(npcRepository.findByCampaignId("camp-1")).thenReturn(List.of());
|
||||
when(npcRepository.save(any(Npc.class))).thenReturn(testNpc);
|
||||
|
||||
npcService.createNpc(new NpcService.NpcData("Premier", null, "camp-1", null));
|
||||
npcService.createNpc(new NpcService.NpcData("Premier", null, null, null, null, "camp-1", null));
|
||||
|
||||
ArgumentCaptor<Npc> captor = ArgumentCaptor.forClass(Npc.class);
|
||||
verify(npcRepository).save(captor.capture());
|
||||
@@ -121,10 +123,11 @@ public class NpcServiceTest {
|
||||
when(npcRepository.save(any(Npc.class))).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
Npc result = npcService.updateNpc("npc-1",
|
||||
new NpcService.NpcData("Borin renommé", "# v2", "camp-1", 7));
|
||||
new NpcService.NpcData("Borin renommé", null, null,
|
||||
Map.of("Notes", "v2"), null, "camp-1", 7));
|
||||
|
||||
assertEquals("Borin renommé", result.getName());
|
||||
assertEquals("# v2", result.getMarkdownContent());
|
||||
assertEquals("v2", result.getValues().get("Notes"));
|
||||
assertEquals(7, result.getOrder());
|
||||
}
|
||||
|
||||
@@ -134,7 +137,8 @@ public class NpcServiceTest {
|
||||
when(npcRepository.save(any(Npc.class))).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
Npc result = npcService.updateNpc("npc-1",
|
||||
new NpcService.NpcData("Borin", "# txt", "camp-1", null));
|
||||
new NpcService.NpcData("Borin", null, null,
|
||||
Map.of("Notes", "txt"), null, "camp-1", null));
|
||||
|
||||
// testNpc avait order=1 → préservé
|
||||
assertEquals(1, result.getOrder());
|
||||
@@ -146,7 +150,7 @@ public class NpcServiceTest {
|
||||
|
||||
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
|
||||
() -> npcService.updateNpc("missing",
|
||||
new NpcService.NpcData("x", null, "camp-1", null)));
|
||||
new NpcService.NpcData("x", null, null, null, null, "camp-1", null)));
|
||||
assertTrue(ex.getMessage().contains("missing"));
|
||||
verify(npcRepository, never()).save(any());
|
||||
}
|
||||
|
||||
@@ -153,19 +153,19 @@ public class CampaignStructuralContextBuilderTest {
|
||||
void testBuild_ProjectsCharactersAndNpcsWithSnippets() {
|
||||
Character pj1 = Character.builder().id("c-1").campaignId("camp-1").order(1)
|
||||
.name("Aragorn")
|
||||
.markdownContent("# Aragorn\n\nRôdeur du Nord, héritier d'Isildur.")
|
||||
.values(new java.util.HashMap<>(java.util.Map.of("Histoire", "# Aragorn\n\nRôdeur du Nord, héritier d'Isildur.")))
|
||||
.build();
|
||||
Character pj2 = Character.builder().id("c-2").campaignId("camp-1").order(2)
|
||||
.name("Legolas")
|
||||
.markdownContent(null) // pas de snippet → string vide
|
||||
.values(null) // pas de snippet → string vide
|
||||
.build();
|
||||
Npc npc1 = Npc.builder().id("n-1").campaignId("camp-1").order(2)
|
||||
.name("Borin le forgeron")
|
||||
.markdownContent("# Borin\n\nNain barbu au regard perçant, ancien clan Feuillefer.")
|
||||
.values(new java.util.HashMap<>(java.util.Map.of("Histoire", "# Borin\n\nNain barbu au regard perçant, ancien clan Feuillefer.")))
|
||||
.build();
|
||||
Npc npc2 = Npc.builder().id("n-2").campaignId("camp-1").order(1)
|
||||
.name("Dame Elara")
|
||||
.markdownContent("")
|
||||
.values(new java.util.HashMap<>(java.util.Map.of("Histoire", "")))
|
||||
.build();
|
||||
|
||||
when(campaignRepository.findById("camp-1")).thenReturn(Optional.of(campaign));
|
||||
@@ -196,7 +196,7 @@ public class CampaignStructuralContextBuilderTest {
|
||||
// Snippet > 160 chars : doit être tronqué à 159 + "…"
|
||||
String longLine = "x".repeat(200);
|
||||
Npc longNpc = Npc.builder().id("n-1").campaignId("camp-1").order(1)
|
||||
.name("Verbeux").markdownContent(longLine).build();
|
||||
.name("Verbeux").values(new java.util.HashMap<>(java.util.Map.of("Histoire", longLine))).build();
|
||||
|
||||
when(campaignRepository.findById("camp-1")).thenReturn(Optional.of(campaign));
|
||||
when(arcRepository.findByCampaignId("camp-1")).thenReturn(List.of());
|
||||
|
||||
@@ -3,12 +3,12 @@ package com.loremind.application.generationcontext;
|
||||
import com.loremind.domain.generationcontext.GenerationContext;
|
||||
import com.loremind.domain.generationcontext.GenerationResult;
|
||||
import com.loremind.domain.generationcontext.ports.AiProvider;
|
||||
import com.loremind.domain.lorecontext.FieldType;
|
||||
import com.loremind.domain.shared.template.FieldType;
|
||||
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.TemplateField;
|
||||
import com.loremind.domain.shared.template.TemplateField;
|
||||
import com.loremind.domain.lorecontext.ports.LoreNodeRepository;
|
||||
import com.loremind.domain.lorecontext.ports.LoreRepository;
|
||||
import com.loremind.domain.lorecontext.ports.PageRepository;
|
||||
|
||||
@@ -115,8 +115,13 @@ public class NarrativeEntityContextBuilderTest {
|
||||
|
||||
@Test
|
||||
void testBuild_Character_MarkdownProjected() {
|
||||
// Refonte 2026-04-30 : les valeurs templates sont projetees individuellement
|
||||
// dans la map fields (cle = nom du champ template).
|
||||
Character c = Character.builder()
|
||||
.id("c-1").name("Aragorn").markdownContent("# Aragorn\nRôdeur")
|
||||
.id("c-1").name("Aragorn")
|
||||
.values(new java.util.HashMap<>(java.util.Map.of(
|
||||
"Histoire", "# Aragorn\nRôdeur",
|
||||
"Race", "Humain")))
|
||||
.build();
|
||||
when(characterRepository.findById("c-1")).thenReturn(Optional.of(c));
|
||||
|
||||
@@ -124,14 +129,17 @@ public class NarrativeEntityContextBuilderTest {
|
||||
|
||||
assertEquals("character", ctx.entityType());
|
||||
assertEquals("Aragorn", ctx.title());
|
||||
assertEquals("# Aragorn\nRôdeur", ctx.fields().get("fiche complète (markdown)"));
|
||||
assertEquals("# Aragorn\nRôdeur", ctx.fields().get("Histoire"));
|
||||
assertEquals("Humain", ctx.fields().get("Race"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBuild_Npc_MarkdownProjected() {
|
||||
Npc n = Npc.builder()
|
||||
.id("n-1").name("Borin le forgeron")
|
||||
.markdownContent("# Borin\n**Faction :** Clan Feuillefer")
|
||||
.values(new java.util.HashMap<>(java.util.Map.of(
|
||||
"Faction", "Clan Feuillefer",
|
||||
"Histoire", "# Borin")))
|
||||
.build();
|
||||
when(npcRepository.findById("n-1")).thenReturn(Optional.of(n));
|
||||
|
||||
@@ -139,13 +147,14 @@ public class NarrativeEntityContextBuilderTest {
|
||||
|
||||
assertEquals("npc", ctx.entityType());
|
||||
assertEquals("Borin le forgeron", ctx.title());
|
||||
assertEquals("# Borin\n**Faction :** Clan Feuillefer",
|
||||
ctx.fields().get("fiche complète (markdown)"));
|
||||
assertEquals("Clan Feuillefer", ctx.fields().get("Faction"));
|
||||
assertEquals("# Borin", ctx.fields().get("Histoire"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBuild_Npc_NormalizesCase() {
|
||||
Npc n = Npc.builder().id("n-1").name("Elara").markdownContent("desc").build();
|
||||
Npc n = Npc.builder().id("n-1").name("Elara")
|
||||
.values(new java.util.HashMap<>(java.util.Map.of("Notes", "desc"))).build();
|
||||
when(npcRepository.findById("n-1")).thenReturn(Optional.of(n));
|
||||
|
||||
NarrativeEntityContext ctx = builder.build(" NPC ", "n-1");
|
||||
|
||||
@@ -5,10 +5,10 @@ import com.loremind.domain.generationcontext.ChatRequest;
|
||||
import com.loremind.domain.generationcontext.ChatUsage;
|
||||
import com.loremind.domain.generationcontext.LoreStructuralContext;
|
||||
import com.loremind.domain.generationcontext.ports.AiChatProvider;
|
||||
import com.loremind.domain.lorecontext.FieldType;
|
||||
import com.loremind.domain.shared.template.FieldType;
|
||||
import com.loremind.domain.lorecontext.Page;
|
||||
import com.loremind.domain.lorecontext.Template;
|
||||
import com.loremind.domain.lorecontext.TemplateField;
|
||||
import com.loremind.domain.shared.template.TemplateField;
|
||||
import com.loremind.domain.lorecontext.ports.PageRepository;
|
||||
import com.loremind.domain.lorecontext.ports.TemplateRepository;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.loremind.application.lorecontext;
|
||||
|
||||
import com.loremind.domain.lorecontext.Template;
|
||||
import com.loremind.domain.lorecontext.TemplateField;
|
||||
import com.loremind.domain.shared.template.TemplateField;
|
||||
import com.loremind.domain.lorecontext.ports.TemplateRepository;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
package com.loremind.domain.gamesystemcontext;
|
||||
|
||||
import com.loremind.domain.shared.template.FieldType;
|
||||
import com.loremind.domain.shared.template.ImageLayout;
|
||||
import com.loremind.domain.shared.template.TemplateField;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests unitaires du domaine GameSystem ciblant la gestion des templates PJ/PNJ.
|
||||
* Le ruleset markdown est testé ailleurs via GameSystemContextSelector.
|
||||
*/
|
||||
class GameSystemTest {
|
||||
|
||||
// --- addCharacterField --------------------------------------------------
|
||||
|
||||
@Test
|
||||
void addCharacterField_appendsField() {
|
||||
GameSystem gs = GameSystem.builder().build();
|
||||
|
||||
gs.addCharacterField(TemplateField.text("Histoire"));
|
||||
gs.addCharacterField(TemplateField.image("Portrait", ImageLayout.HERO));
|
||||
|
||||
assertEquals(2, gs.getCharacterTemplate().size());
|
||||
assertEquals("Histoire", gs.getCharacterTemplate().get(0).getName());
|
||||
assertEquals(FieldType.IMAGE, gs.getCharacterTemplate().get(1).getType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void addCharacterField_rejectsDuplicateNameCaseInsensitive() {
|
||||
GameSystem gs = GameSystem.builder().build();
|
||||
gs.addCharacterField(TemplateField.text("Histoire"));
|
||||
|
||||
// Doublon de cle dans Character.values garanti casse-insensible :
|
||||
// "Histoire" et "histoire" produiraient la meme cle JSON.
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> gs.addCharacterField(TemplateField.number("HISTOIRE")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void addCharacterField_rejectsBlankName() {
|
||||
GameSystem gs = GameSystem.builder().build();
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> gs.addCharacterField(new TemplateField(" ", FieldType.TEXT)));
|
||||
}
|
||||
|
||||
// --- removeCharacterField ----------------------------------------------
|
||||
|
||||
@Test
|
||||
void removeCharacterField_removesByNameCaseInsensitive() {
|
||||
GameSystem gs = GameSystem.builder()
|
||||
.characterTemplate(new ArrayList<>(List.of(
|
||||
TemplateField.text("Histoire"),
|
||||
TemplateField.text("Notes")
|
||||
)))
|
||||
.build();
|
||||
|
||||
gs.removeCharacterField("HISTOIRE");
|
||||
|
||||
assertEquals(1, gs.getCharacterTemplate().size());
|
||||
assertEquals("Notes", gs.getCharacterTemplate().get(0).getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void removeCharacterField_silentNoOpWhenMissing() {
|
||||
GameSystem gs = GameSystem.builder()
|
||||
.characterTemplate(new ArrayList<>(List.of(TemplateField.text("Histoire"))))
|
||||
.build();
|
||||
|
||||
gs.removeCharacterField("absent");
|
||||
|
||||
assertEquals(1, gs.getCharacterTemplate().size());
|
||||
}
|
||||
|
||||
// --- replaceCharacterTemplate ------------------------------------------
|
||||
|
||||
@Test
|
||||
void replaceCharacterTemplate_overwritesEntireList() {
|
||||
GameSystem gs = GameSystem.builder()
|
||||
.characterTemplate(new ArrayList<>(List.of(TemplateField.text("Old"))))
|
||||
.build();
|
||||
|
||||
gs.replaceCharacterTemplate(List.of(
|
||||
TemplateField.text("A"),
|
||||
TemplateField.number("B")));
|
||||
|
||||
assertEquals(2, gs.getCharacterTemplate().size());
|
||||
assertEquals("A", gs.getCharacterTemplate().get(0).getName());
|
||||
assertEquals("B", gs.getCharacterTemplate().get(1).getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void replaceCharacterTemplate_rejectsDuplicates() {
|
||||
GameSystem gs = GameSystem.builder().build();
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> gs.replaceCharacterTemplate(List.of(
|
||||
TemplateField.text("a"),
|
||||
TemplateField.text("A"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void replaceCharacterTemplate_nullBecomesEmptyList() {
|
||||
GameSystem gs = GameSystem.builder().build();
|
||||
gs.replaceCharacterTemplate(null);
|
||||
assertTrue(gs.getCharacterTemplate().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void replaceCharacterTemplate_isolatesInternalListFromCallerMutations() {
|
||||
// Garantie d'encapsulation : muter la liste passee ne doit pas affecter le GameSystem.
|
||||
List<TemplateField> external = new ArrayList<>(List.of(TemplateField.text("A")));
|
||||
GameSystem gs = GameSystem.builder().build();
|
||||
|
||||
gs.replaceCharacterTemplate(external);
|
||||
external.add(TemplateField.text("B"));
|
||||
|
||||
assertEquals(1, gs.getCharacterTemplate().size());
|
||||
}
|
||||
|
||||
// --- Templates NPC : meme logique, sanity check minimal ----------------
|
||||
|
||||
@Test
|
||||
void npcTemplate_followsSameRulesAsCharacterTemplate() {
|
||||
GameSystem gs = GameSystem.builder().build();
|
||||
|
||||
gs.addNpcField(TemplateField.text("Motivation"));
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> gs.addNpcField(TemplateField.text("motivation")));
|
||||
|
||||
gs.removeNpcField("Motivation");
|
||||
assertTrue(gs.getNpcTemplate().isEmpty());
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.loremind.domain.lorecontext;
|
||||
|
||||
import com.loremind.domain.shared.template.ImageLayout;
|
||||
import com.loremind.domain.shared.template.TemplateField;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.loremind.domain.lorecontext;
|
||||
package com.loremind.domain.shared.template;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.loremind.infrastructure.persistence.converter;
|
||||
|
||||
import com.loremind.domain.lorecontext.FieldType;
|
||||
import com.loremind.domain.lorecontext.ImageLayout;
|
||||
import com.loremind.domain.lorecontext.TemplateField;
|
||||
import com.loremind.domain.shared.template.FieldType;
|
||||
import com.loremind.domain.shared.template.ImageLayout;
|
||||
import com.loremind.domain.shared.template.TemplateField;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.loremind.infrastructure.persistence.postgres;
|
||||
|
||||
import com.loremind.domain.lorecontext.FieldType;
|
||||
import com.loremind.domain.lorecontext.ImageLayout;
|
||||
import com.loremind.domain.shared.template.FieldType;
|
||||
import com.loremind.domain.shared.template.ImageLayout;
|
||||
import com.loremind.domain.lorecontext.Lore;
|
||||
import com.loremind.domain.lorecontext.Template;
|
||||
import com.loremind.domain.lorecontext.TemplateField;
|
||||
import com.loremind.domain.shared.template.TemplateField;
|
||||
import com.loremind.domain.lorecontext.ports.LoreRepository;
|
||||
import com.loremind.domain.lorecontext.ports.TemplateRepository;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.loremind.infrastructure.web.controller;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.loremind.infrastructure.web.dto.gamesystemcontext.GameSystemDTO;
|
||||
import com.loremind.infrastructure.web.dto.shared.TemplateFieldDTO;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests d'integration du GameSystemController centres sur la persistance
|
||||
* des templates PJ/PNJ via l'API REST. Le CRUD de base est suppose stable.
|
||||
*/
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@Transactional
|
||||
class GameSystemControllerTest {
|
||||
|
||||
@Autowired private MockMvc mockMvc;
|
||||
@Autowired private ObjectMapper objectMapper;
|
||||
|
||||
@Test
|
||||
void create_persistsCharacterAndNpcTemplates() throws Exception {
|
||||
GameSystemDTO dto = new GameSystemDTO();
|
||||
dto.setName("Nimble Test");
|
||||
dto.setRulesMarkdown("## Combat\n- d20");
|
||||
dto.setCharacterTemplate(List.of(
|
||||
new TemplateFieldDTO("Histoire", "TEXT", null),
|
||||
new TemplateFieldDTO("PV", "NUMBER", null),
|
||||
new TemplateFieldDTO("Portrait", "IMAGE", "HERO")));
|
||||
dto.setNpcTemplate(List.of(
|
||||
new TemplateFieldDTO("Motivation", "TEXT", null)));
|
||||
|
||||
mockMvc.perform(post("/api/game-systems")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(dto)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.id").exists())
|
||||
.andExpect(jsonPath("$.characterTemplate.length()").value(3))
|
||||
.andExpect(jsonPath("$.characterTemplate[1].type").value("NUMBER"))
|
||||
.andExpect(jsonPath("$.characterTemplate[2].layout").value("HERO"))
|
||||
.andExpect(jsonPath("$.npcTemplate.length()").value(1))
|
||||
.andExpect(jsonPath("$.npcTemplate[0].name").value("Motivation"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void update_replacesTemplates() throws Exception {
|
||||
// Creation initiale avec un seul champ.
|
||||
GameSystemDTO dto = new GameSystemDTO();
|
||||
dto.setName("RuleSet");
|
||||
dto.setCharacterTemplate(List.of(new TemplateFieldDTO("Old", "TEXT", null)));
|
||||
|
||||
MvcResult posted = mockMvc.perform(post("/api/game-systems")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(dto)))
|
||||
.andExpect(status().isOk())
|
||||
.andReturn();
|
||||
|
||||
GameSystemDTO created = objectMapper.readValue(
|
||||
posted.getResponse().getContentAsString(), GameSystemDTO.class);
|
||||
|
||||
// Replace template integralement.
|
||||
created.setCharacterTemplate(List.of(
|
||||
new TemplateFieldDTO("Histoire", "TEXT", null),
|
||||
new TemplateFieldDTO("Niveau", "NUMBER", null)));
|
||||
|
||||
mockMvc.perform(put("/api/game-systems/{id}", created.getId())
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(created)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.characterTemplate.length()").value(2))
|
||||
.andExpect(jsonPath("$.characterTemplate[0].name").value("Histoire"))
|
||||
.andExpect(jsonPath("$.characterTemplate[1].type").value("NUMBER"));
|
||||
|
||||
// Verification que le GET independant retourne bien les nouveaux champs (pas de cache stale).
|
||||
mockMvc.perform(get("/api/game-systems/{id}", created.getId()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.characterTemplate[?(@.name == 'Old')]").doesNotExist())
|
||||
.andExpect(jsonPath("$.characterTemplate[?(@.name == 'Histoire')]").exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
void create_rejectsDuplicateFieldNames() throws Exception {
|
||||
GameSystemDTO dto = new GameSystemDTO();
|
||||
dto.setName("BadRules");
|
||||
dto.setCharacterTemplate(List.of(
|
||||
new TemplateFieldDTO("Nom", "TEXT", null),
|
||||
new TemplateFieldDTO("nom", "NUMBER", null)));
|
||||
|
||||
mockMvc.perform(post("/api/game-systems")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(dto)))
|
||||
.andExpect(status().is4xxClientError());
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import com.loremind.domain.lorecontext.Template;
|
||||
import com.loremind.domain.lorecontext.ports.LoreRepository;
|
||||
import com.loremind.domain.lorecontext.ports.TemplateRepository;
|
||||
import com.loremind.infrastructure.web.dto.lorecontext.TemplateDTO;
|
||||
import com.loremind.infrastructure.web.dto.lorecontext.TemplateFieldDTO;
|
||||
import com.loremind.infrastructure.web.dto.shared.TemplateFieldDTO;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
Reference in New Issue
Block a user