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"));
}
}
}