using ManagerService.Controllers; using ManagerService.Data; using ManagerService.Data.SubSection; using ManagerService.DTOs; using ManagerService.Tests.Infrastructure; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging.Abstractions; using System.Collections.Generic; using System.Linq; using Xunit; namespace ManagerService.Tests.Controllers { /// /// Couvre la persistance des règles de progression d'un parcours — les 3 booléens /// derrière la question « Comment le visiteur progresse-t-il ? » de manager-app /// (Libre / Dans l'ordre / Étape par étape) — et le nettoyage des champs morts. /// public class SectionParcoursControllerTests { private SectionParcoursController BuildController(MyInfoMateDbContext db) { var controller = new SectionParcoursController( FakeConfiguration.Create(), NullLogger.Instance, db); FakeUser.SetUser(controller, FakeUser.Create(Permissions.SuperAdmin)); return controller; } private static SectionParcours SeedSection(MyInfoMateDbContext db, string id = "sp1", bool showMap = true) { var section = new SectionParcours { Id = id, InstanceId = "inst-test", ConfigurationId = "cfg1", Label = "Parcours de test", Title = new List(), Description = new List(), ShowMap = showMap, }; db.Sections.Add(section); db.SaveChanges(); return section; } private static GuidedPathDTO MakePath(string title = "Parcours") => new GuidedPathDTO { instanceId = "inst-test", title = new List { new TranslationDTO { language = "FR", value = title } }, description = new List(), steps = new List(), order = 0, }; // ── Modes de progression ───────────────────────────────────────────── [Theory] // Libre : le visiteur choisit ses étapes dans l'ordre qu'il veut [InlineData(false, false, false)] // Dans l'ordre : séquence imposée, sans obligation de réussite [InlineData(true, false, false)] // Étape par étape : séquence + validation, étapes futures masquées [InlineData(true, true, true)] public void CreateGuidedPath_PersistsProgressionFlags(bool isLinear, bool requireSuccess, bool hideNext) { using var db = DbContextFactory.Create(); SeedSection(db); var dto = MakePath(); dto.isLinear = isLinear; dto.requireSuccessToAdvance = requireSuccess; dto.hideNextStepsUntilComplete = hideNext; var result = BuildController(db).CreateGuidedPath("sp1", dto); Assert.Equal(200, result.StatusCode); var saved = db.GuidedPaths.Single(); Assert.Equal(isLinear, saved.IsLinear); Assert.Equal(requireSuccess, saved.RequireSuccessToAdvance); Assert.Equal(hideNext, saved.HideNextStepsUntilComplete); } [Fact] public void UpdateGuidedPath_SwitchingToFreeNavigation_ClearsSequencingFlags() { using var db = DbContextFactory.Create(); SeedSection(db); var created = BuildController(db).CreateGuidedPath("sp1", MakePath()); var path = Assert.IsType(created.Value); // Passage « Étape par étape » → « Libre » (ce que fait applyProgressionMode côté manager-app) path.isLinear = false; path.requireSuccessToAdvance = false; path.hideNextStepsUntilComplete = false; var result = BuildController(db).UpdateGuidedPath(path); Assert.Equal(200, result.StatusCode); var saved = db.GuidedPaths.Single(); Assert.False(saved.IsLinear); Assert.False(saved.RequireSuccessToAdvance); Assert.False(saved.HideNextStepsUntilComplete); } [Fact] public void CreateGuidedPath_KeepsGameModeMessages() { using var db = DbContextFactory.Create(); SeedSection(db); var dto = MakePath(); dto.isGameMode = true; dto.gameMessageDebut = new List { new TranslationAndResourceDTO { language = "FR", value = "Bienvenue dans l'escape" } }; dto.gameMessageFin = new List { new TranslationAndResourceDTO { language = "FR", value = "Bravo !" } }; BuildController(db).CreateGuidedPath("sp1", dto); var saved = db.GuidedPaths.Single(); Assert.True(saved.IsGameMode); Assert.Equal("Bienvenue dans l'escape", saved.GameMessageDebut.Single().value); Assert.Equal("Bravo !", saved.GameMessageFin.Single().value); } [Fact] public void CreateGuidedPath_UnknownSection_Returns404() { using var db = DbContextFactory.Create(); var result = BuildController(db).CreateGuidedPath("does-not-exist", MakePath()); Assert.Equal(404, result.StatusCode); } // ── Étapes ─────────────────────────────────────────────────────────── [Fact] public void CreateGuidedStep_PersistsGeoTriggerAndTimer() { using var db = DbContextFactory.Create(); SeedSection(db); var created = BuildController(db).CreateGuidedPath("sp1", MakePath()); var pathId = Assert.IsType(created.Value).id; var step = new GuidedStepDTO { title = new List(), description = new List(), isGeoTriggered = true, zoneRadiusMeters = 30, isStepTimer = true, timerSeconds = 120, order = 0, }; var result = BuildController(db).CreateGuidedStep(pathId, step); Assert.Equal(200, result.StatusCode); var saved = db.GuidedSteps.Single(); Assert.True(saved.IsGeoTriggered); Assert.Equal(30, saved.ZoneRadiusMeters); Assert.True(saved.IsStepTimer); Assert.Equal(120, saved.TimerSeconds); } [Fact] public void GetAllGuidedPathFromSection_ReturnsPathsOrderedByOrder() { using var db = DbContextFactory.Create(); SeedSection(db); var controller = BuildController(db); var second = MakePath("Second"); second.order = 1; var first = MakePath("Premier"); first.order = 0; controller.CreateGuidedPath("sp1", second); controller.CreateGuidedPath("sp1", first); var result = BuildController(db).GetAllGuidedPathFromSection("sp1"); Assert.Equal(200, result.StatusCode); var paths = Assert.IsAssignableFrom>(result.Value).ToList(); Assert.Equal(2, paths.Count); Assert.Equal(new int?[] { 0, 1 }, paths.Select(p => p.order).ToArray()); } } }