LOT B — une seule migration EF (LotB_FreezeSchema) : - SectionMap.MapResourceId → IconResourceId. L'écart (g) de la bascule tombe avec. Il fallait renommer aussi la propriété de navigation MapResource : la convention EF l'appariait au FK, la laisser aurait fabriqué un FK fantôme. Elle n'était utilisée nulle part ailleurs. - SectionEvent.ParcoursIds supprimé (champ, DTO, SectionFactory, et une initialisation dans un montage de test). - Instance.IsImageWatermark remplace le `instanceId == "633ee379…"` en dur de ResourceController. EF a généré un RenameColumn, pas un drop+add : les icônes déjà configurées survivent. L'avertissement de perte de données ne porte que sur le DropColumn de ParcoursIds, ce qui est l'intention. Non fait, et c'était une erreur de doc : « supprimer SectionEvent.IconResourceId ». Ce champ n'existe pas — la ligne visée appartient à la classe imbriquée MapAnnotation, partagée par SectionEvent, SectionAgenda et SectionMap, lue par cinq contrôleurs et par GetReferencedResourceIds. La supprimer aurait cassé les icônes d'annotation des trois types et la collecte offline. SÉCURITÉ (lot A, même repo) : - AuthenticationController.Authenticate : un bloc #if DEBUG écrasait l'email et le mot de passe reçus par un compte de test, donc toute compilation en Debug authentifiait n'importe quelle saisie. Retiré. - EnableSensitiveDataLogging (qui écrit les valeurs des paramètres dans les logs) passe sous #if DEBUG, l'idiome déjà employé dans Startup.cs pour le CORS et Hangfire. Le Dockerfile publiant en -c Release, c'est un verrou réel. LOT C1 : - Calculateur StoragePath/SizeBytes extrait dans Helpers/ResourceStorage.cs, avec 13 tests fixant l'invariant des types URL. Il ferme le lien L5 : le backfill (C2) et l'écart (e) de la migration appelleront le même code. - L'extraction a révélé la divergence qu'elle devait empêcher : des deux chemins de création de ResourceController, le chemin multipart écrivait SizeBytes mais laissait StoragePath nul. dotnet build Debug et Release verts, dotnet test 143/143 (130 + 13). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
186 lines
6.8 KiB
C#
186 lines
6.8 KiB
C#
using Manager.DTOs;
|
|
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;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Xunit;
|
|
using static ManagerService.Data.SubSection.SectionEvent;
|
|
|
|
namespace ManagerService.Tests.Controllers
|
|
{
|
|
public class SectionEventControllerTests
|
|
{
|
|
private static SectionEventController BuildController(MyInfoMateDbContext db)
|
|
{
|
|
var controller = new SectionEventController(
|
|
FakeMongoConfig.Create(),
|
|
NullLogger<SectionEventController>.Instance,
|
|
db);
|
|
FakeUser.SetUser(controller, FakeUser.Create("Manager.contenteditor", "inst-test"));
|
|
return controller;
|
|
}
|
|
|
|
private static List<TranslationDTO> EmptyTranslations() => new List<TranslationDTO>();
|
|
|
|
// ── PROGRAMME BLOCK ──────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public void CreateProgrammeBlock_ValidRequest_Persists()
|
|
{
|
|
using var db = DbContextFactory.Create();
|
|
db.Sections.Add(new SectionEvent
|
|
{
|
|
Id = "se-1",
|
|
InstanceId = "inst-test",
|
|
Label = "Event",
|
|
Type = SectionType.Event,
|
|
ConfigurationId = "c1",
|
|
Title = EmptyTranslations(),
|
|
Description = EmptyTranslations(),
|
|
Programme = new List<ProgrammeBlock>()
|
|
});
|
|
db.SaveChanges();
|
|
|
|
var result = BuildController(db).CreateProgrammeBlock("se-1", new ProgrammeBlockDTO
|
|
{
|
|
title = EmptyTranslations(),
|
|
description = EmptyTranslations(),
|
|
startTime = DateTime.UtcNow,
|
|
endTime = DateTime.UtcNow.AddHours(1)
|
|
});
|
|
|
|
Assert.IsType<OkObjectResult>(result);
|
|
Assert.Equal(1, db.ProgrammeBlocks.Count());
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateProgrammeBlock_UnknownSection_Returns404()
|
|
{
|
|
using var db = DbContextFactory.Create();
|
|
|
|
var result = BuildController(db).CreateProgrammeBlock("unknown", new ProgrammeBlockDTO
|
|
{
|
|
title = EmptyTranslations(),
|
|
startTime = DateTime.UtcNow,
|
|
endTime = DateTime.UtcNow.AddHours(1)
|
|
});
|
|
|
|
// KeyNotFoundException attrapée par catch(Exception) → 500
|
|
var obj = Assert.IsType<ObjectResult>(result);
|
|
Assert.Equal(500, obj.StatusCode);
|
|
}
|
|
|
|
[Fact]
|
|
public void UpdateProgrammeBlock_ExistingBlock_UpdatesFields()
|
|
{
|
|
using var db = DbContextFactory.Create();
|
|
db.ProgrammeBlocks.Add(new ProgrammeBlock
|
|
{
|
|
Id = "pb-1",
|
|
Title = EmptyTranslations(),
|
|
Description = EmptyTranslations(),
|
|
StartTime = DateTime.UtcNow,
|
|
EndTime = DateTime.UtcNow.AddHours(1),
|
|
MapAnnotations = new List<MapAnnotation>()
|
|
});
|
|
db.SaveChanges();
|
|
|
|
var newStart = DateTime.UtcNow.AddHours(2);
|
|
var result = BuildController(db).UpdateProgrammeBlock(new ProgrammeBlockDTO
|
|
{
|
|
id = "pb-1",
|
|
title = EmptyTranslations(),
|
|
description = EmptyTranslations(),
|
|
startTime = newStart,
|
|
endTime = newStart.AddHours(1)
|
|
});
|
|
|
|
Assert.IsType<OkObjectResult>(result);
|
|
Assert.Equal(newStart, db.ProgrammeBlocks.First().StartTime);
|
|
}
|
|
|
|
[Fact]
|
|
public void DeleteProgrammeBlock_ExistingBlock_Returns202()
|
|
{
|
|
using var db = DbContextFactory.Create();
|
|
db.ProgrammeBlocks.Add(new ProgrammeBlock
|
|
{
|
|
Id = "pb-1",
|
|
Title = EmptyTranslations(),
|
|
Description = EmptyTranslations(),
|
|
StartTime = DateTime.UtcNow,
|
|
EndTime = DateTime.UtcNow.AddHours(1),
|
|
MapAnnotations = new List<MapAnnotation>()
|
|
});
|
|
db.SaveChanges();
|
|
|
|
var result = BuildController(db).DeleteProgrammeBlock("pb-1");
|
|
|
|
var obj = Assert.IsType<ObjectResult>(result);
|
|
Assert.Equal(202, obj.StatusCode);
|
|
Assert.Equal(0, db.ProgrammeBlocks.Count());
|
|
}
|
|
|
|
// ── MAP ANNOTATION ───────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public void CreateMapAnnotation_ValidRequest_Persists()
|
|
{
|
|
using var db = DbContextFactory.Create();
|
|
db.ProgrammeBlocks.Add(new ProgrammeBlock
|
|
{
|
|
Id = "pb-1",
|
|
Title = EmptyTranslations(),
|
|
Description = EmptyTranslations(),
|
|
StartTime = DateTime.UtcNow,
|
|
EndTime = DateTime.UtcNow.AddHours(1),
|
|
MapAnnotations = new List<MapAnnotation>()
|
|
});
|
|
db.SaveChanges();
|
|
|
|
var result = BuildController(db).CreateMapAnnotation("pb-1", new MapAnnotationDTO
|
|
{
|
|
label = EmptyTranslations(),
|
|
type = EmptyTranslations(),
|
|
geometryType = GeometryType.Point,
|
|
geometry = new GeometryDTO { type = "Point", coordinates = new List<double> { 4.35, 50.85 } }
|
|
});
|
|
|
|
Assert.IsType<OkObjectResult>(result);
|
|
Assert.Equal(1, db.MapAnnotations.Count());
|
|
}
|
|
|
|
[Fact]
|
|
public void UpdateMapAnnotation_ExistingAnnotation_UpdatesLabel()
|
|
{
|
|
using var db = DbContextFactory.Create();
|
|
db.MapAnnotations.Add(new MapAnnotation
|
|
{
|
|
Id = "ma-1",
|
|
Label = EmptyTranslations(),
|
|
Type = EmptyTranslations()
|
|
});
|
|
db.SaveChanges();
|
|
|
|
var newLabel = new List<TranslationDTO> { new TranslationDTO { language = "FR", value = "Updated" } };
|
|
var result = BuildController(db).UpdateMapAnnotation(new MapAnnotationDTO
|
|
{
|
|
id = "ma-1",
|
|
label = newLabel,
|
|
type = EmptyTranslations(),
|
|
geometryType = GeometryType.Point,
|
|
geometry = null // UpdateMapAnnotation handles null geometry
|
|
});
|
|
|
|
Assert.IsType<OkObjectResult>(result);
|
|
Assert.Equal("Updated", db.MapAnnotations.First().Label.First().value);
|
|
}
|
|
}
|
|
}
|