243 lines
9.6 KiB
C#
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);
|
|
}
|
|
}
|