Skip to content

Refactors and enhances tests#68

Merged
jldsilva merged 5 commits into
developmentfrom
Refactoring-Tests
Dec 31, 2025
Merged

Refactors and enhances tests#68
jldsilva merged 5 commits into
developmentfrom
Refactoring-Tests

Conversation

@jldsilva

@jldsilva jldsilva commented Dec 30, 2025

Copy link
Copy Markdown
Owner

Refactors the test suite by reorganizing and renaming test projects to improve clarity and maintainability.

Also, integrates Testcontainers for more robust integration tests, ensuring reliable database interactions and adds Docker layer caching in CI to speed up workflow execution.

Summary by CodeRabbit

  • New Features

    • Suporte a operações em lote em repositórios (remoção/atualização).
  • Tests

    • Novas suítes de integração com PostgreSQL (containerizado), vários testes de configuração de entidades e utilitários de dados; reorganização de namespaces/assets e atualização da suíte de testes.
  • Refactor

    • Melhorias no gerenciamento de transações, tratamento de cancelamento e logging em operações de banco.
  • Chores

    • Reestruturação da solução e projetos; ajustes na pipeline CI e nas dependências de testes.

✏️ Tip: You can customize this high-level summary in your review settings.

Refactors the test suite by reorganizing and renaming test projects to improve clarity and maintainability.

Also, integrates Testcontainers for more robust integration tests, ensuring reliable database interactions and adds Docker layer caching in CI to speed up workflow execution.
@jldsilva jldsilva self-assigned this Dec 30, 2025
@jldsilva jldsilva added .NET Pull requests that update .NET code enhancement New feature or request labels Dec 30, 2025
@coderabbitai

coderabbitai Bot commented Dec 30, 2025

Copy link
Copy Markdown

Walkthrough

Reestrutura a solução e testes, adiciona testes de integração com PostgreSQL via Testcontainers, implementa migration EF Core com seed, adiciona operações bulk ao repositório, refatora UnitOfWork para transações EF Core com tratamento de cancelamento e move/renomeia muitos namespaces e ativos de testes.

Changes

