Premiere commit - Omika
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
using Omika.Diagnostics.Models;
|
||||
using Omika.Diagnostics.Queries;
|
||||
using Omika.Diagnostics.Tests.Fakes;
|
||||
|
||||
namespace Omika.Diagnostics.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Tâche 2 — Recherche texte bornée. Les tests s'appuient sur le
|
||||
/// <see cref="FakeDiagnosticEventRepository"/> (in-memory) ; l'implémentation
|
||||
/// PostgreSQL de production exécute les mêmes contrats via ILIKE paramétré.
|
||||
/// </summary>
|
||||
public class DiagnosticSearchTests
|
||||
{
|
||||
private static readonly DateTimeOffset From = new(2025, 1, 1, 0, 0, 0, TimeSpan.Zero);
|
||||
private static readonly DateTimeOffset To = new(2025, 2, 1, 0, 0, 0, TimeSpan.Zero);
|
||||
|
||||
private static DiagnosticEventRecord Ev(
|
||||
string? message = null,
|
||||
string? exceptionType = null,
|
||||
string? stackTrace = null,
|
||||
string eventCode = "ERR",
|
||||
DateTimeOffset? ts = null,
|
||||
string? component = null,
|
||||
string? errorCode = null,
|
||||
string level = "Error",
|
||||
string priority = "P2",
|
||||
string? appVersion = null,
|
||||
string? platform = null,
|
||||
string? actorKey = null) =>
|
||||
new()
|
||||
{
|
||||
EventId = Guid.NewGuid(),
|
||||
EventCode = eventCode,
|
||||
TimestampUtc = ts ?? From.AddMinutes(1),
|
||||
Component = component,
|
||||
ErrorCode = errorCode,
|
||||
Message = message,
|
||||
ExceptionType = exceptionType,
|
||||
StackTrace = stackTrace,
|
||||
Level = level,
|
||||
Priority = priority,
|
||||
AppVersion = appVersion,
|
||||
Platform = platform,
|
||||
ActorKey = actorKey,
|
||||
Source = DiagnosticSources.Mobile,
|
||||
IngestedAtUtc = DateTimeOffset.UtcNow
|
||||
};
|
||||
|
||||
// ------------------------------------------------------- R1 : message ----
|
||||
[Fact]
|
||||
public async Task Search_Message_ContainsTerm_IsFound()
|
||||
{
|
||||
var repo = new FakeDiagnosticEventRepository();
|
||||
await repo.InsertBatchAsync(new[]
|
||||
{
|
||||
Ev(message: "Connexion serveur impossible")
|
||||
});
|
||||
|
||||
DiagnosticQueryResult<DiagnosticEventRecord> result = await repo.SearchAsync(
|
||||
new DiagnosticSearchQuery { FromUtc = From, ToUtc = To, SearchText = "serveur" });
|
||||
|
||||
DiagnosticEventRecord item = Assert.Single(result.Items);
|
||||
Assert.Equal("Connexion serveur impossible", item.Message);
|
||||
Assert.Equal(1, result.TotalCount);
|
||||
}
|
||||
|
||||
// ------------------------------------------------- R2 : insensible casse ----
|
||||
[Fact]
|
||||
public async Task Search_IsCaseInsensitive()
|
||||
{
|
||||
var repo = new FakeDiagnosticEventRepository();
|
||||
await repo.InsertBatchAsync(new[]
|
||||
{
|
||||
Ev(exceptionType: "System.NullReferenceException")
|
||||
});
|
||||
|
||||
DiagnosticQueryResult<DiagnosticEventRecord> result = await repo.SearchAsync(
|
||||
new DiagnosticSearchQuery { FromUtc = From, ToUtc = To, SearchText = "nullreference" });
|
||||
|
||||
Assert.Single(result.Items);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------ R3 : hors période ----
|
||||
[Fact]
|
||||
public async Task Search_OutOfPeriod_IsExcluded()
|
||||
{
|
||||
var repo = new FakeDiagnosticEventRepository();
|
||||
await repo.InsertBatchAsync(new[]
|
||||
{
|
||||
Ev(message: "serveur", ts: From.AddMinutes(-5)), // avant FromUtc
|
||||
Ev(message: "serveur", ts: To.AddMinutes(5)) // après ToUtc (borne exclusive)
|
||||
});
|
||||
|
||||
DiagnosticQueryResult<DiagnosticEventRecord> result = await repo.SearchAsync(
|
||||
new DiagnosticSearchQuery { FromUtc = From, ToUtc = To, SearchText = "serveur" });
|
||||
|
||||
Assert.Empty(result.Items);
|
||||
Assert.Equal(0, result.TotalCount);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------- R4 : filtres ----
|
||||
[Fact]
|
||||
public async Task Search_RespectsOtherFilters()
|
||||
{
|
||||
var repo = new FakeDiagnosticEventRepository();
|
||||
await repo.InsertBatchAsync(new[]
|
||||
{
|
||||
Ev(message: "serveur", component: "Omika.Api"),
|
||||
Ev(message: "serveur", component: "Omika.Worker")
|
||||
});
|
||||
|
||||
DiagnosticQueryResult<DiagnosticEventRecord> result = await repo.SearchAsync(
|
||||
new DiagnosticSearchQuery
|
||||
{
|
||||
FromUtc = From,
|
||||
ToUtc = To,
|
||||
SearchText = "serveur",
|
||||
Component = "Omika.Api"
|
||||
});
|
||||
|
||||
DiagnosticEventRecord item = Assert.Single(result.Items);
|
||||
Assert.Equal("Omika.Api", item.Component);
|
||||
}
|
||||
|
||||
// -------------------------------------- R5 : recherche vide/trop courte ----
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
[InlineData(" ")]
|
||||
[InlineData("a")] // < MinSearchLength (2) → recherche désactivée
|
||||
public async Task Search_EmptyOrTooShort_DisablesTextFilter(string? searchText)
|
||||
{
|
||||
var repo = new FakeDiagnosticEventRepository();
|
||||
await repo.InsertBatchAsync(new[]
|
||||
{
|
||||
Ev(message: "Connexion serveur impossible"),
|
||||
Ev(message: "Autre message")
|
||||
});
|
||||
|
||||
DiagnosticQueryResult<DiagnosticEventRecord> result = await repo.SearchAsync(
|
||||
new DiagnosticSearchQuery { FromUtc = From, ToUtc = To, SearchText = searchText });
|
||||
|
||||
// Pas de filtre texte : tous les événements de la période sont retournés
|
||||
// (jamais un ILIKE '%%').
|
||||
Assert.Equal(2, result.TotalCount);
|
||||
}
|
||||
|
||||
// ----------------------------------------- exception_type recherchable ----
|
||||
[Fact]
|
||||
public async Task Search_MatchesExceptionType()
|
||||
{
|
||||
var repo = new FakeDiagnosticEventRepository();
|
||||
await repo.InsertBatchAsync(new[]
|
||||
{
|
||||
Ev(message: "échec", exceptionType: "System.TimeoutException")
|
||||
});
|
||||
|
||||
DiagnosticQueryResult<DiagnosticEventRecord> result = await repo.SearchAsync(
|
||||
new DiagnosticSearchQuery { FromUtc = From, ToUtc = To, SearchText = "timeout" });
|
||||
|
||||
Assert.Single(result.Items);
|
||||
}
|
||||
|
||||
// ------------------------------------------------ pagination serveur ----
|
||||
[Fact]
|
||||
public async Task Search_RespectsPagination()
|
||||
{
|
||||
var repo = new FakeDiagnosticEventRepository();
|
||||
await repo.InsertBatchAsync(new[]
|
||||
{
|
||||
Ev(message: "serveur A", ts: From.AddMinutes(1)),
|
||||
Ev(message: "serveur B", ts: From.AddMinutes(2)),
|
||||
Ev(message: "serveur C", ts: From.AddMinutes(3))
|
||||
});
|
||||
|
||||
DiagnosticQueryResult<DiagnosticEventRecord> result = await repo.SearchAsync(
|
||||
new DiagnosticSearchQuery
|
||||
{
|
||||
FromUtc = From,
|
||||
ToUtc = To,
|
||||
SearchText = "serveur",
|
||||
Page = 2,
|
||||
PageSize = 1
|
||||
});
|
||||
|
||||
Assert.Equal(3, result.TotalCount);
|
||||
DiagnosticEventRecord item = Assert.Single(result.Items);
|
||||
Assert.Equal("serveur B", item.Message); // tri timestamp DESC → page 2 = 2e plus récent
|
||||
}
|
||||
|
||||
// ------------------------------------------ validation période obligatoire ----
|
||||
[Theory]
|
||||
[InlineData(true, false)]
|
||||
[InlineData(false, true)]
|
||||
public async Task Search_Validate_RejectsMissingPeriod(bool missingFrom, bool missingTo)
|
||||
{
|
||||
var repo = new FakeDiagnosticEventRepository();
|
||||
var query = new DiagnosticSearchQuery
|
||||
{
|
||||
FromUtc = missingFrom ? null : From,
|
||||
ToUtc = missingTo ? null : To,
|
||||
SearchText = "serveur"
|
||||
};
|
||||
|
||||
await Assert.ThrowsAsync<ArgumentException>(() => repo.SearchAsync(query));
|
||||
}
|
||||
|
||||
// ---------------------------------------- joker % littéral (pas un joker) ----
|
||||
[Fact]
|
||||
public async Task Search_PercentIsLiteral()
|
||||
{
|
||||
var repo = new FakeDiagnosticEventRepository();
|
||||
await repo.InsertBatchAsync(new[]
|
||||
{
|
||||
Ev(message: "100% réussi"),
|
||||
Ev(message: "sans pourcent")
|
||||
});
|
||||
|
||||
DiagnosticQueryResult<DiagnosticEventRecord> result = await repo.SearchAsync(
|
||||
new DiagnosticSearchQuery { FromUtc = From, ToUtc = To, SearchText = "100%" });
|
||||
|
||||
DiagnosticEventRecord item = Assert.Single(result.Items);
|
||||
Assert.Equal("100% réussi", item.Message);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user