NALU/tests/Nalu.Tests/NewValidatorsTests.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

169 lines
5.9 KiB
C#

using FluentAssertions;
using Nalu.Api.PostProcessors;
using Nalu.Api.Services;
using Xunit;
namespace Nalu.Tests;
// ── ValidateCpfDigit ──────────────────────────────────────────────────────────
public class ValidateCpfDigitTests
{
private readonly ValidateCpfDigit _sut = new();
[Theory]
[InlineData("529.982.247-25", "529.982.247-25")] // valid CPF
[InlineData("52998224725", "529.982.247-25")] // no formatting
[InlineData("529 982 247 25", "529.982.247-25")] // spaces
public void ValidCpf_ReturnsFormattedValue(string input, string expected)
{
var result = _sut.Process(input);
result.IsValid.Should().BeTrue();
result.Value.Should().Be(expected);
}
[Theory]
[InlineData("111.111.111-11")] // all same digit
[InlineData("000.000.000-00")]
[InlineData("123.456.789-00")] // wrong check digits
[InlineData("1234567890")] // too few digits
[InlineData("123456789012")] // too many digits
public void InvalidCpf_ReturnsInvalid(string input)
{
var result = _sut.Process(input);
result.IsValid.Should().BeFalse();
result.InvalidReason.Should().NotBeNullOrEmpty();
}
}
// ── FormatPhone ───────────────────────────────────────────────────────────────
public class FormatPhoneTests
{
private readonly FormatPhone _sut = new();
[Theory]
[InlineData("11999998888", "(11) 99999-8888")] // mobile, no formatting
[InlineData("(11) 99999-8888", "(11) 99999-8888")] // already formatted
[InlineData("+55 11 99999-8888", "(11) 99999-8888")] // with country code
[InlineData("1134567890", "(11) 3456-7890")] // landline
public void ValidPhone_ReturnsFormatted(string input, string expected)
{
var result = _sut.Process(input);
result.IsValid.Should().BeTrue();
result.Value.Should().Be(expected);
}
[Theory]
[InlineData("9999")] // too short
[InlineData("999999999999999")] // too long
[InlineData("1190909090909")] // 13 digits — no valid Brazilian format
[InlineData("23999998888")] // invalid DDD 23
[InlineData("36999998888")] // invalid DDD 36
[InlineData("52999998888")] // invalid DDD 52
public void InvalidPhone_ReturnsInvalid(string input)
{
var result = _sut.Process(input);
result.IsValid.Should().BeFalse();
}
}
// ── FormatCep ─────────────────────────────────────────────────────────────────
public class FormatCepTests
{
private readonly FormatCep _sut = new();
[Theory]
[InlineData("01001000", "01001-000")]
[InlineData("01001-000", "01001-000")]
[InlineData("04538133", "04538-133")]
public void ValidCep_ReturnsFormatted(string input, string expected)
{
var result = _sut.Process(input);
result.IsValid.Should().BeTrue();
result.Value.Should().Be(expected);
}
[Theory]
[InlineData("0000000")] // 7 digits
[InlineData("000000000")] // 9 digits
[InlineData("00000000")] // all zeros
public void InvalidCep_ReturnsInvalid(string input)
{
var result = _sut.Process(input);
result.IsValid.Should().BeFalse();
}
}
// ── CorrectEmailTypos ─────────────────────────────────────────────────────────
public class CorrectEmailTyposTests
{
private readonly CorrectEmailTypos _sut = new();
[Theory]
[InlineData("user@gamil.com", "user@gmail.com", "user@gamil.com")]
[InlineData("user@gmaill.com", "user@gmail.com", "user@gmaill.com")]
[InlineData("user@hotmal.com", "user@hotmail.com", "user@hotmal.com")]
[InlineData("user@outlok.com", "user@outlook.com", "user@outlok.com")]
[InlineData("user@yaho.com", "user@yahoo.com", "user@yaho.com")]
public void TypoDomain_ReturnsCorrected(string input, string expectedValue, string expectedOriginal)
{
var result = _sut.Process(input);
result.IsValid.Should().BeTrue();
result.WasCorrected.Should().BeTrue();
result.Value.Should().Be(expectedValue);
result.OriginalValue.Should().Be(expectedOriginal);
}
[Theory]
[InlineData("user@gmail.com")]
[InlineData("user@hotmail.com")]
[InlineData("user@company.com.br")]
public void CorrectDomain_ReturnsOk_NotCorrected(string input)
{
var result = _sut.Process(input);
result.IsValid.Should().BeTrue();
result.WasCorrected.Should().BeFalse();
}
}
// ── DeterministicLayer — new validators ───────────────────────────────────────
public class CpfDeterministicTests
{
private readonly DeterministicLayer _layer = new();
[Fact]
public void CpfPattern_ExtractsDigits()
{
var validator = ValidatorLoader.ParseFromContent("validate_cpf",
System.IO.File.ReadAllText("../../../../../src/Nalu.Api/Validators/validate_cpf.md"));
var result = _layer.Evaluate(validator, "123.456.789-09");
result.Outcome.Should().Be(DeterministicOutcome.Accepted);
result.ExtractedValue.Should().Contain("12345678909");
}
[Fact]
public void CpfEvasive_ReturnsRejected()
{
var validator = ValidatorLoader.ParseFromContent("validate_cpf",
System.IO.File.ReadAllText("../../../../../src/Nalu.Api/Validators/validate_cpf.md"));
var result = _layer.Evaluate(validator, "não lembro");
result.Outcome.Should().Be(DeterministicOutcome.Rejected);
}
}