using FluentAssertions; using Nalu.Api.Models; using Nalu.Api.Services; using Xunit; namespace Nalu.Tests; public class DeterministicLayerTests { private readonly DeterministicLayer _layer = new(); private static ValidatorDefinition NameValidator() => new ValidatorDefinition { Id = "validate_full_name", StopWords = ["bom dia", "boa tarde", "boa noite", "olá", "oi"], RejectPatterns = [ @"^(não|nao|sei la|sei lá|tanto faz)$", @"^\d+$" ], AcceptPatterns = [ @"^meu nome é\s+(.+)$", @"^me chamo\s+(.+)$" ], Constraints = new Dictionary { { "min_length", "2" }, { "must_have_alpha", "true" }, { "max_length", "120" } } }; // ── Stop words ──────────────────────────────────────────────────────────── [Theory] [InlineData("Bom dia")] [InlineData("bom dia")] [InlineData("Bom dia!")] [InlineData("Oi")] [InlineData("olá")] public void StopWord_ReturnsRejected(string input) { var result = _layer.Evaluate(NameValidator(), input); result.Outcome.Should().Be(DeterministicOutcome.Rejected); result.ExtractedValue.Should().BeNull(); } // ── Reject patterns ─────────────────────────────────────────────────────── [Theory] [InlineData("não")] [InlineData("sei la")] [InlineData("tanto faz")] [InlineData("12345")] public void RejectPattern_ReturnsRejected(string input) { var result = _layer.Evaluate(NameValidator(), input); result.Outcome.Should().Be(DeterministicOutcome.Rejected); } // ── Accept patterns ─────────────────────────────────────────────────────── [Theory] [InlineData("meu nome é João Silva", "João Silva")] [InlineData("Meu nome é Maria", "Maria")] [InlineData("me chamo Pedro", "Pedro")] public void AcceptPattern_ReturnsAcceptedWithCapturedGroup(string input, string expectedValue) { var result = _layer.Evaluate(NameValidator(), input); result.Outcome.Should().Be(DeterministicOutcome.Accepted); result.ExtractedValue.Should().Be(expectedValue); } // ── Unresolved ──────────────────────────────────────────────────────────── [Theory] [InlineData("Carlos Alberto Ferreira")] [InlineData("Ana")] public void FreeText_ReturnsUnresolved(string input) { var result = _layer.Evaluate(NameValidator(), input); result.Outcome.Should().Be(DeterministicOutcome.Unresolved); } // ── Constraints ─────────────────────────────────────────────────────────── [Fact] public void AcceptPattern_WhenValueTooShort_ReturnsConstraintFailed() { var validator = NameValidator(); validator.Constraints["min_length"] = "5"; var result = _layer.Evaluate(validator, "meu nome é Al"); result.Outcome.Should().Be(DeterministicOutcome.ConstraintFailed); result.Reasoning.Should().Contain("mínimo"); } [Fact] public void AcceptPattern_WhenValueHasNoLetters_ReturnsConstraintFailed() { var result = _layer.Evaluate(NameValidator(), "meu nome é 999"); result.Outcome.Should().Be(DeterministicOutcome.ConstraintFailed); result.Reasoning.Should().Contain("letras"); } [Fact] public void AcceptPattern_WhenValueTooLong_ReturnsConstraintFailed() { var longName = new string('A', 121); var result = _layer.Evaluate(NameValidator(), $"meu nome é {longName}"); result.Outcome.Should().Be(DeterministicOutcome.ConstraintFailed); result.Reasoning.Should().Contain("máximo"); } // ── No patterns configured ──────────────────────────────────────────────── [Fact] public void EmptyValidator_AlwaysUnresolved() { var validator = new ValidatorDefinition { Id = "empty" }; var result = _layer.Evaluate(validator, "qualquer coisa"); result.Outcome.Should().Be(DeterministicOutcome.Unresolved); } }