using Omika.Core.Observability.Outbox; namespace Omika.Tests.Observability.Outbox; /// Tests de la validation stricte serveur (§32, §28, §29, §60). public class DiagnosticBatchValidatorTests { private static readonly RemoteDiagnosticOptions Limits = new(); private static DiagnosticBatchDto NewBatch(int eventCount = 1) => new() { SchemaVersion = 1, Events = Enumerable.Range(0, eventCount) .Select(i => new DiagnosticEventDto { EventId = Guid.NewGuid(), EventCode = "OMK-MOB-APP-001", TimestampUtc = DateTimeOffset.UtcNow, Level = "Info", Priority = "P3" }) .ToList() }; [Fact] public void ValidBatch_Passes() { DiagnosticValidationResult result = DiagnosticBatchValidator.Validate(NewBatch(), Limits); Assert.True(result.IsValid); } [Fact] public void NullBatch_Rejected() { Assert.False(DiagnosticBatchValidator.Validate(null, Limits).IsValid); } [Fact] public void WrongSchemaVersion_Rejected() { DiagnosticBatchDto batch = NewBatch(); batch = new DiagnosticBatchDto { SchemaVersion = 999, Events = batch.Events }; Assert.False(DiagnosticBatchValidator.Validate(batch, Limits).IsValid); } [Fact] public void EmptyBatch_Rejected() { Assert.False(DiagnosticBatchValidator.Validate(new DiagnosticBatchDto(), Limits).IsValid); } [Fact] public void TooManyEvents_Rejected() { DiagnosticBatchDto batch = NewBatch(Limits.MaxEventsPerBatch + 1); Assert.False(DiagnosticBatchValidator.Validate(batch, Limits).IsValid); } [Fact] public void EmptyEventId_Rejected() { DiagnosticBatchDto batch = NewBatch(); batch.Events[0] = new DiagnosticEventDto { EventCode = "OMK-MOB-APP-001" }; Assert.False(DiagnosticBatchValidator.Validate(batch, Limits).IsValid); } [Fact] public void ForbiddenMetadataKey_Rejected() { DiagnosticBatchDto batch = NewBatch(); batch.Events[0] = new DiagnosticEventDto { EventId = Guid.NewGuid(), EventCode = "OMK-MOB-APP-001", Metadata = new Dictionary { ["password"] = "secret" } }; DiagnosticValidationResult result = DiagnosticBatchValidator.Validate(batch, Limits); Assert.False(result.IsValid); Assert.Equal(DiagnosticValidationReason.ForbiddenMetadata, result.Reason); } [Fact] public void ForbiddenMetadataValue_Rejected() { DiagnosticBatchDto batch = NewBatch(); batch.Events[0] = new DiagnosticEventDto { EventId = Guid.NewGuid(), EventCode = "OMK-MOB-APP-001", Metadata = new Dictionary { ["error"] = "ConnectionString=secret" } }; Assert.False(DiagnosticBatchValidator.Validate(batch, Limits).IsValid); } [Fact] public void TooManyMetadata_Rejected() { DiagnosticBatchDto batch = NewBatch(); var metadata = new Dictionary(); for (int i = 0; i <= Limits.MaxMetadataCount; i++) { metadata[$"k{i}"] = i; } batch.Events[0] = new DiagnosticEventDto { EventId = Guid.NewGuid(), EventCode = "OMK-MOB-APP-001", Metadata = metadata }; Assert.False(DiagnosticBatchValidator.Validate(batch, Limits).IsValid); } [Fact] public void OverlongString_Rejected() { DiagnosticBatchDto batch = NewBatch(); batch.Events[0] = new DiagnosticEventDto { EventId = Guid.NewGuid(), EventCode = "OMK-MOB-APP-001", Component = new string('x', Limits.MaxStringLength + 1) }; Assert.False(DiagnosticBatchValidator.Validate(batch, Limits).IsValid); } [Fact] public void ComplexMetadataValue_Rejected() { // Une valeur de métadonnée objet/tableau (non simple) est refusée. DiagnosticBatchDto batch = NewBatch(); batch.Events[0] = new DiagnosticEventDto { EventId = Guid.NewGuid(), EventCode = "OMK-MOB-APP-001", Metadata = new Dictionary { ["nested"] = new { a = 1 } } }; Assert.False(DiagnosticBatchValidator.Validate(batch, Limits).IsValid); } }