manager-service/ManagerService.Tests/Services/AuditLogPurgeServiceTests.cs
Thomas Fransolet cb76df5ddf Purge du journal d'audit — poste du lot J oublié à la première passe
AuditLog porte un UserId, et les valeurs avant/après d'une modification de User
contiennent e-mail, prénom et nom : conservés sans limite jusqu'ici, quand
VisitEvent purge à 13 mois et VisitorQuestion à 90 jours. Le plan l'assignait
explicitement au lot J, il n'avait pas été fait.

12 mois, uniforme. Pas 13 : les 13 mois des statistiques existent pour comparer
une saison à la précédente, les recopier ici serait du mimétisme — un test fige
l'écart voulu. Écarté aussi : garder les suppressions plus longtemps que les
créations, ça double les règles pour un cas qu'une sauvegarde couvre déjà.

Inerte tant qu'Audit:RetentionDays n'est pas défini, même verrou que les
VisitEvent : c'est une suppression définitive et le pg_dump n'est pas en place.
Volontairement différent de VisitorQuestionPurgeService, qui tourne sans
condition parce que ses 90 jours sont un engagement des CGU, pas un réglage.

La suppression se teste contre un vrai Postgres : ExecuteDeleteAsync n'est pas
traduisible par le provider InMemory, et l'écrire en chargeant les lignes puis
RemoveRange l'aurait rendue testable en mémoire au prix de charger un an de
journal — dégrader le code de production pour satisfaire un provider de test.

dotnet test : 213 passés, 16 sautés, 0 échec.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 16:29:35 +02:00

72 lines
2.8 KiB
C#

using ManagerService.Data;
using ManagerService.Services;
using ManagerService.Tests.Infrastructure;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
namespace ManagerService.Tests.Services
{
public class AuditLogPurgeServiceTests
{
private static AuditLogPurgeService BuildService(MyInfoMateDbContext db, int? retentionDays)
{
var settings = new Dictionary<string, string>();
if (retentionDays != null)
settings["Audit:RetentionDays"] = retentionDays.ToString();
var configuration = new ConfigurationBuilder().AddInMemoryCollection(settings).Build();
return new AuditLogPurgeService(db, configuration,
NullLogger<AuditLogPurgeService>.Instance);
}
private static AuditLog Entry(string id, DateTime timestamp) => new AuditLog
{
Id = id,
EntityType = "User",
EntityId = "u1",
Action = "Update",
UserId = "u1",
Timestamp = timestamp
};
/// <summary>
/// Le verrou : sans configuration, rien n'est supprimé. C'est une suppression
/// définitive et le pg_dump quotidien n'est pas en place — un job de purge qui
/// partirait tout seul est exactement ce qu'on ne veut pas.
/// </summary>
[Fact]
public async Task PurgeAsync_WithoutConfiguration_DeletesNothing()
{
using var db = DbContextFactory.Create();
db.AuditLogs.Add(Entry("a1", DateTime.UtcNow.AddYears(-5)));
db.SaveChanges();
await BuildService(db, null).PurgeAsync();
Assert.Single(db.AuditLogs);
}
// ⚠️ La suppression elle-même se teste contre un vrai PostgreSQL, pas ici :
// `ExecuteDeleteAsync` n'est pas traduisible par le provider InMemory. Écrire la
// purge en chargeant les lignes puis `RemoveRange` la rendrait testable ici au prix
// de charger un an de journal en mémoire — dégrader le code de production pour
// satisfaire un provider de test. Voir AuditLogPurgePostgresTests.
/// <summary>
/// 12 mois, pas 13 : les 13 mois des statistiques existent pour comparer une saison à
/// la précédente, les recopier ici serait du mimétisme. Ce test fige l'écart voulu.
/// </summary>
[Fact]
public void DefaultRetention_IsTwelveMonths_NotThirteen()
{
Assert.Equal(365, AuditLogPurgeService.DefaultRetentionDays);
Assert.NotEqual(MyInfoMateDbContext.StatsRetentionDays, AuditLogPurgeService.DefaultRetentionDays);
}
}
}