using Manager.DTOs; using ManagerService.Data; using ManagerService.Data.SubSection; using ManagerService.DTOs; using ManagerService.Services; using ManagerService.Tests.Infrastructure; using Microsoft.Extensions.Caching.Memory; using System.Collections.Generic; using System.Linq; using Xunit; namespace ManagerService.Tests.Services { /// /// L'index inverse décide de ce que la Médiathèque propose à la suppression. /// Une ressource comptée orpheline à tort est une image effacée d'un contenu vivant, /// d'où un test par piège identifié. /// public class ResourceUsageServiceTests { private static ResourceUsageService Build(MyInfoMateDbContext db) => new ResourceUsageService(db, new MemoryCache(new MemoryCacheOptions())); private static void SeedInstance(MyInfoMateDbContext db) { db.Configurations.Add(new Configuration { Id = "cfg-1", InstanceId = "inst-1", Label = "Visite", Title = new List(), Languages = new List { "FR", "NL" } }); } [Fact] public void ImageUsedOnlyInDutch_IsNotOrphan() { using var db = DbContextFactory.Create(); SeedInstance(db); var article = TestSection.Article("sec-1", "inst-1", "Article", "cfg-1"); article.ArticleAudioIds = new List { new TranslationDTO { language = "NL", value = "res-nl" } }; db.Sections.Add(article); db.Resources.Add(new Resource { Id = "res-nl", InstanceId = "inst-1", Label = "Audio NL", Type = ResourceType.Audio }); db.SaveChanges(); var usages = Build(db).GetUsages("res-nl"); Assert.Single(usages); Assert.Equal("Section", usages[0].kind); } [Fact] public void ConfigurationImages_AreCounted() { using var db = DbContextFactory.Create(); db.Configurations.Add(new Configuration { Id = "cfg-1", InstanceId = "inst-1", Label = "Visite", Title = new List(), Languages = new List { "FR" }, ImageId = "res-accueil", LoaderImageId = "res-loader" }); db.Resources.AddRange( new Resource { Id = "res-accueil", InstanceId = "inst-1", Label = "Accueil", Type = ResourceType.Image }, new Resource { Id = "res-loader", InstanceId = "inst-1", Label = "Loader", Type = ResourceType.Image }); db.SaveChanges(); var service = Build(db); Assert.Single(service.GetUsages("res-accueil")); Assert.Equal("image", service.GetUsages("res-accueil")[0].field); Assert.Equal("loaderImage", service.GetUsages("res-loader")[0].field); } [Fact] public void GuidedPathOnEvent_IsWalked() { using var db = DbContextFactory.Create(); SeedInstance(db); db.Sections.Add(new SectionEvent { Id = "sec-event", InstanceId = "inst-1", ConfigurationId = "cfg-1", Label = "Carnaval", Type = SectionType.Event, Title = new List(), Description = new List() }); db.GuidedPaths.Add(new GuidedPath { Id = "path-1", InstanceId = "inst-1", SectionEventId = "sec-event", Title = new List { new TranslationDTO { language = "FR", value = "Chasse au trésor" } }, Description = new List(), ImageResourceId = "res-path" }); db.Resources.Add(new Resource { Id = "res-path", InstanceId = "inst-1", Label = "Vignette", Type = ResourceType.Image }); db.SaveChanges(); var usages = Build(db).GetUsages("res-path"); // SectionEvent.GetReferencedResourceIds ne descend pas dans les parcours : // sans parcours des GuidedPaths, cette image passerait pour orpheline. Assert.Single(usages); Assert.Equal("GuidedPath", usages[0].kind); Assert.Contains("Chasse au trésor", usages[0].path); } [Fact] public void StepResource_IsAttributedToTheStep_NotThePath() { using var db = DbContextFactory.Create(); SeedInstance(db); db.GuidedPaths.Add(new GuidedPath { Id = "path-1", InstanceId = "inst-1", Title = new List { new TranslationDTO { language = "FR", value = "Parcours" } }, Description = new List(), Steps = new List { new GuidedStep { Id = "step-1", GuidedPathId = "path-1", Order = 0, Title = new List { new TranslationDTO { language = "FR", value = "Première étape" } }, Description = new List(), Contents = new List { new ContentDTO { resourceId = "res-step" } }, AudioIds = new List() } } }); db.Resources.Add(new Resource { Id = "res-step", InstanceId = "inst-1", Label = "Photo", Type = ResourceType.Image }); db.SaveChanges(); var usages = Build(db).GetUsages("res-step"); Assert.Single(usages); Assert.Equal("GuidedStep", usages[0].kind); } [Fact] public void UsageSummary_ReportsZeroForUnusedResources_AndConfigurationLabels() { using var db = DbContextFactory.Create(); SeedInstance(db); var article = TestSection.Article("sec-1", "inst-1", "Article", "cfg-1"); article.ImageId = "res-used"; db.Sections.Add(article); db.Resources.AddRange( new Resource { Id = "res-used", InstanceId = "inst-1", Label = "Utilisée", Type = ResourceType.Image }, new Resource { Id = "res-free", InstanceId = "inst-1", Label = "Libre", Type = ResourceType.Image }); db.SaveChanges(); var summary = Build(db).GetUsageSummary("inst-1"); Assert.Equal(1, summary.usages["res-used"].count); Assert.Equal(new[] { "cfg-1" }, summary.usages["res-used"].configurationIds); Assert.Equal(0, summary.usages["res-free"].count); Assert.Equal("Visite", summary.configurations["cfg-1"]); } [Fact] public void GuidedStepImageUrl_IsNotResolved() { using var db = DbContextFactory.Create(); SeedInstance(db); db.GuidedPaths.Add(new GuidedPath { Id = "path-1", InstanceId = "inst-1", Title = new List(), Description = new List(), Steps = new List { new GuidedStep { Id = "step-1", GuidedPathId = "path-1", Order = 0, Title = new List(), Description = new List(), Contents = new List(), AudioIds = new List(), ImageUrl = "https://storage/res-url" } } }); db.Resources.Add(new Resource { Id = "res-url", InstanceId = "inst-1", Label = "Image d'étape", Type = ResourceType.Image }); db.SaveChanges(); // Limite connue et assumée en V1 : ImageUrl est une URL absolue, pas un id. // Le test la fige pour que sa disparition en V2 soit un choix, pas un accident. Assert.Empty(Build(db).GetUsages("res-url")); } } }