using Omika.Diagnostics.Models; using Omika.Diagnostics.Repositories; namespace Omika.Diagnostics.Tests.Fakes; /// /// Repository télémétrie in-memory (sans PostgreSQL) : reproduit le contrat /// ITelemetryMetricRepository (upsert + purge bornée par bucket horaire). /// public sealed class FakeTelemetryMetricRepository : ITelemetryMetricRepository { private readonly List _all = new(); private readonly object _sync = new(); private long _nextId = 1; public IReadOnlyList All { get { lock (_sync) { return _all.ToList(); } } } public Task UpsertHourlyAsync( IReadOnlyList metrics, CancellationToken cancellationToken = default) { lock (_sync) { foreach (TelemetryMetricHourly m in metrics) { m.Id = _nextId++; _all.Add(m); } } return Task.CompletedTask; } public Task DeleteOlderThanAsync( DateTimeOffset cutoff, int batchSize, CancellationToken cancellationToken = default) { if (batchSize <= 0) { return Task.FromResult(0); } lock (_sync) { List toDelete = _all .Where(m => m.BucketUtc < cutoff) .OrderBy(m => m.BucketUtc) .ThenBy(m => m.Id) .Take(batchSize) .ToList(); foreach (TelemetryMetricHourly m in toDelete) { _all.Remove(m); } return Task.FromResult(toDelete.Count); } } } /// /// Simule PostgreSQL DOWN pour la télémétrie : toutes les opérations lèvent. /// La boucle de rétention doit survivre (fail-open). /// public sealed class ThrowingTelemetryMetricRepository : ITelemetryMetricRepository { public Task UpsertHourlyAsync( IReadOnlyList metrics, CancellationToken cancellationToken = default) => throw new InvalidOperationException("PostgreSQL DOWN simulé"); public Task DeleteOlderThanAsync( DateTimeOffset cutoff, int batchSize, CancellationToken cancellationToken = default) => throw new InvalidOperationException("PostgreSQL DOWN simulé"); }