using Microsoft.Data.SqlClient; using Respawn; using SIGCM2.Infrastructure.Persistence; namespace SIGCM2.Application.Tests.Integration; [Collection("Database")] public class UsuarioRepositoryTests : IAsyncLifetime { private const string ConnectionString = "Server=TECNICA3;Database=SIGCM2_Test;User Id=desarrollo;Password=desarrollo2026;TrustServerCertificate=True;"; private SqlConnection _connection = null!; private Respawner _respawner = null!; private UsuarioRepository _repository = null!; public async Task InitializeAsync() { _connection = new SqlConnection(ConnectionString); await _connection.OpenAsync(); _respawner = await Respawner.CreateAsync(_connection, new RespawnerOptions { DbAdapter = DbAdapter.SqlServer }); // Reset DB and seed admin user for each test class run await _respawner.ResetAsync(_connection); await SeedAdminAsync(); var factory = new SqlConnectionFactory(ConnectionString); _repository = new UsuarioRepository(factory); } public async Task DisposeAsync() { await _respawner.ResetAsync(_connection); await _connection.CloseAsync(); await _connection.DisposeAsync(); } // Scenario: GetByUsername returns correct entity when user exists [Fact] public async Task GetByUsernameAsync_ExistingUser_ReturnsUsuario() { var usuario = await _repository.GetByUsernameAsync("admin"); Assert.NotNull(usuario); Assert.Equal("admin", usuario.Username); Assert.Equal("admin", usuario.Rol); Assert.True(usuario.Activo); Assert.False(string.IsNullOrWhiteSpace(usuario.PasswordHash)); } // Triangulation: GetByUsername returns null when user does not exist [Fact] public async Task GetByUsernameAsync_NonExistentUser_ReturnsNull() { var usuario = await _repository.GetByUsernameAsync("noexiste"); Assert.Null(usuario); } // Triangulation: case-sensitive username lookup (SQL Server UNIQUE constraint is case-insensitive by default) [Fact] public async Task GetByUsernameAsync_DifferentUser_ReturnsCorrectUser() { // Insert a second user await _connection.ExecuteAsync( "INSERT INTO dbo.Usuario (Username, PasswordHash, Nombre, Apellido, Rol, PermisosJson) " + "VALUES ('vendedor1', '$2a$12$hash2', 'Juan', 'Pérez', 'vendedor', '[]')"); var admin = await _repository.GetByUsernameAsync("admin"); var vendedor = await _repository.GetByUsernameAsync("vendedor1"); Assert.NotNull(admin); Assert.NotNull(vendedor); Assert.NotEqual(admin.Id, vendedor.Id); Assert.Equal("admin", admin.Rol); Assert.Equal("vendedor", vendedor.Rol); } private async Task SeedAdminAsync() { await _connection.ExecuteAsync( "SET QUOTED_IDENTIFIER ON; " + "IF NOT EXISTS (SELECT 1 FROM dbo.Usuario WHERE Username = 'admin') " + "INSERT INTO dbo.Usuario (Username, PasswordHash, Nombre, Apellido, Rol, PermisosJson, Activo) " + "VALUES ('admin', '$2a$12$rmq6tlSAQ8WXhR2CwLCSeuwCJKz/.8Eab95UQCUNfwe4dokeOqMcW', " + "'Administrador', 'Sistema', 'admin', '[\"*\"]', 1)"); } } // Dapper extension helper for IDbConnection file static class DapperHelper { public static async Task ExecuteAsync(this SqlConnection conn, string sql) { using var cmd = conn.CreateCommand(); cmd.CommandText = sql; await cmd.ExecuteNonQueryAsync(); } }