// ============================================================
// TEST TEMPLATE (VSA Pattern — Handler + Validator)
// Generated by MORPH Framework
// ============================================================
using FluentAssertions;
using FluentValidation;
using FluentValidation.Results;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using {{pascalCase PROJECT_NAME}}.Database;
using {{pascalCase PROJECT_NAME}}.Entities;
using Xunit;
namespace {{pascalCase PROJECT_NAME}}.Tests.Unit.Features.{{pascalCase FEATURE_NAME}};
// ============================================================
// Handler Tests
// ============================================================
///
/// Unit tests for {{pascalCase FEATURE_NAME}} handlers.
/// Tests handlers directly against a real ApplicationDbContext backed by
/// in-memory SQLite (no mocks — DbSet is the repository, DbContext is the unit of work).
///
public class {{pascalCase FEATURE_NAME}}HandlerTests : IDisposable
{
private readonly SqliteConnection _connection;
private readonly ApplicationDbContext _db;
public {{pascalCase FEATURE_NAME}}HandlerTests()
{
_connection = new SqliteConnection("DataSource=:memory:");
_connection.Open();
var options = new DbContextOptionsBuilder()
.UseSqlite(_connection)
.Options;
_db = new ApplicationDbContext(options);
_db.Database.EnsureCreated();
}
public void Dispose()
{
_db.Dispose();
_connection.Dispose();
}
#region Create Handler Tests
[Fact]
public async Task Create_HandleAsync_WithValidCommand_ReturnsSuccessResult()
{
// Arrange
var command = new Create{{pascalCase FEATURE_NAME}}Request("Test {{pascalCase FEATURE_NAME}}");
var handler = new Create{{pascalCase FEATURE_NAME}}Handler(_db);
// Act
var result = await handler.HandleAsync(command, CancellationToken.None);
// Assert
result.IsSuccess.Should().BeTrue();
result.Value.Should().NotBeNull();
var persisted = await _db.Set<{{pascalCase FEATURE_NAME}}>().SingleAsync();
persisted.Name.Should().Be("Test {{pascalCase FEATURE_NAME}}");
}
#endregion
#region GetById Handler Tests
[Fact]
public async Task GetById_HandleAsync_WithValidId_ReturnsSuccessResult()
{
// Arrange
var entity = CreateTestEntity("Test {{pascalCase FEATURE_NAME}}");
_db.Set<{{pascalCase FEATURE_NAME}}>().Add(entity);
await _db.SaveChangesAsync();
var query = new Get{{pascalCase FEATURE_NAME}}ByIdRequest(entity.Id);
var handler = new Get{{pascalCase FEATURE_NAME}}ByIdHandler(_db);
// Act
var result = await handler.HandleAsync(query, CancellationToken.None);
// Assert
result.IsSuccess.Should().BeTrue();
result.Value.Should().NotBeNull();
result.Value!.Name.Should().Be("Test {{pascalCase FEATURE_NAME}}");
}
[Fact]
public async Task GetById_HandleAsync_WithInvalidId_ReturnsFailureResult()
{
// Arrange
var query = new Get{{pascalCase FEATURE_NAME}}ByIdRequest(Guid.CreateVersion7());
var handler = new Get{{pascalCase FEATURE_NAME}}ByIdHandler(_db);
// Act
var result = await handler.HandleAsync(query, CancellationToken.None);
// Assert
result.IsSuccess.Should().BeFalse();
}
#endregion
#region Update Handler Tests
[Fact]
public async Task Update_HandleAsync_WithValidCommand_ReturnsSuccessResult()
{
// Arrange
var entity = CreateTestEntity("Original Name");
_db.Set<{{pascalCase FEATURE_NAME}}>().Add(entity);
await _db.SaveChangesAsync();
var command = new Update{{pascalCase FEATURE_NAME}}Request(entity.Id, "Updated Name");
var handler = new Update{{pascalCase FEATURE_NAME}}Handler(_db);
// Act
var result = await handler.HandleAsync(command, CancellationToken.None);
// Assert
result.IsSuccess.Should().BeTrue();
var persisted = await _db.Set<{{pascalCase FEATURE_NAME}}>().SingleAsync(x => x.Id == entity.Id);
persisted.Name.Should().Be("Updated Name");
}
[Fact]
public async Task Update_HandleAsync_WithNonExistentId_ReturnsFailureResult()
{
// Arrange
var command = new Update{{pascalCase FEATURE_NAME}}Request(Guid.CreateVersion7(), "Updated");
var handler = new Update{{pascalCase FEATURE_NAME}}Handler(_db);
// Act
var result = await handler.HandleAsync(command, CancellationToken.None);
// Assert
result.IsSuccess.Should().BeFalse();
}
#endregion
#region Delete Handler Tests
[Fact]
public async Task Delete_HandleAsync_WithValidId_ReturnsSuccessResult()
{
// Arrange
var entity = CreateTestEntity("To Delete");
_db.Set<{{pascalCase FEATURE_NAME}}>().Add(entity);
await _db.SaveChangesAsync();
var command = new Delete{{pascalCase FEATURE_NAME}}Request(entity.Id);
var handler = new Delete{{pascalCase FEATURE_NAME}}Handler(_db);
// Act
var result = await handler.HandleAsync(command, CancellationToken.None);
// Assert
result.IsSuccess.Should().BeTrue();
(await _db.Set<{{pascalCase FEATURE_NAME}}>().AnyAsync(x => x.Id == entity.Id)).Should().BeFalse();
}
[Fact]
public async Task Delete_HandleAsync_WithNonExistentId_ReturnsFailureResult()
{
// Arrange
var command = new Delete{{pascalCase FEATURE_NAME}}Request(Guid.CreateVersion7());
var handler = new Delete{{pascalCase FEATURE_NAME}}Handler(_db);
// Act
var result = await handler.HandleAsync(command, CancellationToken.None);
// Assert
result.IsSuccess.Should().BeFalse();
}
#endregion
#region Helper Methods
private static {{pascalCase FEATURE_NAME}} CreateTestEntity(string name)
{
return new {{pascalCase FEATURE_NAME}}
{
Id = Guid.CreateVersion7(),
Name = name,
// TODO: preencher demais campos obrigatórios da entidade
};
}
#endregion
}
// ============================================================
// Validator Tests
// ============================================================
///
/// Unit tests for {{pascalCase FEATURE_NAME}} validators.
/// Tests FluentValidation rules using ValidateAsync.
///
public class {{pascalCase FEATURE_NAME}}ValidatorTests
{
#region Create Validator Tests
[Fact]
public async Task CreateValidator_WithValidCommand_PassesValidation()
{
// Arrange
var validator = new Create{{pascalCase FEATURE_NAME}}Validator();
var command = new Create{{pascalCase FEATURE_NAME}}Request("Valid Name");
// Act
ValidationResult result = await validator.ValidateAsync(command);
// Assert
result.IsValid.Should().BeTrue();
result.Errors.Should().BeEmpty();
}
[Theory]
[InlineData("")]
[InlineData(" ")]
[InlineData(null)]
public async Task CreateValidator_WithInvalidName_FailsValidation(string? name)
{
// Arrange
var validator = new Create{{pascalCase FEATURE_NAME}}Validator();
var command = new Create{{pascalCase FEATURE_NAME}}Request(name!);
// Act
ValidationResult result = await validator.ValidateAsync(command);
// Assert
result.IsValid.Should().BeFalse();
result.Errors.Should().Contain(e => e.PropertyName == nameof(Create{{pascalCase FEATURE_NAME}}Request.Name));
}
[Fact]
public async Task CreateValidator_WithNameExceedingMaxLength_FailsValidation()
{
// Arrange
var validator = new Create{{pascalCase FEATURE_NAME}}Validator();
var command = new Create{{pascalCase FEATURE_NAME}}Request(new string('A', 201));
// Act
ValidationResult result = await validator.ValidateAsync(command);
// Assert
result.IsValid.Should().BeFalse();
result.Errors.Should().Contain(e => e.PropertyName == nameof(Create{{pascalCase FEATURE_NAME}}Request.Name));
}
#endregion
#region Update Validator Tests
[Fact]
public async Task UpdateValidator_WithValidCommand_PassesValidation()
{
// Arrange
var validator = new Update{{pascalCase FEATURE_NAME}}Validator();
var command = new Update{{pascalCase FEATURE_NAME}}Request(Guid.CreateVersion7(), "Updated Name");
// Act
ValidationResult result = await validator.ValidateAsync(command);
// Assert
result.IsValid.Should().BeTrue();
}
[Fact]
public async Task UpdateValidator_WithInvalidId_FailsValidation()
{
// Arrange
var validator = new Update{{pascalCase FEATURE_NAME}}Validator();
var command = new Update{{pascalCase FEATURE_NAME}}Request(Guid.Empty, "Updated Name");
// Act
ValidationResult result = await validator.ValidateAsync(command);
// Assert
result.IsValid.Should().BeFalse();
result.Errors.Should().Contain(e => e.PropertyName == nameof(Update{{pascalCase FEATURE_NAME}}Request.Id));
}
#endregion
}