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>
69 lines
2.3 KiB
C#
69 lines
2.3 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
|
|
{
|
|
/// <summary>
|
|
/// La purge s'appuie sur <c>ExecuteDeleteAsync</c>, que le provider InMemory ne sait pas
|
|
/// traduire — un test là-bas ne dirait rien de ce qui se passe en production. Ceux-ci
|
|
/// s'exécutent contre un vrai PostgreSQL, et se sautent proprement sans démon Docker.
|
|
/// </summary>
|
|
[Collection(PostgresCollection.Name)]
|
|
public class AuditLogPurgePostgresTests
|
|
{
|
|
private readonly PostgresFixture _postgres;
|
|
|
|
public AuditLogPurgePostgresTests(PostgresFixture postgres)
|
|
{
|
|
_postgres = postgres;
|
|
}
|
|
|
|
private static AuditLogPurgeService BuildService(MyInfoMateDbContext db, int retentionDays)
|
|
{
|
|
var configuration = new ConfigurationBuilder()
|
|
.AddInMemoryCollection(new Dictionary<string, string>
|
|
{
|
|
["Audit:RetentionDays"] = retentionDays.ToString()
|
|
})
|
|
.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
|
|
};
|
|
|
|
[SkippableFact]
|
|
public async Task PurgeAsync_DeletesOnlyBeyondTheWindow()
|
|
{
|
|
Skip.IfNot(_postgres.Available, _postgres.SkipReason);
|
|
|
|
using var db = _postgres.CreateMigratedContext("audit_purge");
|
|
db.AuditLogs.AddRange(
|
|
Entry("vieux", DateTime.UtcNow.AddDays(-400)),
|
|
Entry("recent", DateTime.UtcNow.AddDays(-30)));
|
|
db.SaveChanges();
|
|
|
|
await BuildService(db, AuditLogPurgeService.DefaultRetentionDays).PurgeAsync();
|
|
|
|
Assert.Equal("recent", Assert.Single(db.AuditLogs).Id);
|
|
}
|
|
}
|
|
}
|