Files
Omika/tests/Omika.Diagnostics.Tests/DiagnosticDeploymentFilterTests.cs
2026-10-23 18:46:50 +02:00

243 lines
9.6 KiB
C#

using Omika.Diagnostics.Models;
using Omika.Diagnostics.Queries;
using Omika.Diagnostics.Tests.Fakes;
namespace Omika.Diagnostics.Tests;
/// <summary>
/// Tâche 13-F — Filtres de contexte de déploiement (ClientCode / ServerInstance /
/// Environment) : filtres backend (F1-F5), regroupement d'erreurs pré-agrégation
/// (F6), et valeurs distinctes bornées/ordonnées/sans doublon (F7-F10). Le tout sur
/// le <see cref="FakeDiagnosticEventRepository"/> (in-memory), miroir du repository
/// PostgreSQL réel (mêmes contrats, §51).
/// </summary>
public class DiagnosticDeploymentFilterTests
{
private static readonly DateTimeOffset From = new(2025, 1, 1, 0, 0, 0, TimeSpan.Zero);
private static DiagnosticEventRecord Ev(
string eventCode = "TEST",
string? clientCode = null,
string? serverInstance = null,
string? environment = null,
DateTimeOffset? ts = null,
string level = "Info",
string? exceptionType = null,
string? component = null,
string? errorCode = null)
{
DiagnosticEventRecord record = DiagnosticTestData.Event(Guid.NewGuid(), eventCode, ts ?? From, component: component, level: level, errorCode: errorCode);
record.ClientCode = clientCode;
record.ServerInstance = serverInstance;
record.Environment = environment;
record.ExceptionType = exceptionType;
return record;
}
// --------------------------- F1 : filtre ClientCode --------------------------
[Fact]
public async Task F1_ClientCodeFilter_ReturnsOnlyMatchingClient()
{
var repo = new FakeDiagnosticEventRepository();
await repo.InsertBatchAsync(new[]
{
Ev(clientCode: "CLIENT-A"),
Ev(clientCode: "CLIENT-B")
});
DiagnosticQueryResult<DiagnosticEventRecord> result =
await repo.QueryAsync(new DiagnosticEventQuery { ClientCode = "CLIENT-A" });
DiagnosticEventRecord record = Assert.Single(result.Items);
Assert.Equal("CLIENT-A", record.ClientCode);
}
// --------------------------- F2 : filtre ServerInstance ----------------------
[Fact]
public async Task F2_ServerInstanceFilter_ReturnsOnlyMatchingInstance()
{
var repo = new FakeDiagnosticEventRepository();
await repo.InsertBatchAsync(new[]
{
Ev(serverInstance: "WKR-SRV-01"),
Ev(serverInstance: "API-SRV-01")
});
DiagnosticQueryResult<DiagnosticEventRecord> result =
await repo.QueryAsync(new DiagnosticEventQuery { ServerInstance = "WKR-SRV-01" });
DiagnosticEventRecord record = Assert.Single(result.Items);
Assert.Equal("WKR-SRV-01", record.ServerInstance);
}
// --------------------------- F3 : filtre Environment -------------------------
[Fact]
public async Task F3_EnvironmentFilter_ExcludesOtherEnvironments()
{
var repo = new FakeDiagnosticEventRepository();
await repo.InsertBatchAsync(new[]
{
Ev(environment: "Production"),
Ev(environment: "Staging"),
Ev(environment: "Development")
});
DiagnosticQueryResult<DiagnosticEventRecord> result =
await repo.QueryAsync(new DiagnosticEventQuery { Environment = "Production" });
DiagnosticEventRecord record = Assert.Single(result.Items);
Assert.Equal("Production", record.Environment);
}
// --------------------------- F4 : combinaison des trois ----------------------
[Fact]
public async Task F4_CombinedFilters_ApplyAllThreeSimultaneously()
{
var repo = new FakeDiagnosticEventRepository();
await repo.InsertBatchAsync(new[]
{
Ev(clientCode: "CLIENT-A", serverInstance: "API-SRV-01", environment: "Production"),
Ev(clientCode: "CLIENT-A", serverInstance: "API-SRV-01", environment: "Staging"),
Ev(clientCode: "CLIENT-A", serverInstance: "WKR-SRV-01", environment: "Production"),
Ev(clientCode: "CLIENT-B", serverInstance: "API-SRV-01", environment: "Production")
});
DiagnosticQueryResult<DiagnosticEventRecord> result = await repo.QueryAsync(new DiagnosticEventQuery
{
ClientCode = "CLIENT-A",
ServerInstance = "API-SRV-01",
Environment = "Production"
});
DiagnosticEventRecord record = Assert.Single(result.Items);
Assert.Equal("CLIENT-A", record.ClientCode);
Assert.Equal("API-SRV-01", record.ServerInstance);
Assert.Equal("Production", record.Environment);
}
// --------------------------- F5 : NULL historiques ---------------------------
[Fact]
public async Task F5_HistoricalNullContext_IsReturnedWithoutFilter()
{
var repo = new FakeDiagnosticEventRepository();
DiagnosticEventRecord historical = Ev(clientCode: null, serverInstance: null, environment: null);
await repo.InsertBatchAsync(new[]
{
historical,
Ev(clientCode: "CLIENT-A", serverInstance: "SRV-1", environment: "Production")
});
DiagnosticQueryResult<DiagnosticEventRecord> result = await repo.QueryAsync(new DiagnosticEventQuery());
Assert.Equal(2, result.TotalCount);
Assert.Contains(result.Items, e => e.ClientCode is null && e.ServerInstance is null && e.Environment is null);
}
// --------------------------- F6 : ErrorGroup pré-agrégation ------------------
[Fact]
public async Task F6_ErrorGroupFilter_IsAppliedBeforeAggregation()
{
var repo = new FakeDiagnosticEventRepository();
// Même signature d'erreur logique (event_code + exception + component) répartie
// sur deux clients / environnements.
await repo.InsertBatchAsync(new[]
{
Ev("ERR", "CLIENT-A", "SRV-1", "Production", exceptionType: "System.E", component: "Omika.Api", level: "Error"),
Ev("ERR", "CLIENT-A", "SRV-1", "Production", exceptionType: "System.E", component: "Omika.Api", level: "Error"),
Ev("ERR", "CLIENT-B", "SRV-1", "Staging", exceptionType: "System.E", component: "Omika.Api", level: "Error"),
Ev("ERR", "CLIENT-A", "SRV-1", "Staging", exceptionType: "System.E", component: "Omika.Api", level: "Error")
});
IReadOnlyList<DiagnosticErrorGroup> all = await repo.GroupErrorsAsync(
new DiagnosticErrorGroupQuery { FromUtc = From, ToUtc = From.AddDays(1) });
DiagnosticErrorGroup allGroup = Assert.Single(all);
Assert.Equal(4, allGroup.OccurrenceCount);
IReadOnlyList<DiagnosticErrorGroup> filtered = await repo.GroupErrorsAsync(
new DiagnosticErrorGroupQuery
{
FromUtc = From,
ToUtc = From.AddDays(1),
ClientCode = "CLIENT-A",
Environment = "Production"
});
DiagnosticErrorGroup filteredGroup = Assert.Single(filtered);
Assert.Equal(2, filteredGroup.OccurrenceCount);
}
// --------------------------- F7 : distinct ClientCode ------------------------
[Fact]
public async Task F7_DistinctClientCodes_AreDistinctSorted()
{
var repo = new FakeDiagnosticEventRepository();
await repo.InsertBatchAsync(new[]
{
Ev(clientCode: "CLIENT-B"),
Ev(clientCode: "CLIENT-A"),
Ev(clientCode: "CLIENT-A"),
Ev(clientCode: null) // exclu
});
IReadOnlyList<string> result = await repo.GetFilterOptionsAsync(
DiagnosticFilterDimension.ClientCode, From, DiagnosticFilterDefaults.ClientCodeOptionsLimit);
Assert.Equal(new[] { "CLIENT-A", "CLIENT-B" }, result);
}
// --------------------------- F8 : distinct ServerInstance --------------------
[Fact]
public async Task F8_DistinctServerInstances_AreDistinctSorted()
{
var repo = new FakeDiagnosticEventRepository();
await repo.InsertBatchAsync(new[]
{
Ev(serverInstance: "WKR-SRV-01"),
Ev(serverInstance: "API-SRV-01"),
Ev(serverInstance: "WKR-SRV-01")
});
IReadOnlyList<string> result = await repo.GetFilterOptionsAsync(
DiagnosticFilterDimension.ServerInstance, From, DiagnosticFilterDefaults.ServerInstanceOptionsLimit);
Assert.Equal(new[] { "API-SRV-01", "WKR-SRV-01" }, result);
}
// --------------------------- F9 : distinct Environment -----------------------
[Fact]
public async Task F9_DistinctEnvironments_AreDistinctSorted()
{
var repo = new FakeDiagnosticEventRepository();
await repo.InsertBatchAsync(new[]
{
Ev(environment: "Production"),
Ev(environment: "Development"),
Ev(environment: "Production"),
Ev(environment: "Staging")
});
IReadOnlyList<string> result = await repo.GetFilterOptionsAsync(
DiagnosticFilterDimension.Environment, From, DiagnosticFilterDefaults.EnvironmentOptionsLimit);
Assert.Equal(new[] { "Development", "Production", "Staging" }, result);
}
// --------------------------- F10 : limite des options ------------------------
[Fact]
public async Task F10_DistinctOptions_RespectLimit()
{
var repo = new FakeDiagnosticEventRepository();
await repo.InsertBatchAsync(new[]
{
Ev(clientCode: "CLIENT-A"),
Ev(clientCode: "CLIENT-B"),
Ev(clientCode: "CLIENT-C"),
Ev(clientCode: "CLIENT-D")
});
IReadOnlyList<string> result = await repo.GetFilterOptionsAsync(
DiagnosticFilterDimension.ClientCode, From, 2);
Assert.Equal(2, result.Count);
}
}