using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Audit;
using ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Maintenance;
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests.Maintenance;
/// Tests for the opt-in config-audit retention purge (review 2026-10-21 08/G1).
public class ConfigAuditPurgeServiceTests : IDisposable
{
private readonly ScadaBridgeDbContext _context = SqliteTestHelper.CreateInMemoryContext();
public void Dispose()
{
_context.Database.CloseConnection();
_context.Dispose();
}
private ConfigAuditPurgeService Create(ConfigAuditOptions opts)
{
var services = new ServiceCollection();
services.AddSingleton(_context);
return new ConfigAuditPurgeService(
services.BuildServiceProvider().GetRequiredService(),
Options.Create(opts),
TimeProvider.System,
NullLogger.Instance);
}
private async Task SeedAsync(int oldCount, int recentCount)
{
var now = DateTimeOffset.UtcNow;
for (var i = 0; i < oldCount; i++)
_context.AuditLogEntries.Add(new AuditLogEntry("u", "Create", "Template", $"o{i}", $"Old{i}") { Timestamp = now.AddDays(-100).AddMinutes(i) });
for (var i = 0; i < recentCount; i++)
_context.AuditLogEntries.Add(new AuditLogEntry("u", "Create", "Template", $"r{i}", $"New{i}") { Timestamp = now.AddDays(-1) });
await _context.SaveChangesAsync();
}
[Fact]
public async Task RetentionZero_DeletesNothing()
{
await SeedAsync(5, 2);
var deleted = await Create(new ConfigAuditOptions { RetentionDays = 0 }).PurgeOnceAsync();
Assert.Equal(0, deleted);
Assert.Equal(7, _context.AuditLogEntries.Count());
}
[Fact]
public async Task Retention_DeletesOnlyOlderThanWindow_AcrossBatches()
{
await SeedAsync(25, 3);
var deleted = await Create(new ConfigAuditOptions { RetentionDays = 30, PurgeBatchSize = 10 }).PurgeOnceAsync();
Assert.Equal(25, deleted);
Assert.Equal(3, _context.AuditLogEntries.Count());
}
[Fact]
public async Task Retention_MaxBatchesPerTick_TimeSlicesTheBacklog()
{
await SeedAsync(25, 0);
var svc = Create(new ConfigAuditOptions { RetentionDays = 30, PurgeBatchSize = 10, MaxBatchesPerTick = 2 });
Assert.Equal(20, await svc.PurgeOnceAsync());
Assert.Equal(5, await svc.PurgeOnceAsync());
}
[Fact]
public void Validator_RejectsNegativeRetention_AcceptsZero()
{
var v = new ConfigAuditOptionsValidator();
Assert.True(v.Validate(null, new ConfigAuditOptions { RetentionDays = 0 }).Succeeded);
Assert.True(v.Validate(null, new ConfigAuditOptions { RetentionDays = 90 }).Succeeded);
Assert.False(v.Validate(null, new ConfigAuditOptions { RetentionDays = -1 }).Succeeded);
Assert.False(v.Validate(null, new ConfigAuditOptions { PurgeBatchSize = 0 }).Succeeded);
}
}