Cohort / File(s) Sumário
CI workflow
​.github/workflows/workflow-ci.yml
Ajuste do grep para descoberta de projetos de teste (procura por .ArchitectureTests, .IntegrationTests, .UnitTests) e ordenação da lista de projetos
Central de pacotes
Directory.Packages.props
Remove Microsoft.EntityFrameworkCore.Sqlite; adiciona Testcontainers e Testcontainers.PostgreSql sob pacotes de teste
Migração EF Core
InvoiceReminder.Data/Migrations/20250930210104_Initial_Create.cs
Nova migração explícita com criação de schema/tabelas, chaves FK, índices, seed de usuários e [ExcludeFromCodeCoverage]
UnitOfWork / Transações
InvoiceReminder.Data/Repository/UnitOfWork.cs
Refatoração para usar BeginTransactionAsync/IDbContextTransaction, abrir/fechar conexão, rollback assíncrono, logging padronizado e tratamento de OperationCanceledException
Interfaces & Repositório (bulk ops)
InvoiceReminder.Data/Interfaces/IBaseRepository.cs, InvoiceReminder.Data/Repository/BaseRepository.cs
IBaseRepository adiciona BulkRemoveAsync e BulkUpdateAsync; BaseRepository implementa ambos (ajusta UpdatedAt antes do bulk update)
IUserRepository
InvoiceReminder.Data/Interfaces/IUserRepository.cs
Remove re-declaração redundante de GetByIdAsync(Guid, CancellationToken)
Entity configs & InternalsVisibleTo
InvoiceReminder.Data/Persistence/EntitiesConfig/*.cs
Atualiza InternalsVisibleTo para InvoiceReminder.UnitTests.Infrastructure; adiciona HasColumnType("uuid") em JobScheduleConfig e ScanEmailDefinitionConfig
Remoção de testes locais
InvoiceReminder.Infrastructure.UnitTests/Data/Repository/*.cs
Remove suites de testes de infraestrutura (BaseRepositoryTests, ScanEmailDefinitionRepositoryTests, UnitOfWorkTests, UserRepositoryTests)
Novos testes unitários de configuração EF
InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/*.cs
Adiciona testes que validam mapeamentos de User, Invoice, JobSchedule, ScanEmailDefinition, EmailAuthToken via ModelBuilder
Integração com PostgreSQL
InvoiceReminder.IntegrationTests/**
InvoiceReminder.IntegrationTests/Data/ContainerSetup/DatabaseFixture.cs, .../Data/Repository/*.cs, .../Data/Utils/TestData.cs, InvoiceReminder.IntegrationTests.csproj
Novo projeto de integração: DatabaseFixture inicia container PostgreSQL, aplica migrations; vários testes de repositório/UnitOfWork executam contra DB real; utilitários Faker centralizados
Renomeação de namespaces & assets
InvoiceReminder.UnitTests.*/**/*.cs, InvoiceReminder.UnitTests.SUT.Assets/*
Migração massiva de namespaces para InvoiceReminder.UnitTests.*; assets movidos/renomeados para UnitTests.SUT.Assets; using atualizados em muitos arquivos
Ajustes de asserts de logging e paralelização
InvoiceReminder.UnitTests.*, InvoiceReminder.UnitTests.JobScheduler/*.cs
Consolidação/inline dasserções de _logger.Log(...) e adição de [assembly: Parallelize(...)] em configurações de teste
Solução & global.json
InvoiceReminder.sln, global.json
Reestruturação extensa da solução (novos GUIDs, projetos e mappings); global.json modificado de forma que pode quebrar a sintaxe JSON (vírgula removida entre propriedades)

Sequence Diagram(s)

sequenceDiagram
  participant Caller as Chamador
  participant UoW as UnitOfWork
  participant DbCtx as DbContext
  participant Tx as IDbContextTransaction
  participant Logger as ILogger

  Note over Caller,UoW: SaveChangesAsync (fluxo refatorado)
  Caller->>UoW: SaveChangesAsync(cancellationToken)
  UoW->>DbCtx: Database.OpenConnectionAsync()
  UoW->>DbCtx: Database.BeginTransactionAsync()
  activate Tx
  alt sucesso
    UoW->>DbCtx: _dbContext.SaveChangesAsync(cancellationToken)
    UoW->>Tx: CommitAsync()
  else cancelado
    UoW->>Logger: LogWarning("Operation canceled: ...")
    UoW->>Tx: RollbackAsync(CancellationToken.None)
    UoW-->>Caller: throw OperationCanceledException
  else erro
    UoW->>Logger: LogError("Save failed: ...")
    UoW->>Tx: RollbackAsync(CancellationToken.None)
    UoW-->>Caller: rethrow
  end
  UoW->>DbCtx: Database.CloseConnection()
  deactivate Tx
  UoW-->>Caller: retorna/propaga resultado
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested labels

dependencies

Poem

🐰 Pulei pelos commits com um pulo só,
Troquei namespaces e trouxe Postgre no rol,
Migrations, bulk ops e testes a rodar,
Transações cuidadas — pronto pra validar,
Cenouras celebram esse grande refator!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.19% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed O título descreve parcialmente as mudanças principais, focando em testes, mas omite aspectos significativos como reorganização de projetos e integração de Testcontainers.
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
InvoiceReminder.UnitTests.Infrastructure/Authentication/StringHashExtensionTests.cs (1)

42-50: Remova o método ToMD5() não utilizado do código de produção.

O método ToMD5() está definido em StringHashExtension.cs, mas não é utilizado em nenhum lugar do código de produção. Visto que MD5 é criptograficamente quebrado desde 1996 e não deve ser usado para fins de segurança, recomenda-se remover completamente esse método para reduzir a superfície de ataque em uma área sensível (namespace Authentication). Alternativas seguras como ToSHA256() e ToSHA512() já estão disponíveis na mesma classe e devem ser utilizadas.

InvoiceReminder.UnitTests.Infrastructure/Data/Repository/InvoiceRepositoryTests.cs (1)

20-22: String de conexão nula causará falhas.

O uso de UseNpgsql(default) passa null como string de conexão, o que causará exceções em tempo de execução quando o DbContext tentar se conectar ou executar operações. Para testes unitários que simulam o DbContext, considere:

  • Usar um banco de dados em memória (SQLite in-memory ou EF Core In-Memory provider)
  • Mockar completamente o DbContext e DbSets sem configurar providers de banco de dados reais
  • Mover esses testes para o projeto de testes de integração com Testcontainers PostgreSQL
🔎 Sugestão: Usar SQLite in-memory para testes unitários
-        var options = new DbContextOptionsBuilder<CoreDbContext>()
-            .UseNpgsql(default)
-            .Options;
+        var options = new DbContextOptionsBuilder<CoreDbContext>()
+            .UseSqlite("DataSource=:memory:")
+            .Options;
-
-        _dbContext = Substitute.ForPartsOf<CoreDbContext>(options);
+        
+        _dbContext = new CoreDbContext(options);
+        _dbContext.Database.OpenConnection();
+        _dbContext.Database.EnsureCreated();
InvoiceReminder.UnitTests.API/AuthenticationSetup/BearerSecuritySchemeTransformerTests.cs (1)

45-55: Verificação incompleta no assertion.

Na linha 51, ContainsKey("Bearer") é chamado mas seu resultado não é verificado. A expressão retorna bool mas é descartada.

🔎 Correção sugerida
             _ = _document.Components.SecuritySchemes.ShouldNotBeNull();
-            _ = _document.Components.SecuritySchemes.ContainsKey("Bearer");
+            _document.Components.SecuritySchemes.ContainsKey("Bearer").ShouldBeTrue();

             _document.Components.SecuritySchemes["Bearer"].Scheme.ShouldBeEquivalentTo("bearer");
♻️ Duplicate comments (1)
InvoiceReminder.IntegrationTests/Data/Repository/BaseRepositoryIntegrationTests.cs (1)

47-55: Duplicação de UserFaker detectada.

O método UserFaker() está duplicado em múltiplos arquivos de teste. Conforme sugerido na revisão de UserRepositoryIntegrationTests.cs, considere extrair para uma classe auxiliar compartilhada.

🧹 Nitpick comments (22)
InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/InvoiceConfigTests.cs (1)

9-17: Considere aprimorar a cobertura do teste.

O teste atual apenas verifica que o construtor de InvoiceConfig não lança exceção, mas não valida se a configuração do Entity Framework está correta. Considere adicionar testes que usem um ModelBuilder mock para verificar que as configurações de entidade (chaves primárias, relacionamentos, constraints, etc.) estão sendo aplicadas corretamente.

💡 Exemplo de teste mais abrangente
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Conventions;
+
 [TestMethod]
 public void InvoiceConfig_ShouldNotThrowErrorWhenInstantiated()
 {
     // Arrange && Act
     Action action = () => _ = new InvoiceConfig();
 
     // Assert
     action.ShouldNotThrow();
 }
+
+[TestMethod]
+public void InvoiceConfig_ShouldConfigureEntityCorrectly()
+{
+    // Arrange
+    var builder = new ModelBuilder(new ConventionSet());
+    var config = new InvoiceConfig();
+
+    // Act
+    config.Configure(builder.Entity<Invoice>());
+
+    // Assert
+    var entityType = builder.Model.FindEntityType(typeof(Invoice));
+    entityType.ShouldNotBeNull();
+    // Adicione asserções específicas para verificar chaves, relacionamentos, etc.
+}
InvoiceReminder.UnitTests.Infrastructure/Data/Repository/EmailAuthTokenRepositoryTests.cs (2)

20-26: Configuração do DbContext com string de conexão nula.

A linha 21 usa UseNpgsql(default), que passa null como string de conexão. Embora isso funcione para este teste específico de atribuibilidade de tipos, é uma abordagem incomum que pode causar confusão.

Adicionalmente, o uso de Substitute.ForPartsOf<CoreDbContext> para criar um substituto parcial de um DbContext é atípico e pode não ser necessário para um teste que apenas verifica relações de tipos.


28-44: Considere se este teste adiciona valor suficiente.

Este teste apenas verifica atribuibilidade de tipos, que é algo que o compilador já garante em tempo de compilação. Como os objetivos da PR mencionam "removendo testes unitários da camada de infraestrutura em favor de testes de integração", e já existem testes de integração que cobrem a funcionalidade real do EmailAuthTokenRepository, considere se este teste unitário é realmente necessário.

Testes de atribuibilidade de tipos geralmente agregam pouco valor, pois não verificam comportamento real e não podem falhar sem que o código também falhe na compilação.

💡 Alternativa: remover este teste e confiar nos testes de integração existentes

Como já existem testes de integração que cobrem cenários reais (GetByIdAsync, GetByUserIdAsync, etc.), você pode considerar remover este arquivo de teste unitário completamente, alinhando-se melhor com os objetivos da PR de mover testes de infraestrutura para testes de integração.

InvoiceReminder.UnitTests.JobScheduler/JobSettings/CronJobTests.cs (2)

62-68: Use Received em vez de ReceivedWithAnyArgs ao especificar matchers de argumentos.

O uso de ReceivedWithAnyArgs(1) com matchers explícitos de argumentos (Arg.Any<EventId>(), Arg.Any<object>(), etc.) é redundante. O ReceivedWithAnyArgs foi projetado para ignorar todos os argumentos, então especificar matchers não tem efeito e cria confusão sobre a intenção do teste.

🔎 Refatoração sugerida
-        _logger.ReceivedWithAnyArgs(1).Log(
+        _logger.Received(1).Log(
             LogLevel.Information,
             Arg.Any<EventId>(),
             Arg.Any<object>(),
             null,
             Arg.Any<Func<object, Exception, string>>()
         );

Ou, se não precisa verificar os argumentos:

-        _logger.ReceivedWithAnyArgs(1).Log(
-            LogLevel.Information,
-            Arg.Any<EventId>(),
-            Arg.Any<object>(),
-            null,
-            Arg.Any<Func<object, Exception, string>>()
-        );
+        _logger.ReceivedWithAnyArgs(1).Log<object>();

110-116: Uso correto de Received com matchers específicos, mas note a inconsistência.

A mudança para Received(1).Log(...) com matchers específicos está correta, especialmente a verificação de que o estado contém "Test Job Description triggered..." na linha 113. No entanto, isso cria uma inconsistência com o teste em Execute_ShouldCreateScopeResolveServiceAndSendMessage (linhas 62-68) que ainda usa ReceivedWithAnyArgs. Considere padronizar a abordagem de asserção do logger em todos os testes.

InvoiceReminder.UnitTests.JobScheduler/HostedService/QuartzHostedServiceTests.cs (1)

163-169: Considere verificar o conteúdo da mensagem para maior consistência.

As asserções de logging nestas linhas usam Arg.Any<object>() para o parâmetro de estado, enquanto a asserção nas linhas 91-97 verifica o conteúdo da mensagem com Arg.Is<object>(o => o.ToString().Contains("CronJob inválido:")).

Verificar o conteúdo da mensagem tornaria os testes mais específicos e ajudaria a detectar regressões nas mensagens de erro.

🔎 Refatoração sugerida para maior especificidade

Para as linhas 163-169:

 _logger.Received(2).Log(
     LogLevel.Error,
     Arg.Any<EventId>(),
-    Arg.Any<object>(),
+    Arg.Is<object>(o => o.ToString().Contains("CronJob inválido:")),
     Arg.Any<Exception>(),
     Arg.Any<Func<object, Exception, string>>()
 );

Para as linhas 195-201:

 _logger.Received(1).Log(
     LogLevel.Error,
     Arg.Any<EventId>(),
-    Arg.Any<object>(),
+    Arg.Is<object>(o => o.ToString().Contains("CronJob inválido:")),
     Arg.Any<Exception>(),
     Arg.Any<Func<object, Exception, string>>()
 );

Also applies to: 195-201

InvoiceReminder.UnitTests.Infrastructure/Data/Repository/JobScheduleRepositoryTests.cs (2)

20-22: Considere usar uma connection string fictícia ao invés de default.

Passar default (null) como connection string para UseNpgsql não causa problemas aqui porque o DbContext é substituído na linha 24, mas pode ser confuso para futuros mantenedores.

🔎 Sugestão de melhoria
 var options = new DbContextOptionsBuilder<CoreDbContext>()
-    .UseNpgsql(default)
+    .UseNpgsql("Host=localhost;Database=test;Username=test;Password=test")
     .Options;

12-44: Considere consolidar ou remover este arquivo de teste.

Após a remoção dos testes comportamentais (que foram movidos para testes de integração), esta classe contém apenas um teste de atribuibilidade de tipos. A infraestrutura de setup (constructor com mocks) parece super-dimensionada para um único teste que apenas instancia o repositório.

Considere:

  1. Remover este arquivo completamente se o teste de tipo não agrega valor significativo
  2. Consolidar o teste de tipo em um arquivo de testes de tipo centralizado
  3. Simplificar o constructor se optar por manter o arquivo
InvoiceReminder.UnitTests.Infrastructure/Data/Repository/ScanEmailDefinitionRepositoryTests.cs (1)

28-44: Teste valida apenas estrutura, não comportamento.

Este teste verifica apenas a compatibilidade de tipos e implementação de interfaces, mas não testa nenhum comportamento real do repositório (operações CRUD, queries, etc.).

Considerações:

  • Verificações de tipo são garantias em tempo de compilação - se o código compila, essas asserções sempre passarão
  • O valor deste teste é mínimo para garantir qualidade
  • Dado que o PR está migrando para testes de integração com Testcontainers, um teste de integração seria mais valioso

Sugestão: Substitua este teste por testes de integração que validem o comportamento real do repositório, como:

  • Adicionar uma ScanEmailDefinition e verificar se foi persistida
  • Buscar por ID e validar os dados retornados
  • Atualizar e deletar registros

Isso se alinha melhor com os objetivos do PR de adicionar "testes de integração mais robustos" com Testcontainers.

InvoiceReminder.UnitTests.Infrastructure/Data/Repository/InvoiceRepositoryTests.cs (1)

28-44: Testes comportamentais foram movidos para o projeto de integração — adição de comentário esclarecedor recomendada.

Os testes comportamentais (AddAsync, BulkInsertAsync, Remove, GetById, GetAll, Update, Where, Dispose) estão confirmados em BaseRepositoryIntegrationTests.cs e InvoiceRepositoryIntegrationTests.cs com Testcontainers, seguindo a estrutura adequada de separação entre testes unitários e de integração.

No entanto, o teste unitário atual é puramente de atribuibilidade de tipos, o que oferece cobertura limitada. Considere adicionar um comentário no arquivo explicando que os testes de comportamento estão no projeto de integração, ou adicionar alguns testes unitários de InvoiceRepository com mocks completos para cenários específicos que não requerem banco de dados (ex.: validações de entrada, tratamento de exceções).

InvoiceReminder.UnitTests.Infrastructure/Data/Repository/UserRepositoryTests.cs (1)

28-44: Teste de conformidade de tipo aprovado, com sugestão de melhoria opcional.

O teste valida corretamente que UserRepository implementa as interfaces esperadas e herda de BaseRepository<CoreDbContext, User>. O uso de UseNpgsql(default) é aceitável neste contexto, pois o teste não acessa o banco de dados.

No entanto, este teste unitário cobre apenas a conformidade estrutural. Embora os testes de integração no arquivo UserRepositoryIntegrationTests.cs cubram o comportamento real, considere adicionar testes unitários para validar lógica específica do repositório (se houver) usando mocks completos do DbContext.

InvoiceReminder.Data/Migrations/20250930210104_Initial_Create.cs (2)

8-10: Posição do comentário XML não-convencional.

O comentário de documentação XML /// <inheritdoc /> está posicionado após o atributo [ExcludeFromCodeCoverage]. Por convenção, comentários de documentação devem preceder todos os atributos.

🔎 Correção sugerida
 [ExcludeFromCodeCoverage]
-/// <inheritdoc />
+/// <inheritdoc />
 public partial class Initial_Create : Migration

Ou, de preferência:

+/// <inheritdoc />
 [ExcludeFromCodeCoverage]
-/// <inheritdoc />
 public partial class Initial_Create : Migration

15-18: Criação de schema redundante.

A criação do schema é feita duas vezes: primeiro com SQL direto (CREATE SCHEMA IF NOT EXISTS) e depois com EnsureSchema. Embora seja seguro (ambos são idempotentes), é desnecessário.

🔎 Sugestão de simplificação
-        _ = migrationBuilder.Sql("CREATE SCHEMA IF NOT EXISTS invoice_reminder;");
-
         _ = migrationBuilder.EnsureSchema(
             name: "invoice_reminder");
InvoiceReminder.Data/Repository/UnitOfWork.cs (1)

63-63: Usar cancellationToken cancelado no RollbackAsync pode falhar.

Se a exceção original foi causada por uma requisição de cancelamento (mas não capturada pelo when clause), passar o mesmo cancellationToken para RollbackAsync pode impedir o rollback. Considere usar CancellationToken.None para garantir que o rollback seja executado.

🔎 Correção proposta
-            await transaction.RollbackAsync(cancellationToken);
+            await transaction.RollbackAsync(CancellationToken.None);
InvoiceReminder.IntegrationTests/Data/ContainerSetup/DatabaseFixture.cs (1)

15-28: Considere adicionar tratamento de erro para falha na inicialização do container.

Se StartAsync ou RunMigrations falhar, o erro pode não ser claro para depuração. Considere adicionar logs ou mensagens de erro mais descritivas.

🔎 Sugestão
     [AssemblyInitialize]
     public static async Task AssemblyInit(TestContext context)
     {
-        _dbContainer = new PostgreSqlBuilder()
-            .WithDatabase("postgres")
-            .WithUsername("postgres")
-            .WithPassword("Fake!Password#123")
-            .WithImage("postgres:15-alpine")
-            .Build();
-
-        await _dbContainer.StartAsync(context.CancellationToken);
-        await CreateSchema(context);
-        await RunMigrations(context);
+        try
+        {
+            _dbContainer = new PostgreSqlBuilder()
+                .WithDatabase("postgres")
+                .WithUsername("postgres")
+                .WithPassword("Fake!Password#123")
+                .WithImage("postgres:15-alpine")
+                .Build();
+
+            await _dbContainer.StartAsync(context.CancellationToken);
+            await CreateSchema(context);
+            await RunMigrations(context);
+        }
+        catch (Exception ex)
+        {
+            context.WriteLine($"Failed to initialize test database: {ex.Message}");
+            throw;
+        }
     }
InvoiceReminder.IntegrationTests/Data/Repository/ScanEmailDefinitionRepositoryIntegrationTests.cs (1)

16-37: Considere implementar IDisposable ou usar [TestCleanup] para liberar recursos.

A classe cria CoreDbContext e UnitOfWork no construtor, mas não implementa IDisposable nem usa [TestCleanup]. Isso pode causar vazamento de conexões de banco de dados entre testes. Compare com UnitOfWorkIntegrationTests que implementa TestInitialize e TestCleanup.

🔎 Sugestão
 [TestClass]
 public sealed class ScanEmailDefinitionRepositoryIntegrationTests
 {
-    private readonly CoreDbContext _dbContext;
-    private readonly ILogger<ScanEmailDefinitionRepository> _repositoryLogger;
-    private readonly ILogger<UnitOfWork> _unitOfWorkLogger;
-    private readonly ScanEmailDefinitionRepository _repository;
-    private readonly UnitOfWork _unitOfWork;
+    private CoreDbContext _dbContext;
+    private ILogger<ScanEmailDefinitionRepository> _repositoryLogger;
+    private ILogger<UnitOfWork> _unitOfWorkLogger;
+    private ScanEmailDefinitionRepository _repository;
+    private UnitOfWork _unitOfWork;

     public TestContext TestContext { get; set; }

-    public ScanEmailDefinitionRepositoryIntegrationTests()
+    [TestInitialize]
+    public void TestInitialize()
     {
         var options = new DbContextOptionsBuilder<CoreDbContext>()
             .UseNpgsql(DatabaseFixture.ConnectionString)
             .Options;

         _dbContext = new CoreDbContext(options);
-        _repositoryLogger = Substitute.For<ILogger<ScanEmailDefinitionRepository>>(); ;
+        _repositoryLogger = Substitute.For<ILogger<ScanEmailDefinitionRepository>>();
         _unitOfWorkLogger = Substitute.For<ILogger<UnitOfWork>>();
         _repository = new ScanEmailDefinitionRepository(_dbContext, _repositoryLogger);
         _unitOfWork = new UnitOfWork(_dbContext, _unitOfWorkLogger);
     }
+
+    [TestCleanup]
+    public void TestCleanup()
+    {
+        _unitOfWork?.Dispose();
+        _dbContext?.Dispose();
+    }
InvoiceReminder.IntegrationTests/Data/Repository/EmailAuthTokenRepositoryIntegrationTests.cs (1)

15-36: Falta implementação de cleanup de recursos.

Assim como em ScanEmailDefinitionRepositoryIntegrationTests, esta classe cria recursos no construtor mas não os libera. Considere usar [TestInitialize] e [TestCleanup] para consistência com UnitOfWorkIntegrationTests.

InvoiceReminder.IntegrationTests/Data/Repository/JobScheduleRepositoryIntegrationTests.cs (1)

15-36: Falta implementação de cleanup de recursos.

Mesmo padrão dos outros arquivos de teste. Considere usar [TestInitialize] e [TestCleanup] para liberar DbContext e UnitOfWork após cada teste.

InvoiceReminder.IntegrationTests/Data/Repository/InvoiceRepositoryIntegrationTests.cs (1)

15-36: Falta implementação de cleanup de recursos.

Mesmo padrão dos outros arquivos de teste de integração. Considere padronizar usando [TestInitialize] e [TestCleanup].

InvoiceReminder.IntegrationTests/Data/Repository/UserRepositoryIntegrationTests.cs (2)

40-82: Considere extrair Fakers duplicados para uma classe compartilhada.

Os métodos UserFaker(), EmailAuthTokenFaker() e InvoiceFaker() estão duplicados em múltiplos arquivos de teste (EmailAuthTokenRepositoryIntegrationTests, InvoiceRepositoryIntegrationTests, BaseRepositoryIntegrationTests). Extrair essas definições para uma classe auxiliar compartilhada melhoraria a manutenibilidade.

💡 Sugestão: criar classe TestDataFakers

Crie um arquivo TestDataFakers.cs:

namespace InvoiceReminder.IntegrationTests.Data.TestHelpers;

public static class TestDataFakers
{
    public static Faker<User> UserFaker() => new Faker<User>()
        .RuleFor(u => u.Id, _ => Guid.NewGuid())
        .RuleFor(u => u.TelegramChatId, f => f.Random.Long(100000000, long.MaxValue))
        .RuleFor(u => u.Name, f => f.Person.FullName)
        .RuleFor(u => u.Email, f => f.Internet.Email())
        .RuleFor(u => u.Password, f => f.Internet.Password(length: 16, memorable: false));

    public static Faker<EmailAuthToken> EmailAuthTokenFaker() => new Faker<EmailAuthToken>()
        .RuleFor(e => e.Id, faker => faker.Random.Guid())
        // ... demais regras

    public static Faker<Invoice> InvoiceFaker() => new Faker<Invoice>()
        .RuleFor(i => i.Id, faker => faker.Random.Guid())
        // ... demais regras
}

Em seguida, use TestDataFakers.UserFaker() nos arquivos de teste.


295-341: Testes de cancelamento bem implementados, com pequena ineficiência.

Os testes verificam corretamente:

  • Lançamento de OperationCanceledException
  • Logging de warning quando operação é cancelada

Observação menor: As linhas 299 e 323 criam e salvam usuários que não são utilizados nos testes de cancelamento. Isso adiciona overhead desnecessário.

💡 Otimização opcional
     [TestMethod]
     public async Task GetByIdAsync_Should_Handle_Cancellation_Request()
     {
         // Arrange
-        var user = await CreateAndSaveUserAsync();
         using var cts = new CancellationTokenSource();
         await cts.CancelAsync();
 
         _ = _repositoryLogger.IsEnabled(Arg.Any<LogLevel>()).Returns(true);
 
         // Act & Assert
         _ = await Should.ThrowAsync<OperationCanceledException>(
-            async () => await _repository.GetByIdAsync(user.Id, cts.Token)
+            async () => await _repository.GetByIdAsync(Guid.NewGuid(), cts.Token)
         );

Mesma otimização pode ser aplicada ao teste GetByEmailAsync_Should_Handle_Cancellation_Request.

InvoiceReminder.IntegrationTests/Data/Repository/BaseRepositoryIntegrationTests.cs (1)

246-320: Testes de GetAll bem implementados, com oportunidade de melhoria.

Os testes verificam adequadamente:

  • Recuperação de todas as entidades
  • Comportamento NoTracking (estado Detached na linha 301)
  • Coleção vazia quando não há entidades

Observação: O padrão de descartar e recriar o contexto (linhas 254-263, 285-293, 308-312) é repetido. Considere extrair para um método auxiliar.

💡 Sugestão de helper method
private BaseRepository<CoreDbContext, T> CreateFreshRepository<T>() where T : class
{
    var options = new DbContextOptionsBuilder<CoreDbContext>()
        .UseNpgsql(DatabaseFixture.ConnectionString)
        .Options;
    
    var dbContext = new CoreDbContext(options);
    return new BaseRepository<CoreDbContext, T>(dbContext);
}

Uso:

// Dispose current context
_userRepository.Dispose();
await _dbContext.DisposeAsync();

// Use fresh repository
using var repository = CreateFreshRepository<User>();
var result = repository.GetAll().ToList();
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3fcf6d5 and 7774a97.

📒 Files selected for processing (96)
  • .github/workflows/workflow-ci.yml
  • Directory.Packages.props
  • InvoiceReminder.API/AuthenticationSetup/BearerSecuritySchemeTransformer.cs
  • InvoiceReminder.ArchitectureTests/InvoiceReminder.ArchitectureTests.csproj
  • InvoiceReminder.Data/Interfaces/IUserRepository.cs
  • InvoiceReminder.Data/Migrations/20250930210104_Initial_Create.cs
  • InvoiceReminder.Data/Persistence/EntitiesConfig/EmailAuthTokenConfig.cs
  • InvoiceReminder.Data/Persistence/EntitiesConfig/InvoiceConfig.cs
  • InvoiceReminder.Data/Persistence/EntitiesConfig/JobScheduleConfig.cs
  • InvoiceReminder.Data/Persistence/EntitiesConfig/ScanEmailDefinitionConfig.cs
  • InvoiceReminder.Data/Persistence/EntitiesConfig/UserConfig.cs
  • InvoiceReminder.Data/Repository/UnitOfWork.cs
  • InvoiceReminder.Infrastructure.UnitTests/Data/Repository/BaseRepositoryTests.cs
  • InvoiceReminder.Infrastructure.UnitTests/Data/Repository/ScanEmailDefinitionRepositoryTests.cs
  • InvoiceReminder.Infrastructure.UnitTests/Data/Repository/UnitOfWorkTests.cs
  • InvoiceReminder.Infrastructure.UnitTests/Data/Repository/UserRepositoryTests.cs
  • InvoiceReminder.IntegrationTests/Data/ContainerSetup/DatabaseFixture.cs
  • InvoiceReminder.IntegrationTests/Data/Repository/BaseRepositoryIntegrationTests.cs
  • InvoiceReminder.IntegrationTests/Data/Repository/EmailAuthTokenRepositoryIntegrationTests.cs
  • InvoiceReminder.IntegrationTests/Data/Repository/InvoiceRepositoryIntegrationTests.cs
  • InvoiceReminder.IntegrationTests/Data/Repository/JobScheduleRepositoryIntegrationTests.cs
  • InvoiceReminder.IntegrationTests/Data/Repository/ScanEmailDefinitionRepositoryIntegrationTests.cs
  • InvoiceReminder.IntegrationTests/Data/Repository/UnitOfWorkIntegrationTests.cs
  • InvoiceReminder.IntegrationTests/Data/Repository/UserRepositoryIntegrationTests.cs
  • InvoiceReminder.IntegrationTests/InvoiceReminder.IntegrationTests.csproj
  • InvoiceReminder.IntegrationTests/MSTestSettings.cs
  • InvoiceReminder.UnitTests.API/AuthenticationSetup/BearerSecuritySchemeTransformerTests.cs
  • InvoiceReminder.UnitTests.API/AuthenticationSetup/JwtBearerOptionsSetupTests.cs
  • InvoiceReminder.UnitTests.API/AuthenticationSetup/JwtOptionsSetupTests.cs
  • InvoiceReminder.UnitTests.API/AuthenticationSetup/LoginRequestTests.cs
  • InvoiceReminder.UnitTests.API/AuthenticationSetup/MockAuthenticationHandler.cs
  • InvoiceReminder.UnitTests.API/Endpoints/GoogleOAuthEndpointsTests.cs
  • InvoiceReminder.UnitTests.API/Endpoints/InvoiceEndpointsTests.cs
  • InvoiceReminder.UnitTests.API/Endpoints/JobScheduleEndPointsTests.cs
  • InvoiceReminder.UnitTests.API/Endpoints/LoginEndpointTests.cs
  • InvoiceReminder.UnitTests.API/Endpoints/ScanEmailDefinitionEndpointsTests.cs
  • InvoiceReminder.UnitTests.API/Endpoints/SendMessageEndpointsTests.cs
  • InvoiceReminder.UnitTests.API/Endpoints/UserEndpointsTests.cs
  • InvoiceReminder.UnitTests.API/Factories/CustomWebApplicationFactory.cs
  • InvoiceReminder.UnitTests.API/InvoiceReminder.UnitTests.API.csproj
  • InvoiceReminder.UnitTests.API/MSTestSettings.cs
  • InvoiceReminder.UnitTests.Application/AppServices/BaseAppServiceTests.cs
  • InvoiceReminder.UnitTests.Application/AppServices/EmailAuthTokenAppServiceTests.cs
  • InvoiceReminder.UnitTests.Application/AppServices/InvoiceAppServiceTests.cs
  • InvoiceReminder.UnitTests.Application/AppServices/JobScheduleAppServiceTests.cs
  • InvoiceReminder.UnitTests.Application/AppServices/ScanEmailDefinitionAppServiceTests.cs
  • InvoiceReminder.UnitTests.Application/AppServices/UserAppServiceTests.cs
  • InvoiceReminder.UnitTests.Application/InvoiceReminder.UnitTests.Application.csproj
  • InvoiceReminder.UnitTests.Application/MSTestSettings.cs
  • InvoiceReminder.UnitTests.Application/ViewModels/EmailAuthTokenViewModelTests.cs
  • InvoiceReminder.UnitTests.Application/ViewModels/InvoiceViewModelTests.cs
  • InvoiceReminder.UnitTests.Application/ViewModels/JobScheduleViewmodelTests.cs
  • InvoiceReminder.UnitTests.Application/ViewModels/ScanEmailDefinitionViewModelTests.cs
  • InvoiceReminder.UnitTests.Application/ViewModels/UserViewModelTests.cs
  • InvoiceReminder.UnitTests.Domain/Entities/EmailAuthTokenTests.cs
  • InvoiceReminder.UnitTests.Domain/Entities/InvoiceTests.cs
  • InvoiceReminder.UnitTests.Domain/Entities/JobScheduleTests.cs
  • InvoiceReminder.UnitTests.Domain/Entities/ScanEmailDefinitionTests.cs
  • InvoiceReminder.UnitTests.Domain/Entities/UserTests.cs
  • InvoiceReminder.UnitTests.Domain/Extensions/EntityExtensionsTests.cs
  • InvoiceReminder.UnitTests.Domain/Extensions/UserExtensionsTests.cs
  • InvoiceReminder.UnitTests.Domain/Extensions/UserParameterTests.cs
  • InvoiceReminder.UnitTests.Domain/InvoiceReminder.UnitTests.Domain.csproj
  • InvoiceReminder.UnitTests.Domain/MSTestSettings.cs
  • InvoiceReminder.UnitTests.Domain/Services/Configuration/ConfigurationServiceTests.cs
  • InvoiceReminder.UnitTests.Domain/Services/Configuration/TokenCryptoServiceTests.cs
  • InvoiceReminder.UnitTests.ExternalServices/BarcodeReader/BarcodeReaderServiceTests.cs
  • InvoiceReminder.UnitTests.ExternalServices/InvoiceReminder.UnitTests.ExternalServices.csproj
  • InvoiceReminder.UnitTests.ExternalServices/MSTestSettings.cs
  • InvoiceReminder.UnitTests.ExternalServices/SendMessage/SendMessageServiceTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Authentication/JwtObjectTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Authentication/JwtOptionsTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Authentication/JwtProviderTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Authentication/StringHashExtensionTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/InvoiceConfigTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/JobScheduleConfigTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/ScanEmailDefinitionConfigTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/UserConfigTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/Repository/EmailAuthTokenRepositoryTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/Repository/InvoiceRepositoryTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/Repository/JobScheduleRepositoryTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/Repository/ScanEmailDefinitionRepositoryTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/Repository/UserRepositoryTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/InvoiceReminder.UnitTests.Infrastructure.csproj
  • InvoiceReminder.UnitTests.Infrastructure/MSTestSettings.cs
  • InvoiceReminder.UnitTests.JobScheduler/HostedService/QuartzHostedServiceTests.cs
  • InvoiceReminder.UnitTests.JobScheduler/InvoiceReminder.UnitTests.JobScheduler.csproj
  • InvoiceReminder.UnitTests.JobScheduler/JobSettings/CronJobTests.cs
  • InvoiceReminder.UnitTests.JobScheduler/MSTestSettings.cs
  • InvoiceReminder.UnitTests.SUT.Assets/Exception/PropertyTestException.cs
  • InvoiceReminder.UnitTests.SUT.Assets/Helpers/Guard.cs
  • InvoiceReminder.UnitTests.SUT.Assets/Helpers/TypeFactory.cs
  • InvoiceReminder.UnitTests.SUT.Assets/InvoiceReminder.UnitTests.SUT.Assets.csproj
  • InvoiceReminder.UnitTests.SUT.Assets/PropertyTester.cs
  • InvoiceReminder.sln
  • global.json
💤 Files with no reviewable changes (6)
  • InvoiceReminder.Infrastructure.UnitTests/Data/Repository/UnitOfWorkTests.cs
  • InvoiceReminder.Data/Interfaces/IUserRepository.cs
  • InvoiceReminder.Infrastructure.UnitTests/Data/Repository/ScanEmailDefinitionRepositoryTests.cs
  • InvoiceReminder.ArchitectureTests/InvoiceReminder.ArchitectureTests.csproj
  • InvoiceReminder.Infrastructure.UnitTests/Data/Repository/BaseRepositoryTests.cs
  • InvoiceReminder.Infrastructure.UnitTests/Data/Repository/UserRepositoryTests.cs
🧰 Additional context used
🧬 Code graph analysis (8)
InvoiceReminder.UnitTests.Infrastructure/Data/Repository/EmailAuthTokenRepositoryTests.cs (1)
InvoiceReminder.Domain/Entities/EmailAuthToken.cs (1)
  • EmailAuthToken (3-12)
InvoiceReminder.IntegrationTests/Data/Repository/ScanEmailDefinitionRepositoryIntegrationTests.cs (2)
InvoiceReminder.Data/Repository/UnitOfWork.cs (6)
  • UnitOfWork (12-113)
  • UnitOfWork (20-25)
  • UnitOfWork (109-112)
  • Task (27-72)
  • Task (74-80)
  • Task (82-88)
InvoiceReminder.Domain/Entities/ScanEmailDefinition.cs (1)
  • ScanEmailDefinition (5-13)
InvoiceReminder.Data/Repository/UnitOfWork.cs (1)
InvoiceReminder.Data/Repository/BaseRepository.cs (2)
  • Dispose (79-87)
  • Dispose (89-93)
InvoiceReminder.UnitTests.Infrastructure/Data/Repository/ScanEmailDefinitionRepositoryTests.cs (1)
InvoiceReminder.Domain/Entities/ScanEmailDefinition.cs (1)
  • ScanEmailDefinition (5-13)
InvoiceReminder.IntegrationTests/Data/Repository/UserRepositoryIntegrationTests.cs (3)
InvoiceReminder.IntegrationTests/Data/Repository/BaseRepositoryIntegrationTests.cs (1)
  • Faker (47-55)
InvoiceReminder.IntegrationTests/Data/Repository/EmailAuthTokenRepositoryIntegrationTests.cs (2)
  • Faker (40-48)
  • Faker (50-62)
InvoiceReminder.IntegrationTests/Data/Repository/InvoiceRepositoryIntegrationTests.cs (2)
  • Faker (40-48)
  • Faker (50-70)
InvoiceReminder.IntegrationTests/Data/Repository/InvoiceRepositoryIntegrationTests.cs (2)
InvoiceReminder.IntegrationTests/Data/ContainerSetup/DatabaseFixture.cs (2)
  • Task (36-43)
  • Task (45-54)
InvoiceReminder.Data/Repository/UnitOfWork.cs (6)
  • UnitOfWork (12-113)
  • UnitOfWork (20-25)
  • UnitOfWork (109-112)
  • Task (27-72)
  • Task (74-80)
  • Task (82-88)
InvoiceReminder.IntegrationTests/Data/Repository/EmailAuthTokenRepositoryIntegrationTests.cs (3)
InvoiceReminder.Data/Repository/UnitOfWork.cs (6)
  • UnitOfWork (12-113)
  • UnitOfWork (20-25)
  • UnitOfWork (109-112)
  • Task (27-72)
  • Task (74-80)
  • Task (82-88)
InvoiceReminder.Domain/Extensions/EntityExtensions.cs (1)
  • Guid (41-52)
InvoiceReminder.Domain/Entities/EmailAuthToken.cs (1)
  • EmailAuthToken (3-12)
InvoiceReminder.IntegrationTests/Data/Repository/UnitOfWorkIntegrationTests.cs (1)
InvoiceReminder.Data/Repository/UnitOfWork.cs (8)
  • UnitOfWork (12-113)
  • UnitOfWork (20-25)
  • UnitOfWork (109-112)
  • Dispose (90-101)
  • Dispose (103-107)
  • Task (27-72)
  • Task (74-80)
  • Task (82-88)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (csharp)

Comment thread .github/workflows/workflow-ci.yml Outdated
Comment thread InvoiceReminder.Data/Repository/UnitOfWork.cs
Comment thread InvoiceReminder.IntegrationTests/Data/Repository/UnitOfWorkIntegrationTests.cs Outdated
Comment thread InvoiceReminder.UnitTests.JobScheduler/MSTestSettings.cs
Refactors the data layer by specifying UUID types for IDs in entity configurations, ensuring data integrity and consistency.

Improves test project discovery by sorting the results for consistent execution across environments.

Enhances the robustness of database initialization in integration tests by adding exception handling.

Rolls back transactions safely by checking for null before attempting to rollback.
Removes docker layer caching to streamline the CI process.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (11)
.github/workflows/workflow-ci.yml (1)

37-37: Ótima adição da ordenação para execução determinística.

A ordenação dos projetos de teste garante execução consistente entre diferentes ambientes e execuções do CI. O padrão atualizado corretamente inclui os novos tipos de projetos de teste (ArchitectureTests, IntegrationTests, UnitTests).

Consideração opcional: Como os testes de integração agora utilizam Testcontainers (PostgreSQL), considere adicionar cache de imagens Docker para melhorar o desempenho do CI. Sem cache, o Testcontainers baixará as imagens em cada execução, adicionando ~30s-2min ao tempo de CI.

💡 Exemplo de implementação de cache para Testcontainers

Adicione este step antes do "Run Tests":

    - name: Cache Docker Images
      uses: actions/cache@v4
      with:
        path: /tmp/docker-images
        key: docker-${{ runner.os }}-postgres-15-alpine
        restore-keys: |
          docker-${{ runner.os }}-postgres-

    - name: Load Cached Docker Images
      run: |
        if [ -d "/tmp/docker-images" ]; then
          echo "🔄 Carregando imagens Docker do cache..."
          for img in /tmp/docker-images/*.tar; do
            [ -f "$img" ] && docker load -i "$img"
          done
        else
          echo "📦 Baixando imagens Docker necessárias..."
          docker pull postgres:15-alpine
          mkdir -p /tmp/docker-images
          docker save postgres:15-alpine -o /tmp/docker-images/postgres-15-alpine.tar
        fi

Nota: Ajuste a tag da imagem PostgreSQL (postgres:15-alpine) para corresponder à versão utilizada nos seus testes de integração.

InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/UserConfigTests.cs (1)

23-92: Adicionar verificação do índice único no Email.

O teste cobre bem as principais configurações da entidade, mas não verifica o índice único definido na propriedade Email em UserConfig.cs:

_ = builder.HasIndex(x => x.Email)
    .HasDatabaseName("idx_user_email")
    .IsUnique();

Índices são importantes para integridade e performance do banco de dados. Considere adicionar a verificação:

🔎 Sugestão de código para verificar o índice

Adicione após a linha 71:

 emailProperty.GetColumnName().ShouldBe("email");
 (!emailProperty.IsNullable).ShouldBeTrue();
+
+// Verifica índice único no Email
+var emailIndex = entityType.GetIndexes()
+    .FirstOrDefault(i => i.Properties.Count == 1 && i.Properties[0].Name == nameof(User.Email));
+_ = emailIndex.ShouldNotBeNull();
+emailIndex.IsUnique.ShouldBeTrue();
+emailIndex.GetDatabaseName().ShouldBe("idx_user_email");

Melhorias opcionais adicionais:

  1. As verificações de MaxLength (255 caracteres para Name, Email e Password) e HasDefaultValue(0) para TelegramChatId não estão sendo testadas.
  2. O estilo de asserção (!property.IsNullable).ShouldBeTrue() poderia ser mais legível como property.IsNullable.ShouldBeFalse().
  3. O método tem mais de 70 linhas - considere dividir em testes menores para melhor isolamento e mensagens de falha mais claras (ex: UserConfig_ShouldConfigureTableName, UserConfig_ShouldConfigurePrimaryKey, etc.).
InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/JobScheduleConfigTests.cs (2)

13-21: Considere remover este teste redundante.

O teste JobScheduleConfig_ShouldConfigureEntityCorrectly já instancia a classe JobScheduleConfig e valida sua funcionalidade completa. Este teste de instanciação simples não adiciona cobertura significativa e pode ser removido para simplificar a suite de testes.


35-35: Padrão de asserção incomum com operador de descarte.

As linhas que usam _ = variable.ShouldNotBeNull(); empregam um padrão incomum. As asserções do Shouldly retornam void, portanto a atribuição ao descarte (_) é desnecessária. O padrão convencional seria simplesmente variable.ShouldNotBeNull();.

🔎 Sugestão para simplificar as asserções
 // Assert
 var entityType = builder.Model.FindEntityType(typeof(JobSchedule));
-_ = entityType.ShouldNotBeNull();
+entityType.ShouldNotBeNull();

 // Verifica tabela
 entityType.GetTableName().ShouldBe("job_schedule");

 // Verifica chave primária
 var primaryKey = entityType.FindPrimaryKey();
-_ = primaryKey.ShouldNotBeNull();
+primaryKey.ShouldNotBeNull();
 primaryKey.Properties.Count.ShouldBe(1);
 primaryKey.Properties[0].Name.ShouldBe(nameof(JobSchedule.Id));

 // Verifica propriedade Id
 var idProperty = entityType.FindProperty(nameof(JobSchedule.Id));
-_ = idProperty.ShouldNotBeNull();
+idProperty.ShouldNotBeNull();

Aplicar o mesmo padrão para as demais verificações de ShouldNotBeNull().

Also applies to: 42-42, 48-48, 56-56, 63-63, 69-69, 76-76

InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/InvoiceConfigTests.cs (1)

13-21: Considere se este teste agrega valor suficiente.

Este teste verifica apenas que InvoiceConfig pode ser instanciada sem lançar exceção. Como a classe não possui lógica de construtor, dependências ou inicialização estática, o teste oferece cobertura mínima. Se este padrão não for usado consistentemente em outros testes de configuração de entidade, considere removê-lo.

InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/ScanEmailDefinitionConfigTests.cs (1)

40-84: Validações das propriedades estão funcionalmente corretas, mas podem ser mais abrangentes.

As validações de chave primária, tipos de coluna, nomes de coluna e nullability estão corretas e alinhadas com ScanEmailDefinitionConfig. No entanto, o teste poderia ser mais completo ao verificar também as restrições de MaxLength(255) configuradas para as propriedades de string (Beneficiary, Description, SenderEmailAddress, AttachmentFileName) e a conversão HasConversion<int>() para InvoiceType.

🔎 Exemplo de como adicionar validação de MaxLength
 // Verifica propriedade Beneficiary
 var beneficiaryProperty = entityType.FindProperty(nameof(ScanEmailDefinition.Beneficiary));
 _ = beneficiaryProperty.ShouldNotBeNull();
 beneficiaryProperty.GetColumnName().ShouldBe("beneficiary");
 (!beneficiaryProperty.IsNullable).ShouldBeTrue();
+beneficiaryProperty.GetMaxLength().ShouldBe(255);
InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/EmailAuthTokenConfigTests.cs (3)

14-21: Considere remover este teste de baixo valor.

O teste EmailAuthTokenConfig_ShouldNotThrowErrorWhenInstantiated apenas verifica que o construtor não lança exceção, o que é trivial para uma classe de configuração simples. Este tipo de teste não agrega valor significativo à suite de testes.


61-83: Considere adicionar verificações para constraints de MaxLength.

O teste valida nomes de colunas, tipos e nullability, mas não verifica as constraints de MaxLength definidas na configuração (AccessToken: 512, RefreshToken: 512, NonceValue: 64, TokenProvider: 25). Adicionar essas verificações tornaria o teste mais completo e detectaria mudanças inadvertidas nesses limites.

🔎 Exemplo de verificação de MaxLength
 // Verifica propriedade AccessToken
 var accessTokenProperty = entityType.FindProperty(nameof(EmailAuthToken.AccessToken));
 _ = accessTokenProperty.ShouldNotBeNull();
 accessTokenProperty.GetColumnName().ShouldBe("access_token");
+accessTokenProperty.GetMaxLength().ShouldBe(512);
 (!accessTokenProperty.IsNullable).ShouldBeTrue();
 
 // Verifica propriedade RefreshToken
 var refreshTokenProperty = entityType.FindProperty(nameof(EmailAuthToken.RefreshToken));
 _ = refreshTokenProperty.ShouldNotBeNull();
 refreshTokenProperty.GetColumnName().ShouldBe("refresh_token");
+refreshTokenProperty.GetMaxLength().ShouldBe(512);
 (!refreshTokenProperty.IsNullable).ShouldBeTrue();
 
 // Verifica propriedade NonceValue
 var nonceValueProperty = entityType.FindProperty(nameof(EmailAuthToken.NonceValue));
 _ = nonceValueProperty.ShouldNotBeNull();
 nonceValueProperty.GetColumnName().ShouldBe("nonce_value");
+nonceValueProperty.GetMaxLength().ShouldBe(64);
 (!nonceValueProperty.IsNullable).ShouldBeTrue();
 
 // Verifica propriedade TokenProvider
 var tokenProviderProperty = entityType.FindProperty(nameof(EmailAuthToken.TokenProvider));
 _ = tokenProviderProperty.ShouldNotBeNull();
 tokenProviderProperty.GetColumnName().ShouldBe("token_provider");
+tokenProviderProperty.GetMaxLength().ShouldBe(25);
 (!tokenProviderProperty.IsNullable).ShouldBeTrue();

51-103: Simplifique as verificações de nullability para melhor legibilidade.

O padrão (!property.IsNullable).ShouldBeTrue() usado nas linhas 51, 59, 65, 71, 77, 83, 89, 96 e 103 pode ser simplificado para property.IsNullable.ShouldBeFalse(), o que torna a intenção mais clara e direta.

🔎 Exemplo de simplificação
-(!idProperty.IsNullable).ShouldBeTrue();
+idProperty.IsNullable.ShouldBeFalse();

Aplique o mesmo padrão para todas as outras verificações de nullability no teste.

InvoiceReminder.IntegrationTests/Data/Repository/UnitOfWorkIntegrationTests.cs (1)

45-53: UserFaker duplicado - considere reutilizar TestData.UserFaker().

Este arquivo define seu próprio UserFaker que é praticamente idêntico ao TestData.UserFaker() (exceto pelos campos CreatedAt/UpdatedAt ausentes em ambos). Para manter consistência e evitar duplicação, considere reutilizar o helper centralizado.

🔎 Refatoração sugerida
+using InvoiceReminder.IntegrationTests.Data.Utils;
 ...
 
-    private static Faker<User> UserFaker()
-    {
-        return new Faker<User>()
-            .RuleFor(u => u.Id, _ => Guid.NewGuid())
-            .RuleFor(u => u.TelegramChatId, f => f.Random.Long(100000000, long.MaxValue))
-            .RuleFor(u => u.Name, f => f.Person.FullName)
-            .RuleFor(u => u.Email, f => f.Internet.Email())
-            .RuleFor(u => u.Password, f => f.Internet.Password(length: 16, memorable: false));
-    }
+    // Use TestData.UserFaker() instead

E nas chamadas:

-        var user = UserFaker().Generate();
+        var user = TestData.UserFaker().Generate();
InvoiceReminder.IntegrationTests/Data/Repository/BaseRepositoryIntegrationTests.cs (1)

23-43: Considere adicionar isolamento de dados entre testes.

Os testes atualmente persistem dados no banco PostgreSQL sem limpeza entre execuções. Isso pode causar testes instáveis, especialmente em testes como GetAll_Should_Return_All_Entities (linha 254) que usa ShouldBeGreaterThanOrEqualTo(3) ao invés de verificar uma contagem exata.

Considere implementar um dos seguintes padrões:

  1. Usar transações que são revertidas após cada teste
  2. Limpar dados específicos no TestCleanup
  3. Usar banco de dados isolado por teste (se viável com Testcontainers)
💡 Exemplo de padrão com transação
 private CoreDbContext _dbContext;
 private BaseRepository<CoreDbContext, User> _userRepository;
 private BaseRepository<CoreDbContext, Invoice> _invoiceRepository;
 private UnitOfWork _unitOfWork;
+private IDbContextTransaction _transaction;

 [TestInitialize]
 public void TestInitialize()
 {
     var options = new DbContextOptionsBuilder<CoreDbContext>()
         .UseNpgsql(DatabaseFixture.ConnectionString)
         .Options;

     _dbContext = new CoreDbContext(options);
+    _transaction = _dbContext.Database.BeginTransaction();
     _userRepository = new BaseRepository<CoreDbContext, User>(_dbContext);
     _invoiceRepository = new BaseRepository<CoreDbContext, Invoice>(_dbContext);
     _unitOfWork = new UnitOfWork(_dbContext, Substitute.For<ILogger<UnitOfWork>>());
 }

 [TestCleanup]
 public void TestCleanup()
 {
+    _transaction?.Rollback();
+    _transaction?.Dispose();
     _unitOfWork?.Dispose();
     _userRepository?.Dispose();
     _invoiceRepository?.Dispose();
     _dbContext?.Dispose();
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7774a97 and 301c4b5.

📒 Files selected for processing (22)
  • .github/workflows/workflow-ci.yml
  • InvoiceReminder.Data/Migrations/20250930210104_Initial_Create.cs
  • InvoiceReminder.Data/Persistence/EntitiesConfig/JobScheduleConfig.cs
  • InvoiceReminder.Data/Persistence/EntitiesConfig/ScanEmailDefinitionConfig.cs
  • InvoiceReminder.Data/Repository/UnitOfWork.cs
  • InvoiceReminder.IntegrationTests/Data/ContainerSetup/DatabaseFixture.cs
  • InvoiceReminder.IntegrationTests/Data/Repository/BaseRepositoryIntegrationTests.cs
  • InvoiceReminder.IntegrationTests/Data/Repository/EmailAuthTokenRepositoryIntegrationTests.cs
  • InvoiceReminder.IntegrationTests/Data/Repository/InvoiceRepositoryIntegrationTests.cs
  • InvoiceReminder.IntegrationTests/Data/Repository/JobScheduleRepositoryIntegrationTests.cs
  • InvoiceReminder.IntegrationTests/Data/Repository/ScanEmailDefinitionRepositoryIntegrationTests.cs
  • InvoiceReminder.IntegrationTests/Data/Repository/UnitOfWorkIntegrationTests.cs
  • InvoiceReminder.IntegrationTests/Data/Repository/UserRepositoryIntegrationTests.cs
  • InvoiceReminder.IntegrationTests/Data/Utils/TestData.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/EmailAuthTokenConfigTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/InvoiceConfigTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/JobScheduleConfigTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/ScanEmailDefinitionConfigTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/UserConfigTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/InvoiceReminder.UnitTests.Infrastructure.csproj
  • InvoiceReminder.UnitTests.JobScheduler/HostedService/QuartzHostedServiceTests.cs
  • InvoiceReminder.UnitTests.JobScheduler/JobSettings/CronJobTests.cs
🚧 Files skipped from review as they are similar to previous changes (4)
  • InvoiceReminder.UnitTests.JobScheduler/HostedService/QuartzHostedServiceTests.cs
  • InvoiceReminder.IntegrationTests/Data/ContainerSetup/DatabaseFixture.cs
  • InvoiceReminder.IntegrationTests/Data/Repository/UserRepositoryIntegrationTests.cs
  • InvoiceReminder.Data/Persistence/EntitiesConfig/ScanEmailDefinitionConfig.cs
🧰 Additional context used
🧬 Code graph analysis (12)
InvoiceReminder.UnitTests.JobScheduler/JobSettings/CronJobTests.cs (2)
InvoiceReminder.JobScheduler.UnitTests/JobSettings/CronJobTests.cs (3)
  • CronJobTests (11-111)
  • Execute_ShouldCreateScopeResolveServiceAndSendMessage (44-65)
  • CronJobTests (23-42)
InvoiceReminder.JobScheduler.UnitTests/HostedService/QuartzHostedServiceTests.cs (1)
  • QuartzHostedServiceTests (11-263)
InvoiceReminder.IntegrationTests/Data/Utils/TestData.cs (3)
InvoiceReminder.Domain/Entities/EmailAuthToken.cs (1)
  • EmailAuthToken (3-12)
InvoiceReminder.Domain/Entities/JobSchedule.cs (1)
  • JobSchedule (3-7)
InvoiceReminder.Domain/Entities/ScanEmailDefinition.cs (1)
  • ScanEmailDefinition (5-13)
InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/EmailAuthTokenConfigTests.cs (2)
InvoiceReminder.Data/Persistence/EntitiesConfig/EmailAuthTokenConfig.cs (1)
  • EmailAuthTokenConfig (10-64)
InvoiceReminder.Domain/Entities/EmailAuthToken.cs (1)
  • EmailAuthToken (3-12)
InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/InvoiceConfigTests.cs (1)
InvoiceReminder.Data/Persistence/EntitiesConfig/InvoiceConfig.cs (2)
  • InvoiceConfig (10-62)
  • Configure (12-61)
InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/UserConfigTests.cs (1)
InvoiceReminder.Data/Persistence/EntitiesConfig/UserConfig.cs (2)
  • UserConfig (10-59)
  • Configure (12-58)
InvoiceReminder.Data/Repository/UnitOfWork.cs (1)
InvoiceReminder.Data/Repository/BaseRepository.cs (2)
  • Dispose (79-87)
  • Dispose (89-93)
InvoiceReminder.IntegrationTests/Data/Repository/ScanEmailDefinitionRepositoryIntegrationTests.cs (5)
InvoiceReminder.IntegrationTests/Data/ContainerSetup/DatabaseFixture.cs (2)
  • Task (44-51)
  • Task (53-62)
InvoiceReminder.IntegrationTests/Data/Repository/EmailAuthTokenRepositoryIntegrationTests.cs (2)
  • Task (260-271)
  • Task (273-283)
InvoiceReminder.Data/Repository/UnitOfWork.cs (8)
  • UnitOfWork (12-113)
  • UnitOfWork (20-25)
  • UnitOfWork (109-112)
  • Dispose (90-101)
  • Dispose (103-107)
  • Task (27-72)
  • Task (74-80)
  • Task (82-88)
InvoiceReminder.Domain/Entities/ScanEmailDefinition.cs (1)
  • ScanEmailDefinition (5-13)
InvoiceReminder.IntegrationTests/Data/Utils/TestData.cs (1)
  • TestData (7-84)
InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/ScanEmailDefinitionConfigTests.cs (2)
InvoiceReminder.Data/Persistence/EntitiesConfig/ScanEmailDefinitionConfig.cs (2)
  • ScanEmailDefinitionConfig (10-64)
  • Configure (12-63)
InvoiceReminder.Domain/Entities/ScanEmailDefinition.cs (1)
  • ScanEmailDefinition (5-13)
InvoiceReminder.IntegrationTests/Data/Repository/EmailAuthTokenRepositoryIntegrationTests.cs (3)
InvoiceReminder.Data/Repository/UnitOfWork.cs (8)
  • UnitOfWork (12-113)
  • UnitOfWork (20-25)
  • UnitOfWork (109-112)
  • Dispose (90-101)
  • Dispose (103-107)
  • Task (27-72)
  • Task (74-80)
  • Task (82-88)
InvoiceReminder.Domain/Entities/EmailAuthToken.cs (1)
  • EmailAuthToken (3-12)
InvoiceReminder.IntegrationTests/Data/Utils/TestData.cs (1)
  • TestData (7-84)
InvoiceReminder.IntegrationTests/Data/Repository/InvoiceRepositoryIntegrationTests.cs (1)
InvoiceReminder.IntegrationTests/Data/ContainerSetup/DatabaseFixture.cs (3)
  • TestClass (8-63)
  • Task (44-51)
  • Task (53-62)
InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/JobScheduleConfigTests.cs (2)
InvoiceReminder.Data/Persistence/EntitiesConfig/JobScheduleConfig.cs (2)
  • JobScheduleConfig (10-44)
  • Configure (12-43)
InvoiceReminder.Domain/Entities/JobSchedule.cs (1)
  • JobSchedule (3-7)
InvoiceReminder.IntegrationTests/Data/Repository/JobScheduleRepositoryIntegrationTests.cs (3)
InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/JobScheduleConfigTests.cs (1)
  • TestClass (10-81)
InvoiceReminder.Domain/Entities/JobSchedule.cs (1)
  • JobSchedule (3-7)
InvoiceReminder.IntegrationTests/Data/Utils/TestData.cs (1)
  • TestData (7-84)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (csharp)
🔇 Additional comments (37)
InvoiceReminder.UnitTests.Infrastructure/InvoiceReminder.UnitTests.Infrastructure.csproj (2)

15-23: Boa prática: remoção de dependências do EF Core dos testes unitários.

A remoção das referências aos pacotes do Entity Framework Core (Microsoft.EntityFrameworkCore, Microsoft.EntityFrameworkCore.Relational, Microsoft.EntityFrameworkCore.Sqlite) dos testes unitários está alinhada com as melhores práticas. Testes unitários devem usar mocks (NSubstitute está presente) para interações com banco de dados, enquanto os testes de integração com Testcontainers devem estar em projetos separados.

Verifique se todos os testes unitários compilam corretamente sem essas dependências diretas.


12-12: Refatoração validada - estrutura consistente.

A referência ao projeto InvoiceReminder.UnitTests.SUT.Assets está correta e o projeto existe com a estrutura esperada. A remoção das dependências do Entity Framework Core é uma boa prática para testes unitários, mantendo o foco em testes isolados com mocks via NSubstitute. As dependências estão adequadas para o escopo de testes unitários.

InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/UserConfigTests.cs (1)

13-21: Teste de instanciação está correto.

O teste verifica que a classe UserConfig pode ser instanciada sem lançar exceções. Embora seja um teste básico (já que não há lógica no construtor), ele pode ser útil para detectar problemas futuros caso alguém adicione lógica ao construtor.

InvoiceReminder.Data/Persistence/EntitiesConfig/JobScheduleConfig.cs (3)

24-27: Consistência: Tipagem UUID aplicada à chave estrangeira.

A adição de HasColumnType("uuid") para a propriedade UserId mantém consistência com a tipagem explícita aplicada à propriedade Id. Isso garante que a chave estrangeira tenha o mesmo tipo de coluna que a chave primária referenciada, melhorando a integridade referencial.


18-22: Tipagem explícita UUID para PostgreSQL está implementada de forma consistente.

A adição de HasColumnType("uuid") para a propriedade Id melhora a consistência entre o tipo C# Guid e o tipo de coluna PostgreSQL. Esta mudança está implementada de forma sistemática em todas as configurações de entidades (Invoice, ScanEmailDefinition, User, EmailAuthToken e JobSchedule), e as migrações do EF Core foram adequadamente atualizadas no Designer e ModelSnapshot.


6-6: O nome do assembly de testes atualizado está correto e consistente.

A alteração do atributo InternalsVisibleTo para InvoiceReminder.UnitTests.Infrastructure está validada. O projeto de testes foi encontrado no repositório com a estrutura esperada, e a configuração é consistente em todos os arquivos de entidade do Data Layer (JobScheduleConfig.cs, InvoiceConfig.cs, UserConfig.cs, ScanEmailDefinitionConfig.cs e EmailAuthTokenConfig.cs). Nenhuma referência ao nome antigo permanece no codebase.

InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/InvoiceConfigTests.cs (3)

1-8: LGTM! Imports e namespace estão corretos.

Os imports são apropriados para testar configurações de entidades do EF Core, e o namespace segue a nova estrutura de organização dos testes.


10-11: LGTM! Declaração da classe de teste está correta.

A classe segue as convenções do MSTest e o modificador sealed é apropriado para classes de teste.


23-107: Excelente! Teste de configuração abrangente e bem estruturado.

Este teste valida de forma completa a configuração da entidade Invoice:

  • Verifica corretamente o nome da tabela, chave primária e todas as propriedades
  • Confirma mapeamentos de colunas, tipos, nulabilidade e restrições
  • Inclui propriedades herdadas (CreatedAt, UpdatedAt)
  • Uso correto de ConventionSet vazio para teste isolado da configuração

A cobertura está alinhada com toda a configuração definida em InvoiceConfig.cs.

InvoiceReminder.UnitTests.JobScheduler/JobSettings/CronJobTests.cs (2)

9-9: Alteração de namespace aprovada.

A reorganização do namespace de InvoiceReminder.JobScheduler.UnitTests.JobSettings para InvoiceReminder.UnitTests.JobScheduler.JobSettings está alinhada com os objetivos do PR de melhorar a clareza e manutenibilidade dos projetos de teste.


62-68: Simplificação das asserções de log aprovada.

A refatoração para usar Arg.Any<>() torna as asserções mais concisas e focadas no que realmente importa (verificar que o log foi chamado com LogLevel.Information). O uso de null para o parâmetro de exceção é apropriado para este cenário de sucesso.

InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/ScanEmailDefinitionConfigTests.cs (6)

1-8: Importações e namespace apropriados.

As importações incluem todas as dependências necessárias para testar configurações de entidades EF Core usando ModelBuilder e ConventionSet. O uso do Shouldly para asserções está alinhado com o padrão do projeto.


10-12: Declaração da classe de teste está correta.

O uso de sealed é uma boa prática para classes de teste que não precisam de herança. A convenção de nomenclatura está consistente com o padrão do projeto.


13-21: Teste de instanciação é adequado.

Este teste de fumaça básico garante que a classe de configuração pode ser instanciada sem erros, o que é uma verificação válida para classes de configuração EF Core.


23-38: Configuração do teste e validação da tabela estão corretas.

O uso de ConventionSet vazia para testes isolados é apropriado. A validação do nome da tabela está alinhada com a configuração em ScanEmailDefinitionConfig.


91-103: Validações das propriedades de auditoria estão corretas.

As verificações de CreatedAt e UpdatedAt estão apropriadas, incluindo mapeamento de colunas, tipos e nullability. A validação de propriedades herdadas de EntityDefaults demonstra uma cobertura de teste completa.


1-104: Nenhum erro de sintaxe encontrado na entidade de domínio.

A propriedade SenderEmailAddress em InvoiceReminder.Domain/Entities/ScanEmailDefinition.cs (linha 11) está sintaticamente correta: public string SenderEmailAddress { get; set; }. Não há duplo ponto-e-vírgula ou qualquer outro erro de sintaxe que impeça a compilação.

Likely an incorrect or invalid review comment.

InvoiceReminder.Data/Repository/UnitOfWork.cs (1)

27-71: Implementação robusta de transações com tratamento adequado de nulos.

A lógica de SaveChangesAsync está bem estruturada com tratamento de cancelamento e rollback. O uso do operador ?. na linha 63 (await transaction?.RollbackAsync(...)) garante que o rollback só será executado se a transação tiver sido iniciada com sucesso, evitando NullReferenceException.

O padrão de abrir conexão → iniciar transação → salvar → commit está correto, e o bloco finally garante a liberação dos recursos.

InvoiceReminder.IntegrationTests/Data/Repository/ScanEmailDefinitionRepositoryIntegrationTests.cs (2)

1-46: Estrutura de testes de integração bem organizada.

A configuração do teste está adequada: cria um contexto real conectado ao PostgreSQL via Testcontainers, utiliza mocks para loggers, e o TestCleanup garante a liberação dos recursos. A inicialização de campos readonly no construtor é uma boa prática.


586-609: Métodos auxiliares bem estruturados para criação de dados de teste.

Os helpers CreateAndSaveUserAsync e CreateAndSaveScanEmailDefinitionAsync encapsulam corretamente a criação e persistência de entidades, respeitando as relações de chave estrangeira (o ScanEmailDefinition precisa de um User existente).

InvoiceReminder.IntegrationTests/Data/Repository/EmailAuthTokenRepositoryIntegrationTests.cs (1)

1-46: Testes de integração consistentes com o padrão do projeto.

A estrutura segue o mesmo padrão dos outros testes de repositório: inicialização via construtor, cleanup via TestCleanup, e uso do DatabaseFixture para conexão com PostgreSQL. Cobertura adequada para os métodos do repositório.

InvoiceReminder.IntegrationTests/Data/Utils/TestData.cs (1)

33-53: Boa utilização de dados de domínio realistas.

O InvoiceFaker utiliza nomes de bancos brasileiros reais e valores monetários apropriados, tornando os dados de teste mais representativos do cenário real de uso.

InvoiceReminder.Data/Migrations/20250930210104_Initial_Create.cs (2)

33-57: Dados de seed com senhas hardcoded - aceitável para desenvolvimento.

Os dados de seed inserem usuários de teste com senhas fixas (hash SHA-256 de "123456"). Isso é adequado para ambiente de desenvolvimento/testes, mas garanta que esses dados não sejam utilizados em produção e que a documentação indique que estas são credenciais de teste.

Verifique se há documentação indicando que os usuários seed são apenas para desenvolvimento/testes.


163-192: Índices bem definidos para otimização de consultas.

Os índices nas colunas de chave estrangeira (user_id) e o índice único no email do usuário (idx_user_email) estão corretamente configurados, garantindo performance nas consultas e integridade dos dados.

InvoiceReminder.IntegrationTests/Data/Repository/JobScheduleRepositoryIntegrationTests.cs (1)

1-46: Testes de integração consistentes com o padrão estabelecido.

A estrutura segue fielmente o padrão dos outros testes de repositório no projeto. O uso de BulkInsertAsync no teste de múltiplos registros (linha 174) demonstra cobertura de diferentes métodos de inserção.

InvoiceReminder.IntegrationTests/Data/Repository/UnitOfWorkIntegrationTests.cs (1)

186-248: Excelente cobertura de testes para cenários de rollback.

Os testes SaveChangesAsync_Should_Rollback_On_Invalid_Data e SaveChangesAsync_Should_Rollback_Transaction_On_Constraint_Violation verificam corretamente o comportamento de rollback em caso de falha, incluindo a verificação de logging de erro. A estratégia de criar um novo contexto após a falha para o teste de constraint violation (linhas 228-232) é apropriada.

InvoiceReminder.IntegrationTests/Data/Repository/InvoiceRepositoryIntegrationTests.cs (2)

1-46: Testes de integração bem estruturados para InvoiceRepository.

A implementação segue o padrão consistente dos outros testes de repositório. O teste de whitespace no barcode (linha 156-172) é importante para garantir que a busca funciona mesmo com espaços extras na entrada.


263-288: Métodos auxiliares seguem o padrão estabelecido.

Os helpers CreateAndSaveUserAsync e CreateAndSaveInvoiceAsync estão consistentes com os de outras classes de teste, garantindo a criação correta de entidades respeitando as dependências de chave estrangeira.

InvoiceReminder.IntegrationTests/Data/Repository/BaseRepositoryIntegrationTests.cs (9)

47-97: Ótima cobertura de testes para AddAsync.

Os testes cobrem adequadamente os cenários de adição de entidade, verificando o estado da entidade, a persistência após SaveChanges, e o retorno correto. O uso de DbContext separado para verificação de persistência é uma boa prática.


103-175: Excelente cobertura de cenários de BulkInsertAsync.

Os testes cobrem casos importantes incluindo inserção múltipla, validação de timestamps automáticos, persistência, coleção vazia e tratamento de cancelamento. A validação de timestamps (linha 126) garante que o comportamento de auditoria funciona corretamente.


181-226: Testes de GetByIdAsync bem estruturados.

Os testes cobrem os cenários principais: recuperação por ID existente, retorno nulo para ID inexistente, e tratamento de cancelamento. As validações com ShouldSatisfyAllConditions garantem verificações completas.


232-283: Ótimos testes para GetAll e comportamento NoTracking.

Os testes verificam corretamente a recuperação de todas as entidades e o comportamento AsNoTracking (confirmando EntityState.Detached na linha 282). O padrão de criar um repositório novo para garantir leitura fresca do banco é apropriado.


302-357: Testes de Remove cobrem cenários importantes.

Os testes verificam adequadamente a marcação como deletado, a remoção efetiva do banco após SaveChanges, e o tratamento correto de entidades desanexadas (linha 349). O teste de desanexamento é particularmente importante para cenários de APIs desconectadas.


363-442: Cobertura completa para operação Update.

Os testes cobrem todos os aspectos importantes: marcação como modificado, persistência de mudanças, tratamento de entidades desanexadas, e verificação do retorno. O cenário de desanexamento (linha 416) é essencial para APIs que trabalham com DTOs.


448-540: Excelente cobertura de consultas com Where.

Os testes cobrem bem diferentes cenários de filtragem: único resultado, múltiplos resultados, nenhum resultado, e predicados complexos. O uso de RuleFor para definir valores específicos nos testes garante resultados determinísticos.


546-585: Testes de Dispose garantem gerenciamento correto de recursos.

Os testes verificam que o DbContext é propriamente liberado (linha 563) e que múltiplas chamadas a Dispose são seguras (linhas 579-584). Isso garante que o repositório segue o padrão IDisposable corretamente.


591-600: Helper method útil para criar repositórios isolados.

O método CreateFreshRepository<T> fornece uma maneira limpa de criar instâncias de repositório com contexto novo, útil para testes que precisam verificar persistência com contexto separado.

Comment thread InvoiceReminder.IntegrationTests/Data/Utils/TestData.cs
Comment thread InvoiceReminder.UnitTests.JobScheduler/JobSettings/CronJobTests.cs
Implements bulk remove and update functionalities in the base repository for improved data manipulation efficiency.

Also adds timestamping of updated entities during bulk updates to maintain audit trails.

Removes redundant tests and adds data seeding to integration tests for better test coverage.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (5)
InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/InvoiceConfigTests.cs (1)

41-41: Considere simplificar as asserções de nulabilidade.

O padrão (!property.IsNullable).ShouldBeTrue() funciona corretamente, mas a leitura seria mais direta usando property.IsNullable.ShouldBeFalse().

🔎 Sugestão de refatoração para melhor legibilidade
-        (!idProperty.IsNullable).ShouldBeTrue();
+        idProperty.IsNullable.ShouldBeFalse();

-        (!userIdProperty.IsNullable).ShouldBeTrue();
+        userIdProperty.IsNullable.ShouldBeFalse();

-        (!bankProperty.IsNullable).ShouldBeTrue();
+        bankProperty.IsNullable.ShouldBeFalse();

-        (!beneficiaryProperty.IsNullable).ShouldBeTrue();
+        beneficiaryProperty.IsNullable.ShouldBeFalse();

-        (!barcodeProperty.IsNullable).ShouldBeTrue();
+        barcodeProperty.IsNullable.ShouldBeFalse();

-        (!amountProperty.IsNullable).ShouldBeTrue();
+        amountProperty.IsNullable.ShouldBeFalse();

-        (!dueDateProperty.IsNullable).ShouldBeTrue();
+        dueDateProperty.IsNullable.ShouldBeFalse();

-        (!createdAtProperty.IsNullable).ShouldBeTrue();
+        createdAtProperty.IsNullable.ShouldBeFalse();

-        (!updatedAtProperty.IsNullable).ShouldBeTrue();
+        updatedAtProperty.IsNullable.ShouldBeFalse();

Also applies to: 49-49, 55-55, 62-62, 69-69, 75-75, 82-82, 89-89, 96-96

InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/JobScheduleConfigTests.cs (1)

17-17: Considere usar o ConventionSet padrão do EF Core.

A criação de um ConventionSet vazio pode não aplicar as convenções padrão do EF Core (como descoberta de chaves, nomenclatura, etc.). Embora o teste funcione porque JobScheduleConfig configura explicitamente todas as propriedades, usar as convenções padrão tornaria o teste mais representativo do comportamento real do EF Core.

🔎 Refatoração sugerida
 // Arrange
-var builder = new ModelBuilder(new ConventionSet());
+var builder = new ModelBuilder(ConventionSet.CreateConventionSet(new DbContext(new DbContextOptions<DbContext>())));
 var config = new JobScheduleConfig();

Ou, alternativamente, use um DbContextOptionsBuilder real:

 // Arrange
+var options = new DbContextOptionsBuilder<DbContext>()
+    .UseInMemoryDatabase("test")
+    .Options;
+var builder = new ModelBuilder(options.GetExtension<CoreOptionsExtension>().Model.ConventionSet);
-var builder = new ModelBuilder(new ConventionSet());
 var config = new JobScheduleConfig();
InvoiceReminder.Data/Repository/BaseRepository.cs (1)

47-55: BulkUpdateAsync implementado corretamente com atualização de timestamp.

A implementação atualiza adequadamente UpdatedAt antes da operação bulk, garantindo auditoria consistente. O padrão de reflexão usado é consistente com BulkInsertAsync.

No entanto, considere que a abordagem baseada em reflexão falha silenciosamente se a propriedade não existir. Para maior robustez, você pode considerar usar uma interface ou classe base que garanta a presença dessas propriedades.

💡 Refatoração opcional para maior segurança de tipos

Considere criar uma interface ou constraint genérico para entidades com timestamps:

public interface ITimestampedEntity
{
    DateTime CreatedAt { get; set; }
    DateTime UpdatedAt { get; set; }
}

// Então no método:
public virtual async Task BulkUpdateAsync(ICollection<TEntity> entities, CancellationToken cancellationToken = default)
    where TEntity : ITimestampedEntity
{
    foreach (var entity in entities)
    {
        entity.UpdatedAt = DateTime.UtcNow;
    }

    await _dbContext.BulkUpdateAsync(entities, cancellationToken: cancellationToken);
}

Isso eliminaria a necessidade de reflexão e forneceria verificação em tempo de compilação.

InvoiceReminder.IntegrationTests/Data/Utils/TestData.cs (2)

17-18: Timestamps do UserFaker agora definidos corretamente!

Ótimo trabalho ao adicionar CreatedAt e UpdatedAt ao UserFaker, resolvendo o problema apontado na revisão anterior.

No entanto, há uma inconsistência lógica: Date.Past() pode retornar datas muito antigas (até 1 ano atrás por padrão), enquanto Date.Recent() retorna datas dos últimos dias. Isso pode resultar em UpdatedAt anterior a CreatedAt, violando a lógica temporal de entidades.

🔎 Correção sugerida para garantir UpdatedAt >= CreatedAt
 public static Faker<User> UserFaker()
 {
     return new Faker<User>()
         .RuleFor(u => u.Id, _ => Guid.NewGuid())
         .RuleFor(u => u.TelegramChatId, f => f.Random.Long(100000000, long.MaxValue))
         .RuleFor(u => u.Name, f => f.Person.FullName)
         .RuleFor(u => u.Email, f => f.Internet.Email())
         .RuleFor(u => u.Password, f => f.Internet.Password(length: 16, memorable: false))
-        .RuleFor(u => u.CreatedAt, f => f.Date.Past().ToUniversalTime())
-        .RuleFor(u => u.UpdatedAt, f => f.Date.Recent().ToUniversalTime());
+        .RuleFor(u => u.CreatedAt, f => f.Date.Past().ToUniversalTime())
+        .RuleFor(u => u.UpdatedAt, (f, u) => f.Date.Between(u.CreatedAt, DateTime.UtcNow).ToUniversalTime());
 }

Isso garante que UpdatedAt sempre será maior ou igual a CreatedAt.


21-33: Inconsistência de timestamps em todos os Fakers.

Todos os fakers (EmailAuthTokenFaker, InvoiceFaker, JobScheduleFaker, ScanEmailDefinitionFaker) têm a mesma inconsistência lógica observada no UserFaker: usar Date.Past() para CreatedAt e Date.Recent() para UpdatedAt pode resultar em UpdatedAt anterior a CreatedAt.

🔎 Correção sugerida para todos os Fakers

Aplique o mesmo padrão de correção a todos os fakers, garantindo que UpdatedAt esteja sempre entre CreatedAt e DateTime.UtcNow:

 // Para EmailAuthTokenFaker (linhas 31-32)
-.RuleFor(e => e.CreatedAt, faker => faker.Date.Past().ToUniversalTime())
-.RuleFor(e => e.UpdatedAt, faker => faker.Date.Recent().ToUniversalTime());
+.RuleFor(e => e.CreatedAt, faker => faker.Date.Past().ToUniversalTime())
+.RuleFor(e => e.UpdatedAt, (faker, e) => faker.Date.Between(e.CreatedAt, DateTime.UtcNow).ToUniversalTime());

 // Para InvoiceFaker (linhas 53-54)
-.RuleFor(i => i.CreatedAt, faker => faker.Date.Past().ToUniversalTime())
-.RuleFor(i => i.UpdatedAt, faker => faker.Date.Recent().ToUniversalTime());
+.RuleFor(i => i.CreatedAt, faker => faker.Date.Past().ToUniversalTime())
+.RuleFor(i => i.UpdatedAt, (faker, i) => faker.Date.Between(i.CreatedAt, DateTime.UtcNow).ToUniversalTime());

 // Para JobScheduleFaker (linhas 69-70)
-.RuleFor(j => j.CreatedAt, faker => faker.Date.Past().ToUniversalTime())
-.RuleFor(j => j.UpdatedAt, faker => faker.Date.Recent().ToUniversalTime());
+.RuleFor(j => j.CreatedAt, faker => faker.Date.Past().ToUniversalTime())
+.RuleFor(j => j.UpdatedAt, (faker, j) => faker.Date.Between(j.CreatedAt, DateTime.UtcNow).ToUniversalTime());

 // Para ScanEmailDefinitionFaker (linhas 83-84)
-.RuleFor(s => s.CreatedAt, faker => faker.Date.Past().ToUniversalTime())
-.RuleFor(s => s.UpdatedAt, faker => faker.Date.Recent().ToUniversalTime());
+.RuleFor(s => s.CreatedAt, faker => faker.Date.Past().ToUniversalTime())
+.RuleFor(s => s.UpdatedAt, (faker, s) => faker.Date.Between(s.CreatedAt, DateTime.UtcNow).ToUniversalTime());

Also applies to: 35-55, 57-71, 73-85

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 301c4b5 and b950766.

📒 Files selected for processing (10)
  • InvoiceReminder.Data/Interfaces/IBaseRepository.cs
  • InvoiceReminder.Data/Repository/BaseRepository.cs
  • InvoiceReminder.IntegrationTests/Data/Repository/BaseRepositoryIntegrationTests.cs
  • InvoiceReminder.IntegrationTests/Data/Utils/TestData.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/EmailAuthTokenConfigTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/InvoiceConfigTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/JobScheduleConfigTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/ScanEmailDefinitionConfigTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/UserConfigTests.cs
  • InvoiceReminder.UnitTests.JobScheduler/JobSettings/CronJobTests.cs
🚧 Files skipped from review as they are similar to previous changes (3)
  • InvoiceReminder.UnitTests.JobScheduler/JobSettings/CronJobTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/ScanEmailDefinitionConfigTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/UserConfigTests.cs
🧰 Additional context used
🧬 Code graph analysis (6)
InvoiceReminder.Data/Interfaces/IBaseRepository.cs (3)
InvoiceReminder.Data/Repository/BaseRepository.cs (1)
  • TEntity (77-87)
InvoiceReminder.Data/Interfaces/IUnitOfWork.cs (2)
  • SaveChangesAsync (5-5)
  • IUnitOfWork (3-6)
InvoiceReminder.Application/AppServices/BaseAppService.cs (2)
  • BaseAppService (8-90)
  • BulkInsertAsync (35-45)
InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/EmailAuthTokenConfigTests.cs (2)
InvoiceReminder.Data/Persistence/EntitiesConfig/EmailAuthTokenConfig.cs (1)
  • EmailAuthTokenConfig (10-64)
InvoiceReminder.Domain/Entities/EmailAuthToken.cs (1)
  • EmailAuthToken (3-12)
InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/JobScheduleConfigTests.cs (1)
InvoiceReminder.Domain/Entities/JobSchedule.cs (1)
  • JobSchedule (3-7)
InvoiceReminder.Data/Repository/BaseRepository.cs (1)
InvoiceReminder.Data/Interfaces/IBaseRepository.cs (6)
  • Task (7-7)
  • Task (8-8)
  • Task (9-9)
  • Task (10-10)
  • Task (12-12)
  • TEntity (14-14)
InvoiceReminder.IntegrationTests/Data/Utils/TestData.cs (4)
InvoiceReminder.Domain/Extensions/EntityExtensions.cs (1)
  • Guid (41-52)
InvoiceReminder.Domain/Entities/EmailAuthToken.cs (1)
  • EmailAuthToken (3-12)
InvoiceReminder.Domain/Entities/JobSchedule.cs (1)
  • JobSchedule (3-7)
InvoiceReminder.Domain/Entities/ScanEmailDefinition.cs (1)
  • ScanEmailDefinition (5-13)
InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/InvoiceConfigTests.cs (1)
InvoiceReminder.Data/Persistence/EntitiesConfig/InvoiceConfig.cs (2)
  • InvoiceConfig (10-62)
  • Configure (12-61)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (csharp)
🔇 Additional comments (10)
InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/EmailAuthTokenConfigTests.cs (1)

78-83: Problema anterior resolvido com sucesso!

A verificação do tipo de coluna para AccessTokenExpiry foi adicionada corretamente na linha 82. Isso resolve a inconsistência apontada no comentário anterior e garante que a configuração de "timestamp with time zone" seja validada pelo teste.

InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/InvoiceConfigTests.cs (2)

13-97: Excelente cobertura de testes!

O teste valida de forma abrangente todos os aspectos da configuração do EF Core para a entidade Invoice:

  • Nome da tabela
  • Chave primária
  • Todas as propriedades (incluindo as herdadas de EntityDefaults)
  • Nomes de colunas, tipos, nulabilidade, comprimentos máximos e geração de valores

A estrutura está clara e consistente com os outros testes de configuração do projeto.


16-21: A configuração de InternalsVisibleTo já está corretamente implementada. O arquivo InvoiceConfig.cs na linha 6 contém o atributo [assembly: InternalsVisibleTo("InvoiceReminder.UnitTests.Infrastructure")], permitindo o acesso à classe internal a partir do projeto de testes. Nenhuma ação é necessária.

InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/JobScheduleConfigTests.cs (1)

51-56: Validação de MaxLength corretamente implementada!

Ótimo trabalho ao adicionar a validação de MaxLength na linha 55. Isso garante cobertura completa da configuração de CronExpression.

InvoiceReminder.Data/Interfaces/IBaseRepository.cs (1)

9-10: Adição de métodos bulk está correta.

As novas assinaturas BulkRemoveAsync e BulkUpdateAsync seguem o padrão existente da interface e complementam bem a operação BulkInsertAsync existente. A consistência na assinatura (uso de ICollection<TEntity> e CancellationToken) está adequada.

InvoiceReminder.Data/Repository/BaseRepository.cs (1)

42-45: Implementação de BulkRemoveAsync está correta.

O método delega apropriadamente para BulkDeleteAsync do EFCore.BulkExtensions, seguindo o padrão estabelecido no repositório.

InvoiceReminder.IntegrationTests/Data/Repository/BaseRepositoryIntegrationTests.cs (4)

280-293: Teste de coleção vazia agora determinístico!

Excelente correção! O teste agora garante um estado vazio determinístico usando BulkRemoveAsync (linhas 284-285) antes de validar que GetAll() retorna uma coleção vazia. Isso resolve completamente o problema apontado na revisão anterior.


47-582: Cobertura de testes abrangente e bem estruturada!

Os testes de integração estão muito bem organizados e cobrem todos os métodos do repositório de forma completa:

  • Testes de AddAsync, BulkInsertAsync, GetByIdAsync, GetAll com validações apropriadas
  • Testes de Remove e Update verificando tanto o estado do DbContext quanto a persistência no banco
  • Testes de Where com predicados simples e complexos
  • Testes de Dispose garantindo liberação adequada de recursos
  • Inclusão de testes de cancelamento (CancellationToken)

A estrutura Arrange-Act-Assert está clara e consistente em todos os testes.


23-34: TestInitialize configurado corretamente.

A inicialização dos recursos de teste está adequada, criando o DbContext, repositórios e UnitOfWork necessários. O uso de NSubstitute para mockar ILogger<UnitOfWork> é apropriado para testes de integração focados em persistência.


588-597: Helper method útil para isolamento de testes.

O método CreateFreshRepository<T>() é útil para criar repositórios com contextos frescos, ajudando a garantir que os testes leiam dados diretamente do banco e não de caches do DbContext.

Enhances test reliability by ensuring a clean state before each test execution.

Updates test data generation to provide more realistic values, especially for dates, improving the accuracy of tests related to temporal data.

Improves assertion clarity and correctness in entity configuration tests.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
InvoiceReminder.UnitTests.API/AuthenticationSetup/BearerSecuritySchemeTransformerTests.cs (1)

52-52: Considere usar ShouldContainKey para melhor idiomaticidade do Shouldly.

A asserção funciona corretamente, mas pode ser mais expressiva usando o método de extensão ShouldContainKey do Shouldly, que é mais idiomático para verificar chaves em dicionários.

🔎 Refatoração sugerida
-            _document.Components.SecuritySchemes.ContainsKey("Bearer").ShouldBe(true);
+            _document.Components.SecuritySchemes.ShouldContainKey("Bearer");
InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/UserConfigTests.cs (1)

51-56: Adicione asserção para o valor padrão do TelegramChatId.

A configuração UserConfig define .HasDefaultValue(0) para a propriedade TelegramChatId, mas o teste não verifica esse valor padrão. Adicione uma asserção para garantir cobertura completa da configuração.

🔎 Sugestão de asserção adicional
         telegramChatIdProperty.GetColumnName().ShouldBe("telegram_chat_id");
         telegramChatIdProperty.GetColumnType().ShouldBe("bigint");
         telegramChatIdProperty.IsNullable.ShouldBeFalse();
+        telegramChatIdProperty.GetDefaultValue().ShouldBe(0L);
InvoiceReminder.IntegrationTests/Data/Repository/BaseRepositoryIntegrationTests.cs (2)

116-127: Considere validação mais rigorosa dos timestamps.

O teste verifica apenas que CreatedAt e UpdatedAt não são valores padrão, mas não confirma que foram definidos com valores recentes. Para maior precisão, você poderia validar que os timestamps estão próximos de DateTime.UtcNow.

🔎 Sugestão de validação mais rigorosa
 [TestMethod]
 public async Task BulkInsertAsync_Should_Set_CreatedAt_And_UpdatedAt()
 {
     // Arrange
     var users = TestData.UserFaker().Generate(3);
+    var beforeInsert = DateTime.UtcNow.AddSeconds(-1);

     // Act
     _ = await _userRepository.BulkInsertAsync(users, TestContext.CancellationToken);
+    var afterInsert = DateTime.UtcNow.AddSeconds(1);

     // Assert
-    users.ShouldAllBe(u => u.CreatedAt != default && u.UpdatedAt != default);
+    users.ShouldAllBe(u => 
+        u.CreatedAt >= beforeInsert && u.CreatedAt <= afterInsert &&
+        u.UpdatedAt >= beforeInsert && u.UpdatedAt <= afterInsert);
 }

227-251: Teste funciona, mas a asserção poderia ser mais determinística.

Linha 249 usa ShouldBeGreaterThanOrEqualTo(3), o que significa que o teste passa mesmo se houver mais de 3 usuários no banco (de outros testes). Embora isso seja aceitável para testes de integração, considere limpar os dados antes do teste se precisar de maior precisão.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b950766 and 8aea040.

📒 Files selected for processing (8)
  • InvoiceReminder.IntegrationTests/Data/Repository/BaseRepositoryIntegrationTests.cs
  • InvoiceReminder.IntegrationTests/Data/Utils/TestData.cs
  • InvoiceReminder.UnitTests.API/AuthenticationSetup/BearerSecuritySchemeTransformerTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/EmailAuthTokenConfigTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/InvoiceConfigTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/JobScheduleConfigTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/ScanEmailDefinitionConfigTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/UserConfigTests.cs
🚧 Files skipped from review as they are similar to previous changes (4)
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/InvoiceConfigTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/EmailAuthTokenConfigTests.cs
  • InvoiceReminder.IntegrationTests/Data/Utils/TestData.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/ScanEmailDefinitionConfigTests.cs
🧰 Additional context used
🧬 Code graph analysis (2)
InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/JobScheduleConfigTests.cs (2)
InvoiceReminder.Data/Persistence/EntitiesConfig/JobScheduleConfig.cs (2)
  • JobScheduleConfig (10-44)
  • Configure (12-43)
InvoiceReminder.Domain/Entities/JobSchedule.cs (1)
  • JobSchedule (3-7)
InvoiceReminder.IntegrationTests/Data/Repository/BaseRepositoryIntegrationTests.cs (5)
InvoiceReminder.IntegrationTests/Data/ContainerSetup/DatabaseFixture.cs (2)
  • Task (44-51)
  • Task (53-62)
InvoiceReminder.Data/Repository/BaseRepository.cs (10)
  • BaseRepository (8-109)
  • BaseRepository (15-20)
  • Dispose (94-102)
  • Dispose (104-108)
  • Task (22-27)
  • Task (29-40)
  • Task (42-45)
  • Task (47-55)
  • Task (67-70)
  • Remove (57-65)
InvoiceReminder.Data/Interfaces/IBaseRepository.cs (6)
  • Task (7-7)
  • Task (8-8)
  • Task (9-9)
  • Task (10-10)
  • Task (12-12)
  • Remove (11-11)
InvoiceReminder.IntegrationTests/Data/Utils/TestData.cs (1)
  • TestData (7-86)
InvoiceReminder.Domain/Extensions/EntityExtensions.cs (1)
  • Guid (41-52)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (csharp)
🔇 Additional comments (15)
InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/JobScheduleConfigTests.cs (1)

1-72: Excelente implementação do teste de configuração!

O teste valida de forma abrangente toda a configuração da entidade JobSchedule, incluindo:

  • Mapeamento da tabela
  • Chave primária
  • Todas as propriedades com seus tipos de coluna, nomes, restrições de nulidade e comprimentos máximos
  • Geração de valor para a propriedade Id

Nota importante: A validação de MaxLength para CronExpression (linha 55) que estava faltando no comentário de revisão anterior foi corretamente implementada, garantindo cobertura completa da configuração.

A estrutura do teste está clara e bem organizada, com comentários separando cada seção de validação, facilitando a manutenção.

InvoiceReminder.UnitTests.API/AuthenticationSetup/BearerSecuritySchemeTransformerTests.cs (1)

8-8: Reorganização do namespace aprovada.

A mudança do namespace melhora a clareza e organização da estrutura de testes. O atributo InternalsVisibleTo em BearerSecuritySchemeTransformer.cs foi corretamente atualizado para InvoiceReminder.UnitTests.API, permitindo acesso adequado aos membros internos pelo projeto de testes.

InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/UserConfigTests.cs (3)

1-21: Estrutura do teste está correta!

A configuração do teste está bem organizada. O uso de um ConventionSet vazio é uma prática adequada para testes unitários de configurações EF Core, pois isola as configurações explícitas sem interferência das convenções padrão.


23-49: Validações de tabela, chave primária e índice estão completas!

As asserções para nome da tabela, chave primária, índice único no Email e propriedade Id estão corretas e abrangentes.


58-92: Validações das propriedades restantes estão corretas!

As asserções para Name, Email, Password, CreatedAt e UpdatedAt validam corretamente os nomes de colunas, tipos, comprimentos máximos e nulabilidade conforme a configuração. Os comentários em português também facilitam a leitura do teste.

InvoiceReminder.IntegrationTests/Data/Repository/BaseRepositoryIntegrationTests.cs (10)

23-43: Configuração e limpeza de recursos estão corretas.

A inicialização cria os recursos necessários (DbContext, repositórios, UnitOfWork) e o TestCleanup garante a liberação adequada de todos os recursos na ordem correta.


47-97: Testes de AddAsync cobrem os cenários essenciais.

Os testes verificam corretamente o estado da entidade, a persistência após SaveChanges e o valor de retorno. A criação de um contexto novo para verificar a persistência é uma boa prática.


176-221: Testes de GetByIdAsync estão completos e corretos.

Boa cobertura incluindo caso de sucesso, ID inexistente e tratamento de cancelamento.


280-293: Teste agora valida corretamente o cenário de coleção vazia!

O teste foi significativamente melhorado em relação à versão anterior:

  • Remove explicitamente todas as invoices antes do teste (linha 285)
  • Valida corretamente que o resultado está vazio usando ShouldBeEmpty() (linha 292)
  • Garante uma condição determinística em vez de assumir que o banco está vazio

Isso resolve completamente a preocupação levantada na revisão anterior.


299-354: Testes de Remove cobrem todos os cenários importantes.

Boa cobertura incluindo marcação de estado, persistência de deleção e tratamento de entidades desanexadas.


360-439: Testes de Update estão completos e corretos.

Excelente cobertura incluindo marcação de estado, persistência de alterações, tratamento de entidades desanexadas e verificação do valor de retorno.


445-514: Testes de Where cobrem bem os cenários de filtragem.

Linha 476 mostra uma boa prática ao limpar todos os usuários antes do teste para garantir resultados determinísticos. Boa cobertura de predicados simples, múltiplas correspondências, e resultados vazios.


516-539: Teste de predicados complexos está correto.

O teste verifica adequadamente que o método Where suporta predicados compostos com múltiplas condições (&&).


545-584: Testes de Dispose verificam corretamente o gerenciamento de recursos.

Os testes cobrem tanto a liberação de recursos (verificando que ObjectDisposedException é lançada após dispose) quanto a segurança de múltiplas chamadas a Dispose.


590-599: Método auxiliar está bem implementado.

O helper CreateFreshRepository<T> encapsula corretamente a lógica de criação de um novo contexto e repositório, permitindo flexibilidade com o parâmetro genérico.

Refactors tests to use more reliable and accurate assertions.
This change addresses potential issues with date comparisons in integration tests by asserting that timestamps fall within an acceptable range, preventing failures due to slight timing differences.
Additionally, it strengthens assertions in other tests by using more specific checks (e.g., ShouldContainKey instead of ContainsKey) and adjusting expected counts to be more flexible.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/UserConfigTests.cs (1)

59-78: Considere verificar os tipos de coluna para propriedades string.

As asserções para Name, Email e Password verificam o nome da coluna, comprimento máximo e nulabilidade, mas não verificam o tipo de coluna no banco de dados.

Como UserConfig não define explicitamente o tipo de coluna para estas propriedades (apenas o comprimento máximo), o tipo real depende do provedor do banco de dados. Se a intenção é que estas colunas sejam mapeadas para um tipo específico (como varchar(255) ou text), considere adicionar asserções para GetColumnType() similar ao que é feito para outras propriedades.

🔎 Exemplo de asserção adicional
 // Verifica propriedade Name
 var nameProperty = entityType.FindProperty(nameof(User.Name));
 _ = nameProperty.ShouldNotBeNull();
 nameProperty.GetColumnName().ShouldBe("name");
+nameProperty.GetColumnType().ShouldNotBeNullOrWhiteSpace(); // ou .ShouldBe("character varying(255)") se quiser ser específico
 nameProperty.GetMaxLength().ShouldBe(255);
 nameProperty.IsNullable.ShouldBeFalse();
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8aea040 and 551a3d3.

📒 Files selected for processing (3)
  • InvoiceReminder.IntegrationTests/Data/Repository/BaseRepositoryIntegrationTests.cs
  • InvoiceReminder.UnitTests.API/AuthenticationSetup/BearerSecuritySchemeTransformerTests.cs
  • InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/UserConfigTests.cs
🚧 Files skipped from review as they are similar to previous changes (2)
  • InvoiceReminder.UnitTests.API/AuthenticationSetup/BearerSecuritySchemeTransformerTests.cs
  • InvoiceReminder.IntegrationTests/Data/Repository/BaseRepositoryIntegrationTests.cs
🧰 Additional context used
🧬 Code graph analysis (1)
InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/UserConfigTests.cs (1)
InvoiceReminder.Data/Persistence/EntitiesConfig/UserConfig.cs (2)
  • UserConfig (10-59)
  • Configure (12-58)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: Analyze (csharp)
🔇 Additional comments (2)
InvoiceReminder.UnitTests.Infrastructure/Data/EntitiesConfig/UserConfigTests.cs (2)

23-41: Excelente cobertura de tabela, chave primária e índices!

As asserções validam corretamente:

  • Nome da tabela
  • Existência e estrutura da chave primária
  • Configuração do índice único no Email com nome do banco de dados

A abordagem de verificar o índice usando FirstOrDefault com predicado é robusta e evita dependências na ordem dos índices.


80-92: Boa validação das propriedades herdadas!

As asserções para CreatedAt e UpdatedAt verificam corretamente:

  • Nomes das colunas mapeadas
  • Tipo específico do PostgreSQL (timestamp with time zone)
  • Nulabilidade

Os comentários indicando que estas propriedades são herdadas de EntityDefaults adicionam contexto útil para futuros mantenedores.

@jldsilva jldsilva merged commit 93f240b into development Dec 31, 2025
5 checks passed
@jldsilva jldsilva deleted the Refactoring-Tests branch January 3, 2026 00:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request .NET Pull requests that update .NET code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant