NALU/tests/Nalu.Tests/DeterministicLayerTests.cs
Ricardo Carneiro ea6cdb5395 Initial commit — NALU AI web platform
- ASP.NET Core 9 Razor Pages + Minimal API hybrid
- 14 validators (CPF, CEP, CNPJ, email, phone, name, yes-no, birthdate, handoff, cancel-intent, company-name, plate-br, postal-code, validate_reply)
- OAuth login (Google, Microsoft, GitHub) + cookie auth
- MongoDB usage tracking + CEP cache collection
- Stripe checkout with inline PriceData (no Price IDs)
- MCP server for Claude Code / Cursor integration
- Playground (10 calls/IP/day, no auth)
- Docs: Quickstart, API Reference, N8N, MCP, Créditos, Erros, Fluxos
- Credit system: 3 cr standard validators, 5 cr validate_reply
- SmartSuggestion: contextual re-ask via IA when obtained=false
- Per-IP rate limiting + daily cap + shared-IP abuse detection
- Lightbox for comic images
- Validadores page split: Brasileiros / Universais + Em breve

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 16:39:04 -03:00

134 lines
4.7 KiB
C#

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<string, string>
{
{ "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);
}
}