Skip to content

Sprint 10 - Ratings Module, Instagram OIDC, Gating Diff#197

Merged
frigini merged 32 commits intomasterfrom
feature/sprint-10-quality-onboarding
Apr 14, 2026
Merged

Sprint 10 - Ratings Module, Instagram OIDC, Gating Diff#197
frigini merged 32 commits intomasterfrom
feature/sprint-10-quality-onboarding

Conversation

@frigini
Copy link
Copy Markdown
Owner

@frigini frigini commented Apr 13, 2026

Summary by CodeRabbit

  • Novos Recursos

    • Sistema de avaliações: enviar avaliações, consultar por prestador, checar status; moderação/flagging automático; atualização das métricas do prestador após aprovação.
    • Autenticação: login via Instagram e endpoint público para listar provedores de autenticação.
    • Novas APIs: registro de clientes, endpoints administrativos para templates e logs de comunicações, e operações de documentos.
  • Documentação

    • Roadmap consolidado em formato de status atual (arquivos anteriores removidos/reorganizados).
  • Tests

    • Ampliação de testes unitários, de integração e E2E cobrindo Ratings, repositórios, moderação e fluxo de eventos.

- Merged roadmap-current and roadmap-future into a single roadmap.md.
- Updated roadmap-history with Sprint 9 conclusion details.
- Defined detailed scope for Sprint 10 (Ratings Module, Instagram Login, CI Gating).
- Organized next steps for Sprint 11 (Payments Module, i18n).
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 13, 2026

Warning

Rate limit exceeded

@frigini has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 24 minutes and 47 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 24 minutes and 47 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 491f8d92-f189-42e7-a61d-c33389ff5791

📥 Commits

Reviewing files that changed from the base of the PR and between 02b0460 and a3378f1.

📒 Files selected for processing (3)
  • docs/roadmap.md
  • src/Modules/Communications/Application/Services/CommunicationsOutboxWorker.cs
  • src/Modules/Communications/Tests/Unit/Application/ModuleApi/CommunicationsModuleApiTests.cs

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adiciona o módulo Ratings (Domain/Application/Infrastructure/API/tests), endpoints de reviews, integração de eventos (ReviewApprovedIntegrationEvent) com consumidor em SearchProviders, CI para detecção de breaking OpenAPI, registro do provedor Instagram no Keycloak e reorganização/remoção de roadmaps e ajustes na infra de testes/E2E para propagação síncrona de eventos.

Changes

Cohort / File(s) Summary
Roadmaps & Docs
docs/roadmap-current.md, docs/roadmap-future.md, docs/roadmap.md
Remoção de roadmap-current.md e roadmap-future.md; docs/roadmap.md reescrito com seção “Status Atual (Abril 2026)”, Sprints 10/11 e Roadmaps futuros.
Ratings — Domain
src/Modules/Ratings/Domain/*, src/Modules/Ratings/Domain/MeAjudaAi.Modules.Ratings.Domain.csproj
Novo agregado Review, ReviewId, EReviewStatus enum, eventos (Created/Approved/Rejected), IReviewRepository, DuplicateReviewException.
Ratings — Application
src/Modules/Ratings/Application/*, src/Modules/Ratings/Application/MeAjudaAi.Modules.Ratings.Application.csproj
CreateReviewCommand, CreateReviewCommandHandler (dup check, moderação), IContentModerator/ContentModerator, DI AddApplication().
Ratings — Infrastructure
src/Modules/Ratings/Infrastructure/*, src/Modules/Ratings/Infrastructure/MeAjudaAi.Modules.Ratings.Infrastructure.csproj
RatingsDbContext, EF configs (ReviewConfiguration), ReviewRepository (tratamento 23505 → DuplicateReviewException), handlers de evento (ReviewApproved/Rejected), DI AddInfrastructure().
Ratings — API & Clients
src/Modules/Ratings/API/*, src/Modules/Ratings/API/API.Client/*, src/Bootstrapper/.../Program.cs, MeAjudaAi.slnx
Endpoints (POST /api/v1/ratings, GET /{id}, GET /provider/{providerId}), .bru clients (create/get/status), AddRatingsModule/UseRatingsModule, solução/bootstrapper atualizados.
Integration & Messaging
src/Shared/Messaging/Messages/Ratings/ReviewApprovedIntegrationEvent.cs, src/Modules/Ratings/Infrastructure/Events/Handlers/*, src/Modules/SearchProviders/Infrastructure/...
Novo ReviewApprovedIntegrationEvent, publicação pelo handler de domínio e consumidor em SearchProviders que atualiza métricas do provedor.
CI / OpenAPI
.github/workflows/ci-backend.yml
Expande filtros de paths para src/Contracts/**, adiciona testes do módulo Ratings, renomeia passo de coverage comment e cria job openapi-breaking-change-check que compara specs e falha em breaking changes.
Keycloak & Auth
infrastructure/keycloak/realms/meajudaai-realm.dev.json, src/Contracts/Identity/Enums/EAuthProvider.cs, src/Modules/Users/API/API.Client/Public/GetAuthProviders.bru
Adiciona provedor instagram ao realm, parametriza meajudaai-api-service secret e adiciona enum Instagram + .bru para listar provedores.
Test infra / E2E
tests/MeAjudaAi.E2E.Tests/*, tests/MeAjudaAi.E2E.Tests/Base/*, tests/MeAjudaAi.E2E.Tests/Modules/Ratings/*
Toggle EnableEventsAndMessageBus, SynchronousInMemoryMessageBus, mocks, migration/schema helpers, E2E tests que habilitam processamento síncrono para propagar eventos e validar fluxo completo.
Tests — Ratings
src/Modules/Ratings/Tests/*
Novos testes unitários e de integração (SQLite) cobrindo criação, domínio, repository, content moderation e handlers.
SearchProviders infra
src/Modules/SearchProviders/Infrastructure/Extensions.cs, src/Modules/SearchProviders/Infrastructure/Events/Handlers/ReviewApprovedIntegrationEventHandler.cs
Refatoração de persistência, alteração de resolução de connection string, habilita retry EF Core, remove Dapper reg; adiciona handler para ReviewApprovedIntegrationEvent.
Shared & Misc
src/Shared/Extensions/ServiceCollectionExtensions.cs, src/Shared/Exceptions/*, src/Shared/Utilities/ClaimHelpers.cs
Registra services.AddDapper(), ajusta mensagem de erro genérico, adiciona utilitário ClaimHelpers para extrair user id.
API client defs (outros módulos)
src/Modules/Communications/API/API.Client/*, src/Modules/Documents/API/API.Client/*, src/Modules/Users/API/API.Client/*
Novas .bru definitions: Communications (logs/templates), Documents (status/request-verification), Users (register, auth-providers).
Frontend / Tests
src/Web/MeAjudaAi.Web.Admin/hooks/admin/use-services.ts, src/Web/MeAjudaAi.Web.Admin/__tests__/hooks/admin/use-services.test.ts
useServices agora aceita activeOnly e inclui no query; testes ajustados.
Build / Solution
MeAjudaAi.slnx, src/Bootstrapper/MeAjudaAi.ApiService/MeAjudaAi.ApiService.csproj
Adiciona projetos do módulo Ratings à solução; ApiService referencia o projeto Ratings.API e faz Add/Use do módulo em Program.cs.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client as Cliente/API
    participant RatingsAPI as Ratings API
    participant CmdHandler as CreateReviewCommandHandler
    participant Repo as ReviewRepository
    participant DB as Database
    participant DomainEvtProc as DomainEventProcessor
    participant RatingsInfraHandler as ReviewApprovedDomainEventHandler
    participant MsgBus as MessageBus
    participant SearchHandler as SearchProviders Handler

    Client->>RatingsAPI: POST /api/v1/ratings (providerId,rating,comment)
    RatingsAPI->>CmdHandler: Handle(CreateReviewCommand)
    CmdHandler->>Repo: GetByProviderAndCustomerAsync (dup check)
    Repo->>DB: SELECT ...
    DB-->>Repo: result
    CmdHandler->>CmdHandler: aplicar regras de moderação
    CmdHandler->>Repo: AddAsync(review)
    Repo->>DB: INSERT review
    DB-->>Repo: OK
    CmdHandler->>DomainEvtProc: publicar eventos de domínio
    DomainEvtProc->>RatingsInfraHandler: Dispatch ReviewApprovedDomainEvent
    RatingsInfraHandler->>Repo: GetAverageRatingForProviderAsync
    Repo->>DB: AVG/COUNT query
    DB-->>Repo: (avg,total)
    RatingsInfraHandler->>MsgBus: Publish(ReviewApprovedIntegrationEvent)
    MsgBus->>SearchHandler: Deliver integration event
    SearchHandler->>DB: UPDATE provider metrics
    DB-->>SearchHandler: OK
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutos

Possibly related PRs

Poem

🐰
Pulei linhas, plantei um review no jardim,
Médias se espalham, eventos cruzam o caminho,
Instagram bateu à porta do realm, bem-vindo!
Testes dançam, o coelho observa contente,
Código florido — vamos lançar sorrindo. ✨

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/sprint-10-quality-onboarding

@frigini frigini changed the title chore(docs): consolidate roadmaps and define Sprint 10 scope Sprint 10 - Ratings Module, Instagram OIDC, Gating Diff Apr 13, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 11

🧹 Nitpick comments (7)
src/Modules/Ratings/Application/Services/ContentModerator.cs (1)

18-19: Evite Contains na moderação para reduzir falso positivo/negativo.

Em Line 19, o match por substring é frágil. Prefira correspondência por palavra com Regex.Escape e IgnoreCase.

♻️ Sugestão de ajuste
-        var lowerContent = content.ToLower();
-        return !BadWords.Any(word => lowerContent.Contains(word));
+        return !BadWords.Any(word =>
+            Regex.IsMatch(
+                content,
+                $@"\b{Regex.Escape(word)}\b",
+                RegexOptions.IgnoreCase | RegexOptions.CultureInvariant));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Ratings/Application/Services/ContentModerator.cs` around lines 18
- 19, O uso de substring via lowerContent.Contains está causando falsos
positivos/negativos; dentro da classe/método ContentModerator substitua a
verificação atual que usa lowerContent/Contains por uma checagem por palavra
inteira usando Regex: para cada palavra em BadWords construa um pattern com
Regex.Escape(word) envolto em \b (word boundaries) e faça Regex.IsMatch(content,
pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) (ou compile os
padrões uma vez em um cache para desempenho), assegurando que a correspondência
seja por palavra inteira e case-insensitive em vez de Contains.
src/Modules/Ratings/Infrastructure/Extensions.cs (1)

14-17: Adicione validação fail-fast da DefaultConnection.

Hoje a conexão pode chegar nula/vazia ao UseNpgsql, gerando falha tardia e menos diagnóstica no startup.

💡 Sugestão de ajuste
     public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
     {
         var connectionString = configuration.GetConnectionString("DefaultConnection");
+        if (string.IsNullOrWhiteSpace(connectionString))
+            throw new InvalidOperationException("Connection string 'DefaultConnection' is not configured.");

         services.AddDbContext<RatingsDbContext>(options =>
             options.UseNpgsql(connectionString, m => m.MigrationsHistoryTable("__EFMigrationsHistory", "ratings")));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Ratings/Infrastructure/Extensions.cs` around lines 14 - 17, A
conexão "DefaultConnection" pode ser nula ou vazia causando falha tardia em
UseNpgsql; antes de chamar services.AddDbContext<RatingsDbContext>(...) valide o
resultado de configuration.GetConnectionString("DefaultConnection") e faça um
fail-fast (lançando exceção ou registrando erro e encerrando) quando for
null/empty para superfície imediata do problema; ajuste o bloco que usa
configuration.GetConnectionString("DefaultConnection"),
AddDbContext<RatingsDbContext> e UseNpgsql para checar e tratar a string de
conexão antes de passar para UseNpgsql.
src/Modules/Ratings/Infrastructure/Events/Handlers/ReviewApprovedDomainEventHandler.cs (1)

24-33: Considere renomear o evento de integração para melhor clareza.

O evento ReviewCreatedIntegrationEvent é publicado quando uma avaliação é aprovada, não quando é criada. A nomenclatura atual pode causar confusão, especialmente porque ReviewCreatedDomainEvent já existe e é disparado na criação.

Uma nomenclatura mais precisa seria ReviewApprovedIntegrationEvent, que refletiria melhor o momento em que o evento é disparado.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Ratings/Infrastructure/Events/Handlers/ReviewApprovedDomainEventHandler.cs`
around lines 24 - 33, O evento de integração instanciado em
ReviewApprovedDomainEventHandler está usando ReviewCreatedIntegrationEvent, mas
deve refletir que a avaliação foi aprovada; renomeie a classe/arquivo e todas as
referências de ReviewCreatedIntegrationEvent para ReviewApprovedIntegrationEvent
e atualize a instância no handler (onde é criado o objeto com
Source/ProviderId/ReviewId etc.) e quaisquer outros pontos que publiquem ou
consumam esse evento (event publishers, consumers, DTOs/mappers e testes) para
garantir consistência de nomes.
src/Modules/Ratings/Tests/Unit/Domain/Entities/ReviewTests.cs (1)

62-75: Considere verificar a emissão do evento de domínio no teste de Reject.

O teste Approve_WhenPending_ShouldChangeStatusToApprovedAndAddDomainEvent verifica corretamente a emissão do ReviewApprovedDomainEvent, mas o teste Reject_WithReason_ShouldChangeStatusToRejected não verifica se um evento de domínio correspondente é emitido (se existir um ReviewRejectedDomainEvent).

Para manter consistência entre os testes, considere adicionar a verificação do evento ou documentar por que não é necessário.

🧪 Sugestão de melhoria
     // Assert
     review.Status.Should().Be(EReviewStatus.Rejected);
     review.RejectionReason.Should().Be(reason);
+    // Se existir ReviewRejectedDomainEvent:
+    // review.DomainEvents.Should().Contain(e => e is ReviewRejectedDomainEvent);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Ratings/Tests/Unit/Domain/Entities/ReviewTests.cs` around lines
62 - 75, Atualize o teste Reject_WithReason_ShouldChangeStatusToRejected para
também verificar a emissão do evento de domínio: após chamar
Review.Reject(reason) assert que a coleção de eventos de domínio (por exemplo
Review.DomainEvents ou equivalente usado em
Approve_WhenPending_ShouldChangeStatusToApprovedAndAddDomainEvent) contém um
ReviewRejectedDomainEvent com o ReviewId correto e que o payload/Reason
corresponde à variável reason; mantenha as verificações existentes de
Review.Status e Review.RejectionReason.
src/Modules/Ratings/Tests/Unit/Application/Handlers/CreateReviewCommandHandlerTests.cs (1)

31-58: Considere adicionar testes para casos de borda da lógica de auto-aprovação.

Os testes atuais cobrem cenários importantes, mas a lógica de auto-aprovação no handler (Rating >= 4 && string.IsNullOrWhiteSpace(Comment)) tem alguns casos de borda não testados:

  1. Rating 4 sem comentário (limite inferior) - deve auto-aprovar
  2. Rating 5 com comentário limpo - NÃO deve auto-aprovar (comentário presente)
  3. Rating 3 sem comentário - NÃO deve auto-aprovar (rating < 4)
🧪 Sugestão de testes adicionais
[Fact]
public async Task HandleAsync_Rating4WithoutComment_ShouldAutoApprove()
{
    // Arrange
    var command = new CreateReviewCommand(Guid.NewGuid(), Guid.NewGuid(), 4, null);

    // Act
    await _handler.HandleAsync(command);

    // Assert
    _repositoryMock.Verify(r => r.AddAsync(
        It.Is<Review>(rev => rev.Status == EReviewStatus.Approved), 
        It.IsAny<CancellationToken>()), Times.Once);
}

[Fact]
public async Task HandleAsync_Rating5WithCleanComment_ShouldNotAutoApprove()
{
    // Arrange
    var command = new CreateReviewCommand(Guid.NewGuid(), Guid.NewGuid(), 5, "Great service!");

    // Act
    await _handler.HandleAsync(command);

    // Assert
    _repositoryMock.Verify(r => r.AddAsync(
        It.Is<Review>(rev => rev.Status == EReviewStatus.Pending), 
        It.IsAny<CancellationToken>()), Times.Once);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Ratings/Tests/Unit/Application/Handlers/CreateReviewCommandHandlerTests.cs`
around lines 31 - 58, Add three unit tests covering auto-approval edge cases for
the CreateReviewCommand handler: create tests named
HandleAsync_Rating4WithoutComment_ShouldAutoApprove,
HandleAsync_Rating5WithCleanComment_ShouldNotAutoApprove, and
HandleAsync_Rating3WithoutComment_ShouldNotAutoApprove; each should call
_handler.HandleAsync with a CreateReviewCommand (rating 4/5/3 and comment null
or non-empty as appropriate) and verify via _repositoryMock.Verify that the
added Review (It.Is<Review>(rev => rev.Status == EReviewStatus.Approved or
Pending)) matches the expected status and that AddAsync was called once with
Any<CancellationToken>().
src/Modules/Ratings/Application/Handlers/CreateReviewCommandHandler.cs (2)

28-37: Considere implementar o status Flagged mencionado no comentário.

O comentário indica que avaliações com conteúdo inapropriado poderiam ser marcadas como Flagged em vez de Pending. O enum EReviewStatus já inclui o valor Flagged (conforme o código em EReviewStatus.cs).

Implementar isso agora melhoraria a distinção entre avaliações aguardando aprovação normal vs. aquelas sinalizadas por conteúdo suspeito.

Deseja que eu gere a implementação para marcar reviews com conteúdo inapropriado como Flagged?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Ratings/Application/Handlers/CreateReviewCommandHandler.cs`
around lines 28 - 37, When isClean is false, set the review's status to the
Flagged enum instead of leaving it Pending: update the branch that currently
only logs (logger.LogWarning(...)) to call the domain operation that marks a
review as flagged (prefer calling a domain method like review.MarkAsFlagged() or
review.SetStatus(EReviewStatus.Flagged); if those methods don't exist, assign
review.Status = EReviewStatus.Flagged and add a small domain method to
encapsulate that change). Keep the warning log but change its text/context if
needed and ensure any invariants/notifications triggered by review.Approve()
have equivalents for the Flagged state.

15-43: Considere adicionar validação para avaliações duplicadas.

O handler não verifica se o cliente já avaliou este provedor. Atualmente, IReviewRepository não possui método para consultar reviews existentes por cliente e provedor, e não há constraint de unicidade na base de dados. Dependendo das regras de negócio, pode ser necessário:

  • Adicionar método GetByProviderAndCustomerAsync ao repositório
  • Implementar validação no handler para impedir múltiplas avaliações do mesmo cliente para o mesmo provedor
  • Considerar adicionar constraint de unicidade composta (ProviderId, CustomerId) no banco de dados
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Ratings/Application/Handlers/CreateReviewCommandHandler.cs`
around lines 15 - 43, Add a duplicate-review check in
CreateReviewCommandHandler.HandleAsync by querying the repository for an
existing review for the same ProviderId and CustomerId before creating a new
Review: add a new method signature GetByProviderAndCustomerAsync(Guid
providerId, Guid customerId, CancellationToken) to IReviewRepository and
implement it in the concrete repository, call
repository.GetByProviderAndCustomerAsync(...) at the start of HandleAsync and
return/throw a domain/validation error if a review already exists instead of
calling repository.AddAsync(review,...); also add a DB-level unique constraint
on (ProviderId, CustomerId) to enforce uniqueness at persistence layer.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/ci-backend.yml:
- Around line 326-329: The current step "Checkout Base Branch Spec" uses the
command 'git show origin/${{ github.base_ref }}:api/api-spec.json >
api-base.json || echo "{}" > api-base.json', which writes a "{}" fallback and
can hide real breaking changes; replace this behavior so the job either produces
a real base spec or fails explicitly: remove the 'echo "{}"' fallback and
instead invoke the script/command that generates the base branch spec (or exit
non‑zero with a clear error) when git show fails, ensuring 'api-base.json' is a
valid spec or the workflow fails; update any step that relies on 'api-base.json'
accordingly.

In `@docs/roadmap.md`:
- Around line 7-18: The sprint status labels in docs/roadmap.md are
inconsistent: the header "## 📊 Status Atual (Abril 2026)" and Sprint 11 are
marked as current, but the "## 🚀 Sprint 10 - Qualidade & Onboarding (12 Abr -
26 Abr 2026) [CONCLUÍDA]" line still shows "[CONCLUÍDA]"; update the Sprint 10
line (the heading text "## 🚀 Sprint 10 - Qualidade & Onboarding (12 Abr - 26
Abr 2026) [CONCLUÍDA]") to reflect the correct state (remove or change the
"[CONCLUÍDA]" tag to "[EM ANDAMENTO]" or correct dates), or adjust the "## 📊
Status Atual (Abril 2026)" block so Sprint 11 is not marked current until Sprint
10 is closed—ensure the two headings and their status tags are consistent.

In `@infrastructure/keycloak/realms/meajudaai-realm.dev.json`:
- Line 299: A entrada "secret": "meajudaai-api-secret-dev-2024" está versionando
um segredo em texto puro; substitua esse valor por um placeholder que leia da
variável de ambiente (como os outros clientSecret no arquivo), por exemplo
referenciando uma chave de configuração/ENV (p.ex. CLIENT_SECRET_MEAJUDAAI_API)
e documente no README/infra como injetar essa variável em dev; altere o campo
"secret" correspondente ao client que atualmente contém
"meajudaai-api-secret-dev-2024" para usar o placeholder/env reference para
permitir rotação segura.
- Around line 127-149: The Keycloak IdP "instagram" is added but the API
endpoint GetAuthProviders relies on Enum.GetNames<EAuthProvider>() which lacks
an Instagram member; add a corresponding value (e.g., Instagram or instagram to
match the Keycloak alias) to the shared EAuthProvider enum in Shared.Contracts,
update any mappings/serializers if casing must match the IdP alias,
rebuild/consume the updated Shared.Contracts in the API so GetAuthProviders
exposes Instagram, and verify the client displays the provider.
- Around line 127-143: The realm JSON config is using the generic OIDC provider
("providerId": "oidc") while configuring Instagram OAuth endpoints (e.g.,
"authorizationUrl", "tokenUrl", "userInfoUrl") which is incompatible because
Instagram is OAuth2-only and provides no JWKS or ID tokens; change the provider
to Keycloak's dedicated Instagram provider by setting "providerId" to
"instagram" (use InstagramIdentityProvider), remove or ignore OIDC-only fields
like "jwksUrl" and OIDC issuer, and ensure "clientId"/"clientSecret" remain
mapped to ${INSTAGRAM_CLIENT_ID}/${INSTAGRAM_CLIENT_SECRET} so the Instagram
OAuth flow (authorizationUrl/tokenUrl/userInfoUrl) works correctly.

In `@src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs`:
- Around line 24-27: O endpoint registrado via
MapGet("/provider/{providerId:guid}", GetProviderReviewsAsync) está marcado
AllowAnonymous() mas retorna o campo CustomerId (expondo identificadores de
usuário); fix: remove CustomerId from the public response model returned by
GetProviderReviewsAsync (or replace it with a non-identifying field like
CustomerDisplayName/anonymous flag) or alternatively require authentication on
that route (remove AllowAnonymous() and enforce authorization) and ensure any
returned CustomerId is only included for authorized callers; update the
DTO/anonymous projection used by GetProviderReviewsAsync to not include
CustomerId and adjust related serialization/tests accordingly.
- Around line 19-21: The endpoint mapping for CreateReviewAsync uses a named
route "CreateReview" via group.MapPost("/", CreateReviewAsync) which has no {id}
route parameter, so the call to CreatedAtRoute("CreateReview", new { id =
reviewId }, ...) will throw at runtime; replace the CreatedAtRoute usage with
Results.Created(...) returning the resource URI (or omit location) and the
created payload, i.e., update the response creation in CreateReviewAsync to use
Results.Created with an appropriate URI (or Results.Created(string.Empty, ...))
instead of CreatedAtRoute to match the POST route definition.

In `@src/Modules/Ratings/API/Extensions.cs`:
- Line 21: O comentário "// Domain Event Handlers" em
src/Modules/Ratings/API/Extensions.cs deve ser padronizado para português;
substitua esse comentário por uma versão em português como "Manipuladores de
eventos de domínio" (ou "Manipuladores de eventos do domínio") para manter a
convenção do projeto e alinhar com outros comentários/XML no código.

In `@src/Modules/Ratings/Domain/Entities/Review.cs`:
- Around line 84-87: O comentário inline em inglês dentro da chamada
AddDomainEvent(new ReviewApprovedDomainEvent(...)) — especificamente o texto
"Versioning is simple for now" associado ao argumento de versão — deve ser
padronizado para português; localize o trecho em Review.cs onde
ReviewApprovedDomainEvent é instanciado (referência: AddDomainEvent e
ReviewApprovedDomainEvent) e substitua o comentário por uma versão em português
concisa (ex.: "Versionamento simples por enquanto") mantendo o conteúdo sem
alterar a lógica ou valores.
- Around line 77-103: The Approve() method can leave a stale RejectionReason
when transitioning from Rejected to Approved; update Approve() (the method that
sets Status to EReviewStatus.Approved, calls MarkAsUpdated(), and raises
ReviewApprovedDomainEvent) to clear RejectionReason (e.g., set RejectionReason =
null or empty) before calling MarkAsUpdated() and raising the event so the
entity never ends up Approved with a lingering rejection reason.

In
`@src/Modules/SearchProviders/Infrastructure/Events/Handlers/ReviewCreatedIntegrationEventHandler.cs`:
- Around line 40-45: The catch block in ReviewCreatedIntegrationEventHandler
(the catch(Exception ex) after handling the ReviewCreatedIntegrationEvent)
currently swallows failures; update it so failures are surfaced to the message
broker instead of being silenced: after calling logger.LogError(ex, "Error
handling ReviewCreatedIntegrationEvent for provider {ProviderId}",
integrationEvent.ProviderId) rethrow the exception (or throw a specific
transient/processing exception) so the broker can perform retries;
alternatively, if you choose DLQ/compensation, replace the swallow behavior with
logic to publish a failure/compensation event or move the message to a DLQ via
your messaging client, ensuring the code references
ReviewCreatedIntegrationEventHandler and the same integrationEvent.ProviderId
context when publishing/logging.

---

Nitpick comments:
In `@src/Modules/Ratings/Application/Handlers/CreateReviewCommandHandler.cs`:
- Around line 28-37: When isClean is false, set the review's status to the
Flagged enum instead of leaving it Pending: update the branch that currently
only logs (logger.LogWarning(...)) to call the domain operation that marks a
review as flagged (prefer calling a domain method like review.MarkAsFlagged() or
review.SetStatus(EReviewStatus.Flagged); if those methods don't exist, assign
review.Status = EReviewStatus.Flagged and add a small domain method to
encapsulate that change). Keep the warning log but change its text/context if
needed and ensure any invariants/notifications triggered by review.Approve()
have equivalents for the Flagged state.
- Around line 15-43: Add a duplicate-review check in
CreateReviewCommandHandler.HandleAsync by querying the repository for an
existing review for the same ProviderId and CustomerId before creating a new
Review: add a new method signature GetByProviderAndCustomerAsync(Guid
providerId, Guid customerId, CancellationToken) to IReviewRepository and
implement it in the concrete repository, call
repository.GetByProviderAndCustomerAsync(...) at the start of HandleAsync and
return/throw a domain/validation error if a review already exists instead of
calling repository.AddAsync(review,...); also add a DB-level unique constraint
on (ProviderId, CustomerId) to enforce uniqueness at persistence layer.

In `@src/Modules/Ratings/Application/Services/ContentModerator.cs`:
- Around line 18-19: O uso de substring via lowerContent.Contains está causando
falsos positivos/negativos; dentro da classe/método ContentModerator substitua a
verificação atual que usa lowerContent/Contains por uma checagem por palavra
inteira usando Regex: para cada palavra em BadWords construa um pattern com
Regex.Escape(word) envolto em \b (word boundaries) e faça Regex.IsMatch(content,
pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) (ou compile os
padrões uma vez em um cache para desempenho), assegurando que a correspondência
seja por palavra inteira e case-insensitive em vez de Contains.

In
`@src/Modules/Ratings/Infrastructure/Events/Handlers/ReviewApprovedDomainEventHandler.cs`:
- Around line 24-33: O evento de integração instanciado em
ReviewApprovedDomainEventHandler está usando ReviewCreatedIntegrationEvent, mas
deve refletir que a avaliação foi aprovada; renomeie a classe/arquivo e todas as
referências de ReviewCreatedIntegrationEvent para ReviewApprovedIntegrationEvent
e atualize a instância no handler (onde é criado o objeto com
Source/ProviderId/ReviewId etc.) e quaisquer outros pontos que publiquem ou
consumam esse evento (event publishers, consumers, DTOs/mappers e testes) para
garantir consistência de nomes.

In `@src/Modules/Ratings/Infrastructure/Extensions.cs`:
- Around line 14-17: A conexão "DefaultConnection" pode ser nula ou vazia
causando falha tardia em UseNpgsql; antes de chamar
services.AddDbContext<RatingsDbContext>(...) valide o resultado de
configuration.GetConnectionString("DefaultConnection") e faça um fail-fast
(lançando exceção ou registrando erro e encerrando) quando for null/empty para
superfície imediata do problema; ajuste o bloco que usa
configuration.GetConnectionString("DefaultConnection"),
AddDbContext<RatingsDbContext> e UseNpgsql para checar e tratar a string de
conexão antes de passar para UseNpgsql.

In
`@src/Modules/Ratings/Tests/Unit/Application/Handlers/CreateReviewCommandHandlerTests.cs`:
- Around line 31-58: Add three unit tests covering auto-approval edge cases for
the CreateReviewCommand handler: create tests named
HandleAsync_Rating4WithoutComment_ShouldAutoApprove,
HandleAsync_Rating5WithCleanComment_ShouldNotAutoApprove, and
HandleAsync_Rating3WithoutComment_ShouldNotAutoApprove; each should call
_handler.HandleAsync with a CreateReviewCommand (rating 4/5/3 and comment null
or non-empty as appropriate) and verify via _repositoryMock.Verify that the
added Review (It.Is<Review>(rev => rev.Status == EReviewStatus.Approved or
Pending)) matches the expected status and that AddAsync was called once with
Any<CancellationToken>().

In `@src/Modules/Ratings/Tests/Unit/Domain/Entities/ReviewTests.cs`:
- Around line 62-75: Atualize o teste
Reject_WithReason_ShouldChangeStatusToRejected para também verificar a emissão
do evento de domínio: após chamar Review.Reject(reason) assert que a coleção de
eventos de domínio (por exemplo Review.DomainEvents ou equivalente usado em
Approve_WhenPending_ShouldChangeStatusToApprovedAndAddDomainEvent) contém um
ReviewRejectedDomainEvent com o ReviewId correto e que o payload/Reason
corresponde à variável reason; mantenha as verificações existentes de
Review.Status e Review.RejectionReason.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3b960c82-e764-4974-a86b-4b17e439688e

📥 Commits

Reviewing files that changed from the base of the PR and between 658c244 and a65e79f.

⛔ Files ignored due to path filters (18)
  • src/Bootstrapper/MeAjudaAi.ApiService/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Communications/Tests/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Documents/Tests/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Locations/Tests/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Providers/Tests/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Ratings/API/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Ratings/Application/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Ratings/Domain/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Ratings/Infrastructure/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Ratings/Tests/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/SearchProviders/Tests/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/ServiceCatalogs/Tests/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Users/Tests/packages.lock.json is excluded by !**/packages.lock.json
  • tests/MeAjudaAi.ApiService.Tests/packages.lock.json is excluded by !**/packages.lock.json
  • tests/MeAjudaAi.Architecture.Tests/packages.lock.json is excluded by !**/packages.lock.json
  • tests/MeAjudaAi.E2E.Tests/packages.lock.json is excluded by !**/packages.lock.json
  • tests/MeAjudaAi.Integration.Tests/packages.lock.json is excluded by !**/packages.lock.json
  • tests/MeAjudaAi.Shared.Tests/packages.lock.json is excluded by !**/packages.lock.json
📒 Files selected for processing (41)
  • .github/workflows/ci-backend.yml
  • MeAjudaAi.slnx
  • docs/roadmap.md
  • infrastructure/keycloak/realms/meajudaai-realm.dev.json
  • src/Bootstrapper/MeAjudaAi.ApiService/MeAjudaAi.ApiService.csproj
  • src/Bootstrapper/MeAjudaAi.ApiService/Program.cs
  • src/Modules/Communications/API/API.Client/CommunicationAdmin/GetCommunicationLogs.bru
  • src/Modules/Communications/API/API.Client/CommunicationAdmin/GetEmailTemplates.bru
  • src/Modules/Documents/API/API.Client/DocumentAdmin/GetDocumentStatus.bru
  • src/Modules/Documents/API/API.Client/DocumentAdmin/RequestVerification.bru
  • src/Modules/Ratings/API/API.Client/Ratings/CreateReview.bru
  • src/Modules/Ratings/API/API.Client/Ratings/GetProviderReviews.bru
  • src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs
  • src/Modules/Ratings/API/Extensions.cs
  • src/Modules/Ratings/API/MeAjudaAi.Modules.Ratings.API.csproj
  • src/Modules/Ratings/Application/Commands/CreateReviewCommand.cs
  • src/Modules/Ratings/Application/Extensions.cs
  • src/Modules/Ratings/Application/Handlers/CreateReviewCommandHandler.cs
  • src/Modules/Ratings/Application/MeAjudaAi.Modules.Ratings.Application.csproj
  • src/Modules/Ratings/Application/Services/ContentModerator.cs
  • src/Modules/Ratings/Domain/Entities/Review.cs
  • src/Modules/Ratings/Domain/Enums/EReviewStatus.cs
  • src/Modules/Ratings/Domain/Events/ReviewApprovedDomainEvent.cs
  • src/Modules/Ratings/Domain/Events/ReviewCreatedDomainEvent.cs
  • src/Modules/Ratings/Domain/MeAjudaAi.Modules.Ratings.Domain.csproj
  • src/Modules/Ratings/Domain/Repositories/IReviewRepository.cs
  • src/Modules/Ratings/Domain/ValueObjects/ReviewId.cs
  • src/Modules/Ratings/Infrastructure/Events/Handlers/ReviewApprovedDomainEventHandler.cs
  • src/Modules/Ratings/Infrastructure/Extensions.cs
  • src/Modules/Ratings/Infrastructure/MeAjudaAi.Modules.Ratings.Infrastructure.csproj
  • src/Modules/Ratings/Infrastructure/Persistence/Configurations/ReviewConfiguration.cs
  • src/Modules/Ratings/Infrastructure/Persistence/RatingsDbContext.cs
  • src/Modules/Ratings/Infrastructure/Persistence/Repositories/ReviewRepository.cs
  • src/Modules/Ratings/Tests/MeAjudaAi.Modules.Ratings.Tests.csproj
  • src/Modules/Ratings/Tests/Unit/Application/Handlers/CreateReviewCommandHandlerTests.cs
  • src/Modules/Ratings/Tests/Unit/Domain/Entities/ReviewTests.cs
  • src/Modules/SearchProviders/Infrastructure/Events/Handlers/ReviewCreatedIntegrationEventHandler.cs
  • src/Modules/SearchProviders/Infrastructure/Extensions.cs
  • src/Modules/Users/API/API.Client/Public/GetAuthProviders.bru
  • src/Modules/Users/API/API.Client/Public/RegisterCustomer.bru
  • src/Shared/Messaging/Messages/Ratings/ReviewCreatedIntegrationEvent.cs
✅ Files skipped from review due to trivial changes (12)
  • src/Modules/Documents/API/API.Client/DocumentAdmin/RequestVerification.bru
  • src/Modules/Documents/API/API.Client/DocumentAdmin/GetDocumentStatus.bru
  • src/Modules/Users/API/API.Client/Public/RegisterCustomer.bru
  • src/Modules/Ratings/API/MeAjudaAi.Modules.Ratings.API.csproj
  • src/Modules/Ratings/Domain/Enums/EReviewStatus.cs
  • src/Modules/Communications/API/API.Client/CommunicationAdmin/GetEmailTemplates.bru
  • src/Modules/Ratings/Domain/Events/ReviewApprovedDomainEvent.cs
  • src/Modules/Users/API/API.Client/Public/GetAuthProviders.bru
  • src/Modules/Communications/API/API.Client/CommunicationAdmin/GetCommunicationLogs.bru
  • src/Modules/Ratings/API/API.Client/Ratings/GetProviderReviews.bru
  • src/Modules/Ratings/API/API.Client/Ratings/CreateReview.bru
  • src/Shared/Messaging/Messages/Ratings/ReviewCreatedIntegrationEvent.cs

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

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

⚠️ Outside diff range comments (1)
.github/workflows/ci-backend.yml (1)

168-179: ⚠️ Potential issue | 🟠 Major

Os testes do módulo Ratings não estão sendo executados no CI.

A matriz de módulos não inclui o projeto de testes de Ratings, então regressões desse PR podem passar sem validação automática.

🧪 Ajuste sugerido
           MODULES=(
             "src/Modules/Users/Tests/MeAjudaAi.Modules.Users.Tests.csproj"
             "src/Modules/Providers/Tests/MeAjudaAi.Modules.Providers.Tests.csproj"
             "src/Modules/Documents/Tests/MeAjudaAi.Modules.Documents.Tests.csproj"
             "src/Modules/ServiceCatalogs/Tests/MeAjudaAi.Modules.ServiceCatalogs.Tests.csproj"
             "src/Modules/Locations/Tests/MeAjudaAi.Modules.Locations.Tests.csproj"
             "src/Modules/Communications/Tests/MeAjudaAi.Modules.Communications.Tests.csproj"
+            "src/Modules/Ratings/Tests/MeAjudaAi.Modules.Ratings.Tests.csproj"
             "src/Modules/SearchProviders/Tests/MeAjudaAi.Modules.SearchProviders.Tests.csproj"
             "tests/MeAjudaAi.Shared.Tests/MeAjudaAi.Shared.Tests.csproj"
             "tests/MeAjudaAi.ApiService.Tests/MeAjudaAi.ApiService.Tests.csproj"
             "tests/MeAjudaAi.Architecture.Tests/MeAjudaAi.Architecture.Tests.csproj"
           )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci-backend.yml around lines 168 - 179, The MODULES array
in the CI workflow is missing the Ratings test project, so add the Ratings test
csproj to the MODULES list (e.g.,
"src/Modules/Ratings/Tests/MeAjudaAi.Modules.Ratings.Tests.csproj") inside the
MODULES=( ...) block in .github/workflows/ci-backend.yml so the CI runs the
Ratings tests along with the other modules.
🧹 Nitpick comments (3)
src/Modules/Ratings/Application/Services/ContentModerator.cs (1)

13-20: Evite recompilar regex a cada instância do serviço.

A lista de padrões pode ser estática para reduzir custo por request/escopo sem mudar comportamento.

♻️ Sugestão de ajuste
-    private readonly List<Regex> _patterns;
+    private static readonly Regex[] Patterns = BadWords
+        .Select(word => new Regex($@"\b{Regex.Escape(word)}\b", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled))
+        .ToArray();

     public ContentModerator()
     {
-        _patterns = BadWords
-            .Select(word => new Regex($@"\b{Regex.Escape(word)}\b", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled))
-            .ToList();
     }
@@
-        return !_patterns.Any(regex => regex.IsMatch(content));
+        return !Patterns.Any(regex => regex.IsMatch(content));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Ratings/Application/Services/ContentModerator.cs` around lines 13
- 20, The _patterns list is being compiled per ContentModerator instance which
is wasteful; make it a static readonly field and initialize it once (either
inline or in a static constructor) by transforming the current instance
initialization of _patterns that uses BadWords.Select(...).ToList() into a
static initializer so the Regex objects are compiled once for the lifetime of
the app; keep the same RegexOptions and reference BadWords in the static
context.
src/Modules/Ratings/Tests/Unit/Application/Handlers/CreateReviewCommandHandlerTests.cs (1)

45-73: Falta o caso de comentário só com espaços.

O handler usa string.IsNullOrWhiteSpace, então " " deveria seguir a mesma regra de autoaprovação de null. Cobrir esse branch evita regressão silenciosa.

💡 Teste sugerido
+    [Fact]
+    public async Task HandleAsync_Rating4WithWhitespaceComment_ShouldAutoApprove()
+    {
+        // Arrange
+        var command = new CreateReviewCommand(Guid.NewGuid(), Guid.NewGuid(), 4, "   ");
+
+        // Act
+        await _handler.HandleAsync(command);
+
+        // Assert
+        _repositoryMock.Verify(r => r.AddAsync(
+            It.Is<Review>(rev => rev.Status == EReviewStatus.Approved),
+            It.IsAny<CancellationToken>()), Times.Once);
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Ratings/Tests/Unit/Application/Handlers/CreateReviewCommandHandlerTests.cs`
around lines 45 - 73, Adicionar teste cobrindo comentário composto apenas por
espaços porque o handler usa string.IsNullOrWhiteSpace; crie um novo Fact
similar a HandleAsync_Rating5WithoutComment_ShouldAutoApprove (e opcionalmente
para Rating4) no CreateReviewCommandHandlerTests que constrói
CreateReviewCommand com comment = "   ", chama _handler.HandleAsync(command) e
verifica que _repositoryMock.AddAsync recebeu um Review com Status ==
EReviewStatus.Approved (It.IsAny<CancellationToken>()), garantindo que a branch
de whitespace é tratada como null.
src/Modules/Ratings/Application/Handlers/CreateReviewCommandHandler.cs (1)

33-45: Faça short-circuit de comentário vazio antes da moderação.

Hoje IsClean roda até para null/whitespace. Isso adiciona custo/latência desnecessários e acopla a regra de autoaprovação a uma implementação de moderador que pode não aceitar vazio.

💡 Ajuste sugerido
-        // Moderação Automática
-        var isClean = contentModerator.IsClean(command.Comment);
-
-        if (!isClean)
+        // Moderação Automática
+        var hasComment = !string.IsNullOrWhiteSpace(command.Comment);
+
+        if (!hasComment && command.Rating >= 4)
+        {
+            // Auto-aprovação para notas altas sem comentário
+            review.Approve();
+        }
+        else if (hasComment && !contentModerator.IsClean(command.Comment))
         {
             logger.LogWarning("Review {ReviewId} flagged for moderation due to inappropriate content", review.Id.Value);
             review.MarkAsFlagged();
         }
-        else if (command.Rating >= 4 && string.IsNullOrWhiteSpace(command.Comment))
-        {
-            // Auto-aprovação para notas altas sem comentário
-            review.Approve();
-        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Ratings/Application/Handlers/CreateReviewCommandHandler.cs`
around lines 33 - 45, Short-circuit the empty-comment case before calling
contentModerator.IsClean: first check if
string.IsNullOrWhiteSpace(command.Comment) and, if command.Rating >= 4, call
review.Approve(); otherwise, do not call contentModerator.IsClean for
empty/null/whitespace comments. Only call
contentModerator.IsClean(command.Comment) when the comment is non-empty and
then, if it returns false, call review.MarkAsFlagged() and
logger.LogWarning(...). This removes unnecessary calls to
contentModerator.IsClean and separates the auto-approval rule from the moderator
implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs`:
- Around line 49-51: A resposta usa
Results.Created($"/api/v1/ratings/{reviewId}", reviewId) mas não existe um GET
para esse Location; ou implemente um GET endpoint que corresponda (e.g.,
MapGet("/api/v1/ratings/{reviewId:guid}", async (Guid reviewId,
CancellationToken ct) => { var rating = await handlerForGet.HandleAsync(new
GetRatingByIdQuery(reviewId), ct); return rating is null ? Results.NotFound() :
Results.Ok(rating); }) ) garantindo que a rota e tipo (guid) casem com a URL
criada, ou altere o retorno no método que chama handler.HandleAsync(command,
cancellationToken) para não usar Results.Created (por exemplo
Results.Ok(reviewId) ou Results.NoContent()) para evitar um Location header
apontando para um endpoint inexistente; update as rotas/handlers correspondentes
(reviewId, handler.HandleAsync, Results.Created).
- Around line 19-22: O endpoint mapeado com group.MapPost("/",
CreateReviewAsync) documenta 201 e 400 mas omite 401 que o handler
CreateReviewAsync pode retornar; adicione
.Produces(StatusCodes.Status401Unauthorized) à cadeia de chamadas do MapPost (ao
lado de .WithName("CreateReview")/.Produces(...)) para que OpenAPI reflita o
retorno Results.Unauthorized() gerado quando o claim de identificação estiver
ausente ou inválido.
- Around line 24-27: Replace the anonymous projection and generic response type
with an explicit DTO: create a ProviderReviewDto (Id: Guid, Rating, Comment,
CreatedAt) and update the endpoint registration for GetProviderReviewsAsync to
return Produces<IEnumerable<ProviderReviewDto>> instead of
Produces<IEnumerable<object>>; change the projection inside
GetProviderReviewsAsync to map r => new ProviderReviewDto { Id = r.Id.Value,
Rating = r.Rating, Comment = r.Comment, CreatedAt = r.CreatedAt } (and apply the
same DTO/projection change for the other similar route block around lines
61-73).

In `@src/Modules/Ratings/Application/Handlers/CreateReviewCommandHandler.cs`:
- Around line 19-25: Mantenha o pré-check atual
(repository.GetByProviderAndCustomerAsync / logger.LogWarning /
InvalidOperationException) para UX, mas imponha unicidade no write path criando
uma constraint/index único no armazenamento (ex.: índice único em Review sobre
ProviderId+CustomerId na configuração do modelo/persistência). Além disso,
capture violações dessa constraint no ponto de escrita (no repositório ao
salvar/AddAsync/SaveChangesAsync ou no CreateReviewCommandHandler ao finalizar
persistência), detecte a exceção específica de update/constraint (ex.:
DbUpdateException/SqlException) e traduza-a para uma exceção de conflito de
negócio coerente (ex.: InvalidOperationException ou custom ConflictException)
com a mesma mensagem amigável ao usuário.

In `@src/Modules/Ratings/Domain/Events/ReviewRejectedDomainEvent.cs`:
- Around line 8-13: O handler de ReviewRejectedDomainEvent não está registrado
no contêiner de DI: abra Extensions.cs e adicione o registro para o handler
correspondente (por exemplo registrar ReviewRejectedDomainEventHandler como
implementação do handler de ReviewRejectedDomainEvent), espelhando o registro
existente de ReviewApprovedDomainEventHandler (linha onde
ReviewApprovedDomainEventHandler é adicionado) para que o evento emitido em
Review.cs:105 seja processado.

In
`@src/Modules/Ratings/Infrastructure/Persistence/Repositories/ReviewRepository.cs`:
- Around line 42-53: The current GetAverageRatingForProviderAsync materializes
all ratings with ToListAsync then averages in memory; change it to perform
aggregation in the database by removing ToListAsync and using query-level async
aggregations: use the same filtered IQueryable (context.Reviews.Where(r =>
r.ProviderId == providerId && r.Status == EReviewStatus.Approved)) and call
CountAsync(...) to get total and, if >0, call AverageAsync(r => r.Rating,
cancellationToken) (or AverageAsync on the selected rating) to get the average,
then Math.Round the resulting numeric value to 2 decimals and return (Average,
Count); keep references to GetAverageRatingForProviderAsync, context.Reviews,
ToListAsync, Average/AverageAsync and CountAsync to locate the change.

In
`@src/Modules/Ratings/Tests/Integration/Persistence/Repositories/ReviewRepositoryTests.cs`:
- Around line 17-19: The test currently builds DbContextOptions via new
DbContextOptionsBuilder<RatingsDbContext>().UseInMemoryDatabase(...).Options
which skips relational behaviors; replace this with an SQLite in-memory
relational provider: create a
Microsoft.Data.Sqlite.SqliteConnection("DataSource=:memory:") and Open() it,
then use new
DbContextOptionsBuilder<RatingsDbContext>().UseSqlite(connection).Options (keep
the connection open for the test lifetime) and call
context.Database.EnsureCreated() before exercising ReviewRepository to ensure
relational constraints and SQL behaviors are validated.

---

Outside diff comments:
In @.github/workflows/ci-backend.yml:
- Around line 168-179: The MODULES array in the CI workflow is missing the
Ratings test project, so add the Ratings test csproj to the MODULES list (e.g.,
"src/Modules/Ratings/Tests/MeAjudaAi.Modules.Ratings.Tests.csproj") inside the
MODULES=( ...) block in .github/workflows/ci-backend.yml so the CI runs the
Ratings tests along with the other modules.

---

Nitpick comments:
In `@src/Modules/Ratings/Application/Handlers/CreateReviewCommandHandler.cs`:
- Around line 33-45: Short-circuit the empty-comment case before calling
contentModerator.IsClean: first check if
string.IsNullOrWhiteSpace(command.Comment) and, if command.Rating >= 4, call
review.Approve(); otherwise, do not call contentModerator.IsClean for
empty/null/whitespace comments. Only call
contentModerator.IsClean(command.Comment) when the comment is non-empty and
then, if it returns false, call review.MarkAsFlagged() and
logger.LogWarning(...). This removes unnecessary calls to
contentModerator.IsClean and separates the auto-approval rule from the moderator
implementation.

In `@src/Modules/Ratings/Application/Services/ContentModerator.cs`:
- Around line 13-20: The _patterns list is being compiled per ContentModerator
instance which is wasteful; make it a static readonly field and initialize it
once (either inline or in a static constructor) by transforming the current
instance initialization of _patterns that uses BadWords.Select(...).ToList()
into a static initializer so the Regex objects are compiled once for the
lifetime of the app; keep the same RegexOptions and reference BadWords in the
static context.

In
`@src/Modules/Ratings/Tests/Unit/Application/Handlers/CreateReviewCommandHandlerTests.cs`:
- Around line 45-73: Adicionar teste cobrindo comentário composto apenas por
espaços porque o handler usa string.IsNullOrWhiteSpace; crie um novo Fact
similar a HandleAsync_Rating5WithoutComment_ShouldAutoApprove (e opcionalmente
para Rating4) no CreateReviewCommandHandlerTests que constrói
CreateReviewCommand com comment = "   ", chama _handler.HandleAsync(command) e
verifica que _repositoryMock.AddAsync recebeu um Review com Status ==
EReviewStatus.Approved (It.IsAny<CancellationToken>()), garantindo que a branch
de whitespace é tratada como null.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6c8977b8-19ac-4661-9b85-70871715db48

📥 Commits

Reviewing files that changed from the base of the PR and between a65e79f and da2e680.

📒 Files selected for processing (21)
  • .github/workflows/ci-backend.yml
  • infrastructure/keycloak/realms/meajudaai-realm.dev.json
  • src/Contracts/Identity/Enums/EAuthProvider.cs
  • src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs
  • src/Modules/Ratings/API/Extensions.cs
  • src/Modules/Ratings/Application/Handlers/CreateReviewCommandHandler.cs
  • src/Modules/Ratings/Application/Services/ContentModerator.cs
  • src/Modules/Ratings/Domain/Entities/Review.cs
  • src/Modules/Ratings/Domain/Events/ReviewRejectedDomainEvent.cs
  • src/Modules/Ratings/Domain/Repositories/IReviewRepository.cs
  • src/Modules/Ratings/Infrastructure/Events/Handlers/ReviewApprovedDomainEventHandler.cs
  • src/Modules/Ratings/Infrastructure/Extensions.cs
  • src/Modules/Ratings/Infrastructure/Persistence/Configurations/ReviewConfiguration.cs
  • src/Modules/Ratings/Infrastructure/Persistence/Repositories/ReviewRepository.cs
  • src/Modules/Ratings/Tests/Integration/Persistence/Repositories/ReviewRepositoryTests.cs
  • src/Modules/Ratings/Tests/Unit/Application/Handlers/CreateReviewCommandHandlerTests.cs
  • src/Modules/Ratings/Tests/Unit/Application/Services/ContentModeratorTests.cs
  • src/Modules/Ratings/Tests/Unit/Domain/Entities/ReviewTests.cs
  • src/Modules/SearchProviders/Infrastructure/Events/Handlers/ReviewApprovedIntegrationEventHandler.cs
  • src/Modules/SearchProviders/Infrastructure/Extensions.cs
  • src/Shared/Messaging/Messages/Ratings/ReviewApprovedIntegrationEvent.cs
✅ Files skipped from review due to trivial changes (2)
  • src/Shared/Messaging/Messages/Ratings/ReviewApprovedIntegrationEvent.cs
  • src/Contracts/Identity/Enums/EAuthProvider.cs
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/Modules/Ratings/Infrastructure/Extensions.cs
  • src/Modules/Ratings/Domain/Repositories/IReviewRepository.cs
  • src/Modules/Ratings/Tests/Unit/Domain/Entities/ReviewTests.cs
  • infrastructure/keycloak/realms/meajudaai-realm.dev.json
  • src/Modules/Ratings/Domain/Entities/Review.cs
  • src/Modules/SearchProviders/Infrastructure/Extensions.cs

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
src/Modules/Ratings/Application/Handlers/CreateReviewCommandHandler.cs (1)

55-72: ⚠️ Potential issue | 🟠 Major

Troque a detecção de violação única por exceções tipadas do provider.

Hoje a detecção depende de texto (Line 70-71), o que é frágil e pode deixar passar conflitos reais ou classificar erro errado. Prefira DbUpdateException + PostgresException.SqlState == 23505.

🛠️ Ajuste sugerido
+using Microsoft.EntityFrameworkCore;
+using Npgsql;
@@
-    private static bool IsUniqueConstraintViolation(Exception ex)
-    {
-        // Detecta violação de unicidade (Postgres 23505)
-        var message = ex.ToString();
-        return message.Contains("23505") || message.Contains("unique constraint") || (ex.InnerException?.Message.Contains("23505") ?? false);
-    }
+    private static bool IsUniqueConstraintViolation(Exception ex)
+    {
+        if (ex is DbUpdateException { InnerException: PostgresException pgEx })
+            return pgEx.SqlState == PostgresErrorCodes.UniqueViolation;
+
+        if (ex is PostgresException postgresEx)
+            return postgresEx.SqlState == PostgresErrorCodes.UniqueViolation;
+
+        return false;
+    }
#!/bin/bash
# Verificação read-only: confirmar padrão atual e dependências para trocar por exceção tipada.
rg -nP --type=cs 'IsUniqueConstraintViolation|23505|unique constraint|DbUpdateException|PostgresException|PostgresErrorCodes'
fd -i 'MeAjudaAi.Modules.Ratings.*.csproj' src/Modules/Ratings --type f --exec cat
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Ratings/Application/Handlers/CreateReviewCommandHandler.cs`
around lines 55 - 72, Replace the fragile string-based uniqueness detection with
typed exception handling: in the catch block that currently catches Exception
and calls IsUniqueConstraintViolation, catch DbUpdateException (or Exception if
necessary) and check for a PostgresException inner exception with
PostgresException.SqlState == "23505" (use the Npgsql.PostgresException type);
log the warning using the existing logger call (keeping ProviderId and
CustomerId) and rethrow the InvalidOperationException as before. Remove or
deprecate the IsUniqueConstraintViolation method (or change it to check typed
exception types) and ensure the catch references the new typed check (e.g.,
catch DbUpdateException dbEx when dbEx.InnerException is PostgresException pgEx
&& pgEx.SqlState == "23505").
docs/roadmap.md (1)

7-11: ⚠️ Potential issue | 🟡 Minor

Ajuste a consistência temporal do status das sprints.

Em 13 de abril de 2026, a Sprint 10 (12–26/04/2026) ainda está em andamento, mas aparece como [CONCLUÍDA] (Line 17) enquanto a Sprint 11 já está como atual (Line 9). Isso deixa o roadmap contraditório.

🛠️ Sugestão de ajuste
-**Sprint Atual**: 11 (Monetização & Polimento)
-**Status**: 🔄 Em Planejamento
+**Sprint Atual**: 10 (Qualidade & Onboarding)
+**Status**: 🔄 Em Andamento
@@
-## 🚀 Sprint 10 - Qualidade & Onboarding (12 Abr - 26 Abr 2026) [CONCLUÍDA]
+## 🚀 Sprint 10 - Qualidade & Onboarding (12 Abr - 26 Abr 2026) [EM ANDAMENTO]

Also applies to: 17-17

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/roadmap.md` around lines 7 - 11, The roadmap shows conflicting sprint
statuses: "Sprint Atual: 11 (Monetização & Polimento)" while "Sprint 10
(12–26/04/2026)" is marked as concluded; update the temporal consistency by
making the current sprint reflect the actual date range — either set "Sprint
Atual" to Sprint 10 (if today falls within 12–26/04/2026) or mark Sprint 10 as
ongoing and Sprint 11 as planned; edit the block containing "## 📊 Status Atual
(Abril 2026)" and the lines referencing "Sprint Atual", "Sprint 10
(12–26/04/2026)" and "Sprint 11 (Monetização & Polimento)" to ensure dates and
status labels (e.g., Em Andamento / Concluída / Em Planejamento) are consistent.
🧹 Nitpick comments (2)
src/Modules/Ratings/Infrastructure/Events/Handlers/ReviewRejectedDomainEventHandler.cs (1)

12-13: Evite logar Reason completo em nível Warning.

Se domainEvent.Reason vier de texto livre, isso pode aumentar risco de exposição de conteúdo sensível e ruído operacional. Sugiro manter o Warning sem o motivo completo e, se necessário, registrar prévia truncada em Debug.

💡 Diff sugerido
-        logger.LogWarning("Review {ReviewId} for provider {ProviderId} was rejected. Reason: {Reason}", 
-            domainEvent.AggregateId, domainEvent.ProviderId, domainEvent.Reason);
+        logger.LogWarning("Review {ReviewId} for provider {ProviderId} was rejected.",
+            domainEvent.AggregateId, domainEvent.ProviderId);
+
+        var reasonPreview = domainEvent.Reason.Length > 200
+            ? domainEvent.Reason[..200]
+            : domainEvent.Reason;
+        logger.LogDebug("Rejected review reason preview: {ReasonPreview}", reasonPreview);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Ratings/Infrastructure/Events/Handlers/ReviewRejectedDomainEventHandler.cs`
around lines 12 - 13, O LogWarning em ReviewRejectedDomainEventHandler não deve
exibir o texto livre domainEvent.Reason; altere a chamada logger.LogWarning(...)
para remover o Reason placeholder and only log "Review {ReviewId} for provider
{ProviderId} was rejected." using domainEvent.AggregateId and
domainEvent.ProviderId, then add a separate logger.LogDebug call that logs a
safely truncated preview of domainEvent.Reason (e.g., null-check then take first
N characters or use a SafeTruncate helper) so full free-form text is not emitted
at Warning level but is available at Debug for troubleshooting.
tests/MeAjudaAi.E2E.Tests/Modules/Ratings/RatingsEndToEndTests.cs (1)

73-73: Troque os literais de enum por valores compartilhados do contrato.

Type = 0 e Status = 1 deixam o teste quebradiço a qualquer reordenação dos enums/constantes. Como o arquivo já referencia MeAjudaAi.Contracts.Models, prefira serializar os valores nomeados do contrato para manter o E2E alinhado com backend/shared.

Based on learnings: priorize reutilizar Shared.Contracts para enums/constantes e manter o Web alinhado ao código compartilhado/back-end.

Also applies to: 106-106

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/MeAjudaAi.E2E.Tests/Modules/Ratings/RatingsEndToEndTests.cs` at line
73, Replace the numeric enum literals used in the test object (the properties
"Type" and "Status" in RatingsEndToEndTests.cs) with the corresponding named
enum values from the shared contract namespace MeAjudaAi.Contracts.Models (e.g.,
use the contract's enum members for the rating type and status instead of 0 and
1) so the E2E test relies on the shared enums and won't break if enum ordering
changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/MeAjudaAi.E2E.Tests/Base/TestContainerFixture.cs`:
- Line 280: The CleanupDatabaseAsync method doesn't include the
RatingsDbContext, so add logic to obtain
MeAjudaAi.Modules.Ratings.Infrastructure.Persistence.RatingsDbContext inside
CleanupDatabaseAsync (similar to how other DbContexts are handled) and
truncate/reset its tables (e.g., via Database.ExecuteSqlRaw TRUNCATE or delete +
reseed, or drop and reapply migrations) to restore isolation; update any helper
that enumerates contexts to include RatingsDbContext and apply the same change
in the other duplicate cleanup location referenced in the review.

In `@tests/MeAjudaAi.E2E.Tests/Modules/Ratings/RatingsEndToEndTests.cs`:
- Around line 48-49: Remove the fixed await Task.Delay(1000) in
RatingsEndToEndTests.cs (the sleep after the POST /api/v1/ratings call); because
SynchronousInMemoryMessageBus guarantees handlers run before the request
returns, either assert the expected state immediately after the POST or replace
the delay with a small polling loop that checks the expected condition with a
timeout (e.g., poll the read model or API until success or timeout) to avoid
flaky 1s sleeps.

---

Duplicate comments:
In `@docs/roadmap.md`:
- Around line 7-11: The roadmap shows conflicting sprint statuses: "Sprint
Atual: 11 (Monetização & Polimento)" while "Sprint 10 (12–26/04/2026)" is marked
as concluded; update the temporal consistency by making the current sprint
reflect the actual date range — either set "Sprint Atual" to Sprint 10 (if today
falls within 12–26/04/2026) or mark Sprint 10 as ongoing and Sprint 11 as
planned; edit the block containing "## 📊 Status Atual (Abril 2026)" and the
lines referencing "Sprint Atual", "Sprint 10 (12–26/04/2026)" and "Sprint 11
(Monetização & Polimento)" to ensure dates and status labels (e.g., Em Andamento
/ Concluída / Em Planejamento) are consistent.

In `@src/Modules/Ratings/Application/Handlers/CreateReviewCommandHandler.cs`:
- Around line 55-72: Replace the fragile string-based uniqueness detection with
typed exception handling: in the catch block that currently catches Exception
and calls IsUniqueConstraintViolation, catch DbUpdateException (or Exception if
necessary) and check for a PostgresException inner exception with
PostgresException.SqlState == "23505" (use the Npgsql.PostgresException type);
log the warning using the existing logger call (keeping ProviderId and
CustomerId) and rethrow the InvalidOperationException as before. Remove or
deprecate the IsUniqueConstraintViolation method (or change it to check typed
exception types) and ensure the catch references the new typed check (e.g.,
catch DbUpdateException dbEx when dbEx.InnerException is PostgresException pgEx
&& pgEx.SqlState == "23505").

---

Nitpick comments:
In
`@src/Modules/Ratings/Infrastructure/Events/Handlers/ReviewRejectedDomainEventHandler.cs`:
- Around line 12-13: O LogWarning em ReviewRejectedDomainEventHandler não deve
exibir o texto livre domainEvent.Reason; altere a chamada logger.LogWarning(...)
para remover o Reason placeholder and only log "Review {ReviewId} for provider
{ProviderId} was rejected." using domainEvent.AggregateId and
domainEvent.ProviderId, then add a separate logger.LogDebug call that logs a
safely truncated preview of domainEvent.Reason (e.g., null-check then take first
N characters or use a SafeTruncate helper) so full free-form text is not emitted
at Warning level but is available at Debug for troubleshooting.

In `@tests/MeAjudaAi.E2E.Tests/Modules/Ratings/RatingsEndToEndTests.cs`:
- Line 73: Replace the numeric enum literals used in the test object (the
properties "Type" and "Status" in RatingsEndToEndTests.cs) with the
corresponding named enum values from the shared contract namespace
MeAjudaAi.Contracts.Models (e.g., use the contract's enum members for the rating
type and status instead of 0 and 1) so the E2E test relies on the shared enums
and won't break if enum ordering changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dffd102a-a758-4449-a863-0b08e91708e5

📥 Commits

Reviewing files that changed from the base of the PR and between da2e680 and b6f376a.

⛔ Files ignored due to path filters (1)
  • src/Modules/Ratings/Tests/packages.lock.json is excluded by !**/packages.lock.json
📒 Files selected for processing (19)
  • .github/workflows/ci-backend.yml
  • docs/roadmap.md
  • src/Bootstrapper/MeAjudaAi.ApiService/Program.cs
  • src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs
  • src/Modules/Ratings/API/Extensions.cs
  • src/Modules/Ratings/Application/Handlers/CreateReviewCommandHandler.cs
  • src/Modules/Ratings/Application/Services/ContentModerator.cs
  • src/Modules/Ratings/Infrastructure/Events/Handlers/ReviewRejectedDomainEventHandler.cs
  • src/Modules/Ratings/Infrastructure/Extensions.cs
  • src/Modules/Ratings/Infrastructure/Persistence/RatingsDbContext.cs
  • src/Modules/Ratings/Infrastructure/Persistence/Repositories/ReviewRepository.cs
  • src/Modules/Ratings/Tests/Integration/Persistence/Repositories/ReviewRepositoryTests.cs
  • src/Modules/Ratings/Tests/MeAjudaAi.Modules.Ratings.Tests.csproj
  • src/Modules/Ratings/Tests/Unit/Application/Handlers/CreateReviewCommandHandlerTests.cs
  • src/Modules/SearchProviders/Infrastructure/Extensions.cs
  • tests/MeAjudaAi.E2E.Tests/Base/TestContainerFixture.cs
  • tests/MeAjudaAi.E2E.Tests/Infrastructure/SynchronousInMemoryMessageBus.cs
  • tests/MeAjudaAi.E2E.Tests/MeAjudaAi.E2E.Tests.csproj
  • tests/MeAjudaAi.E2E.Tests/Modules/Ratings/RatingsEndToEndTests.cs
✅ Files skipped from review due to trivial changes (2)
  • src/Modules/Ratings/Application/Services/ContentModerator.cs
  • src/Modules/Ratings/Tests/MeAjudaAi.Modules.Ratings.Tests.csproj
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/Bootstrapper/MeAjudaAi.ApiService/Program.cs
  • src/Modules/Ratings/Infrastructure/Persistence/RatingsDbContext.cs
  • src/Modules/Ratings/API/Extensions.cs
  • src/Modules/SearchProviders/Infrastructure/Extensions.cs
  • src/Modules/Ratings/Tests/Unit/Application/Handlers/CreateReviewCommandHandlerTests.cs

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/ci-backend.yml:
- Around line 331-357: The workflow uses untrusted github.head_ref and
github.base_ref directly in an inline shell (risking command injection) and
manipulates the worktree with git stash/git checkout (fragile); instead, pass
the branch names into the script via environment variables (use env: to set
SAFE_HEAD_REF=${{ github.head_ref }} and SAFE_BASE_REF=${{ github.base_ref }}),
validate/sanitize SAFE_* inside the script (reject/exit on suspicious
characters), and replace the stash/checkout flow with git worktree add to a
temporary directory to build the base branch there (use git worktree add
--detach tmp-base $SAFE_BASE_REF, build the project and run swagger tofile
writing api-base.json in that temp dir, then remove the worktree). Also remove
the silent fallback "|| echo \"{}\" > api-base.json" after swagger tofile and
fail the job explicitly if the base build or swagger generation fails so
breaking changes are not masked.

In `@tests/MeAjudaAi.E2E.Tests/Infrastructure/SynchronousInMemoryMessageBus.cs`:
- Around line 30-31: In SynchronousInMemoryMessageBus, resolve handlers by the
runtime event type instead of the generic TMessage: build the handlerType using
typeof(IEventHandler<>).MakeGenericType(message.GetType()) when publishing (and
the analogous place at the second occurrence), then call
scope.ServiceProvider.GetServices(handlerType) so handlers registered for
concrete runtime types (or derived types) are found; update references around
the handlerType and handlers variables in the publish/dispatch logic
accordingly.
- Around line 45-51: The current catch in SynchronousInMemoryMessageBus (the
catch(Exception ex) around the handler invocation for TMessage) swallows handler
exceptions causing false-positive E2E tests; instead, after logging the error
with handler.GetType().Name and typeof(TMessage).Name, rethrow the original
exception (use throw;) so failures surface to the test runner rather than being
swallowed.
- Around line 55-58: Change SynchronousInMemoryMessageBus.SubscribeAsync to
throw a NotSupportedException (or InvalidOperationException) instead of
returning Task.CompletedTask so incorrect subscription usage fails fast; locate
the SubscribeAsync method in class SynchronousInMemoryMessageBus and replace the
silent no-op with a thrown exception that explains subscriptions are not
supported in this test bus and that handlers must be resolved via
PublishAsync/DI (as documented in BaseTestContainerTest).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 207bc906-58cc-4a3f-8dcf-534372683230

📥 Commits

Reviewing files that changed from the base of the PR and between b6f376a and a38b3f3.

📒 Files selected for processing (8)
  • .github/workflows/ci-backend.yml
  • src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs
  • src/Modules/Ratings/Infrastructure/Extensions.cs
  • src/Modules/Ratings/Infrastructure/Persistence/RatingsDbContext.cs
  • src/Shared/Exceptions/ExceptionsExtensions.cs
  • tests/MeAjudaAi.E2E.Tests/Base/TestContainerFixture.cs
  • tests/MeAjudaAi.E2E.Tests/Infrastructure/SynchronousInMemoryMessageBus.cs
  • tests/MeAjudaAi.E2E.Tests/Modules/Ratings/RatingsEndToEndTests.cs
✅ Files skipped from review due to trivial changes (4)
  • src/Shared/Exceptions/ExceptionsExtensions.cs
  • src/Modules/Ratings/Infrastructure/Persistence/RatingsDbContext.cs
  • tests/MeAjudaAi.E2E.Tests/Modules/Ratings/RatingsEndToEndTests.cs
  • src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/MeAjudaAi.E2E.Tests/Base/TestContainerFixture.cs
  • src/Modules/Ratings/Infrastructure/Extensions.cs

Comment on lines +30 to +31
var handlerType = typeof(IEventHandler<>).MakeGenericType(typeof(TMessage));
var handlers = scope.ServiceProvider.GetServices(handlerType);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Despache handlers pelo tipo runtime do evento.

Em Line 30-31, usar typeof(TMessage) pode não encontrar handlers quando o publish for chamado com tipo base/interface.

💡 Ajuste sugerido
-        var handlerType = typeof(IEventHandler<>).MakeGenericType(typeof(TMessage));
+        var eventType = `@event`!.GetType();
+        var handlerType = typeof(IEventHandler<>).MakeGenericType(eventType);
         var handlers = scope.ServiceProvider.GetServices(handlerType);
...
-                    handler.GetType().Name, typeof(TMessage).Name);
+                    handler.GetType().Name, eventType.Name);

Also applies to: 48-48

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/MeAjudaAi.E2E.Tests/Infrastructure/SynchronousInMemoryMessageBus.cs`
around lines 30 - 31, In SynchronousInMemoryMessageBus, resolve handlers by the
runtime event type instead of the generic TMessage: build the handlerType using
typeof(IEventHandler<>).MakeGenericType(message.GetType()) when publishing (and
the analogous place at the second occurrence), then call
scope.ServiceProvider.GetServices(handlerType) so handlers registered for
concrete runtime types (or derived types) are found; update references around
the handlerType and handlers variables in the publish/dispatch logic
accordingly.

Comment on lines +55 to +58
public Task SubscribeAsync<TMessage>(Func<TMessage, CancellationToken, Task> handler, string? subscriptionName = null, CancellationToken cancellationToken = default)
{
// Não implementado para esta versão simplificada de teste
return Task.CompletedTask;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verifica se existe uso real de SubscribeAsync no repositório.
# Esperado:
# - Se houver ocorrências em fluxo E2E/aplicação, o no-op atual é risco concreto.
# - Se não houver ocorrências, a mudança para fail-fast evita regressões futuras.
rg -nP --type=cs -C2 '\bSubscribeAsync\s*<|\bSubscribeAsync\s*\('

Repository: frigini/MeAjudaAi

Length of output: 6653


🏁 Script executed:

# Search for uses of SynchronousInMemoryMessageBus in the codebase
rg -nP --type=cs 'SynchronousInMemoryMessageBus' -A 5 -B 5

Repository: frigini/MeAjudaAi

Length of output: 4371


🏁 Script executed:

# Search for actual calls to SubscribeAsync in E2E test files
rg -nP --type=cs 'SubscribeAsync' tests/MeAjudaAi.E2E.Tests/ -B 3 -A 3

Repository: frigini/MeAjudaAi

Length of output: 1664


🏁 Script executed:

# Search for actual calls to SubscribeAsync (not method signatures)
rg -nP --type=cs '\.\s*SubscribeAsync\s*<' -B 2 -A 2

Repository: frigini/MeAjudaAi

Length of output: 1094


Alterar SubscribeAsync para lançar exceção em vez de retornar silenciosamente.

O método SubscribeAsync na linha 55-58 retorna Task.CompletedTask sem fazer nada, o que pode esconder uso incorreto do bus de teste. SynchronousInMemoryMessageBus foi projetado para resolver handlers via DI através de PublishAsync, não via subscrições (conforme documentado em BaseTestContainerTest.cs:659: "No-op: subscriptions are not actually created in E2E tests"). Implementar como no-op silencioso viola esse contrato explícito e permite que código incorreto passe silenciosamente.

Ajuste sugerido
     public Task SubscribeAsync<TMessage>(Func<TMessage, CancellationToken, Task> handler, string? subscriptionName = null, CancellationToken cancellationToken = default)
     {
-        // Não implementado para esta versão simplificada de teste
-        return Task.CompletedTask;
+        throw new NotSupportedException(
+            "SubscribeAsync is not supported by SynchronousInMemoryMessageBus. Use PublishAsync with DI-registered IEventHandler<T>.");
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/MeAjudaAi.E2E.Tests/Infrastructure/SynchronousInMemoryMessageBus.cs`
around lines 55 - 58, Change SynchronousInMemoryMessageBus.SubscribeAsync to
throw a NotSupportedException (or InvalidOperationException) instead of
returning Task.CompletedTask so incorrect subscription usage fails fast; locate
the SubscribeAsync method in class SynchronousInMemoryMessageBus and replace the
silent no-op with a thrown exception that explains subscriptions are not
supported in this test bus and that handlers must be resolved via
PublishAsync/DI (as documented in BaseTestContainerTest).

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

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

⚠️ Outside diff range comments (1)
src/Shared/Exceptions/GlobalExceptionHandler.cs (1)

129-137: ⚠️ Potential issue | 🔴 Critical

Não exponha detalhes internos da exceção no ProblemDetails.Detail de erro 500.

Na Line 132, o response inclui exception.Message e exception.ToString(). Isso pode vazar stack trace, estrutura interna e dados sensíveis para o cliente. Mantenha detalhe genérico no payload e preserve detalhes técnicos apenas no log (já coberto nas Lines 143-144).

🔒 Ajuste sugerido
             _ => (
                 StatusCodes.Status500InternalServerError,
                 "Erro Interno do Servidor",
-                $"Ocorreu um erro inesperado: {exception.Message} - {exception.ToString()}",
+                "Ocorreu um erro inesperado ao processar sua requisição",
                 null,
                 new Dictionary<string, object?>
                 {
                     ["traceId"] = httpContext.TraceIdentifier
                 })

Based on learnings: User-facing validation messages and API responses shown to the frontend must be in Portuguese.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Shared/Exceptions/GlobalExceptionHandler.cs` around lines 129 - 137, O
trecho do GlobalExceptionHandler que monta o tuple para o case default está
incluindo exception.Message e exception.ToString() no ProblemDetails.Detail,
vazando stack traces; substitua esse detalhe por uma mensagem genérica em
português (por exemplo: "Ocorreu um erro inesperado no servidor.") e mantenha os
detalhes técnicos apenas nos logs (não alterar as linhas de logging existentes),
alterando a construção retornada no padrão "_" (o tuple em questão) para remover
qualquer referência a exception.Message/exception.ToString() e usar só a
mensagem genérica.
♻️ Duplicate comments (1)
.github/workflows/ci-backend.yml (1)

331-357: ⚠️ Potential issue | 🟠 Major

Endureça a geração da spec da base antes de usar esse gate.

Em Line 334, Line 343 e Line 355, github.base_ref/github.head_ref continuam interpolados diretamente no shell, mantendo o risco apontado pelo actionlint. Em Line 340-Line 356, o fluxo com git stash/git checkout no mesmo worktree também é frágil. E, em Line 352, o fallback para {} ainda pode mascarar breaking changes reais quando a geração da spec da base falha. Passe as refs por env:, gere a branch base em um git worktree separado e faça o job falhar explicitamente se api-base.json não puder ser produzido.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci-backend.yml around lines 331 - 357, Do not interpolate
github.base_ref/github.head_ref directly in the shell; instead expose them via
env (e.g., BASE_REF and HEAD_REF) and reference those env vars in the steps,
stop using git stash/git checkout in the current worktree and create a separate
git worktree to check out and build the base branch (use git worktree add <dir>
origin/$BASE_REF), run dotnet build and swagger tofile inside that worktree to
produce api-base.json, remove the fragile `|| echo "{}" > api-base.json`
fallback so failures are visible, and add an explicit check that api-base.json
exists (fail the job if missing) before proceeding; refer to the symbols
api-base.json, github.base_ref/github.head_ref, git worktree/git stash/git
checkout, dotnet build and swagger tofile to locate the relevant steps to
change.
🧹 Nitpick comments (3)
tests/MeAjudaAi.E2E.Tests/Base/BaseTestContainerTest.cs (1)

250-272: Reduza duplicação no swap de IMessageBus e IDomainEventProcessor.

Os dois ramos removem registros com a mesma lógica; extrair helper deixa a manutenção mais simples.

♻️ Refactor sugerido
+                    static void ReplaceAll(IServiceCollection services, Type serviceType, ServiceDescriptor newDescriptor)
+                    {
+                        var descriptors = services.Where(d => d.ServiceType == serviceType).ToList();
+                        foreach (var descriptor in descriptors) services.Remove(descriptor);
+                        services.Add(newDescriptor);
+                    }
+
                     if (EnableEventsAndMessageBus)
                     {
-                        var messageBusDescriptors = services.Where(d => d.ServiceType == typeof(MeAjudaAi.Shared.Messaging.IMessageBus)).ToList();
-                        foreach (var descriptor in messageBusDescriptors) services.Remove(descriptor);
-                        services.AddSingleton<MeAjudaAi.Shared.Messaging.IMessageBus, MeAjudaAi.E2E.Tests.Infrastructure.SynchronousInMemoryMessageBus>();
+                        ReplaceAll(
+                            services,
+                            typeof(MeAjudaAi.Shared.Messaging.IMessageBus),
+                            ServiceDescriptor.Singleton<MeAjudaAi.Shared.Messaging.IMessageBus, MeAjudaAi.E2E.Tests.Infrastructure.SynchronousInMemoryMessageBus>());
 
-                        var domainEventProcessorDescriptors = services.Where(d => d.ServiceType == typeof(MeAjudaAi.Shared.Events.IDomainEventProcessor)).ToList();
-                        foreach (var descriptor in domainEventProcessorDescriptors) services.Remove(descriptor);
-                        services.AddScoped<MeAjudaAi.Shared.Events.IDomainEventProcessor, MeAjudaAi.Shared.Events.DomainEventProcessor>();
+                        ReplaceAll(
+                            services,
+                            typeof(MeAjudaAi.Shared.Events.IDomainEventProcessor),
+                            ServiceDescriptor.Scoped<MeAjudaAi.Shared.Events.IDomainEventProcessor, MeAjudaAi.Shared.Events.DomainEventProcessor>());
                     }
                     else
                     {
-                        var messageBusDescriptors = services.Where(d => d.ServiceType == typeof(MeAjudaAi.Shared.Messaging.IMessageBus)).ToList();
-                        foreach (var descriptor in messageBusDescriptors) services.Remove(descriptor);
-                        services.AddSingleton<MeAjudaAi.Shared.Messaging.IMessageBus, MockMessageBus>();
+                        ReplaceAll(
+                            services,
+                            typeof(MeAjudaAi.Shared.Messaging.IMessageBus),
+                            ServiceDescriptor.Singleton<MeAjudaAi.Shared.Messaging.IMessageBus, MockMessageBus>());
 
-                        var domainEventProcessorDescriptors = services.Where(d => d.ServiceType == typeof(MeAjudaAi.Shared.Events.IDomainEventProcessor)).ToList();
-                        foreach (var descriptor in domainEventProcessorDescriptors) services.Remove(descriptor);
-                        services.AddScoped<MeAjudaAi.Shared.Events.IDomainEventProcessor, MockDomainEventProcessor>();
+                        ReplaceAll(
+                            services,
+                            typeof(MeAjudaAi.Shared.Events.IDomainEventProcessor),
+                            ServiceDescriptor.Scoped<MeAjudaAi.Shared.Events.IDomainEventProcessor, MockDomainEventProcessor>());
                     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/MeAjudaAi.E2E.Tests/Base/BaseTestContainerTest.cs` around lines 250 -
272, Extract the duplicated logic that finds and removes existing service
descriptors and then registers a replacement into a small helper method (e.g.,
RemoveAndReplaceService or ReplaceServiceDescriptor) and call it for both
IMessageBus and IDomainEventProcessor branches; the helper should accept
IServiceCollection, the service Type
(typeof(MeAjudaAi.Shared.Messaging.IMessageBus) /
typeof(MeAjudaAi.Shared.Events.IDomainEventProcessor)) and the replacement
implementation Type (SynchronousInMemoryMessageBus, MockMessageBus,
DomainEventProcessor, MockDomainEventProcessor) plus desired lifetime, perform
the services.Where(...).ToList()/Remove loop, then add the new registration
(AddSingleton/AddScoped) — replace the duplicated loops in the if
(EnableEventsAndMessageBus) / else blocks with calls to this helper.
src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs (1)

93-94: Sugestão: avaliar mover os DTOs para Shared.Contracts (ou contrato compartilhado equivalente).

Ajuda a reduzir drift entre backend e consumidores Web conforme o módulo evoluir.

Based on learnings: prioritize reuse of Shared.Contracts for enums/constants to keep Web aligned with backend/shared code.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs` around lines 93 - 94,
Move the DTOs CreateReviewRequest and ProviderReviewResponse out of
RatingsEndpoints.cs into the shared contracts project/namespace (e.g.,
Shared.Contracts) so frontend and other services can reuse them; create
equivalent types in Shared.Contracts, update their namespace and any
using/imports in RatingsEndpoints.cs and related files to reference
Shared.Contracts, remove the locally defined records to avoid duplicate types,
and ensure any serialization attributes or field names are preserved so
consumers remain compatible.
ReadLogApp/Program.cs (1)

1-19: Consolide este parser com ReadLog.cs para evitar código duplicado e comportamento divergente.

Este arquivo replica a mesma implementação (incluindo o risco da Line 9 com caminho absoluto). Vale manter um único entry point/fonte de verdade e reaproveitar a mesma rotina de leitura/extração.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ReadLogApp/Program.cs` around lines 1 - 19, O Program.Main em Program.cs
duplica a lógica de parsing presente em ReadLog.cs e ainda usa um caminho
absoluto; substitua esse arquivo para reaproveitar a rotina única em ReadLog.cs:
remove a lógica de Regex/IO de Program.Main, invocar a função pública existente
em ReadLog.cs (por exemplo ReadLog.Parse/ReadLog.ExtractError ou outro símbolo
público correspondente) e passar o caminho do ficheiro via
argumento/configuração em vez de @"c:\Code\..."; se ReadLog.cs não expor uma API
apropriada, refatore ReadLog.cs para expor um método público (por exemplo
ParseLog(string path) ou ExtractError(string text)) e faça Program.Main apenas
orquestrar leitura do caminho e chamar esse método.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/roadmap.md`:
- Around line 34-35: O texto do item "OpenAPI Breaking Change Gating (CI)" está
incorreto: o workflow em ci-backend.yml apenas compara api-base.json com
api-current/api-spec.json usando breaking-only e fail-on-diff e não verifica se
houve bump de versão; ajuste o roadmap para descrever esse comportamento real
(remova a afirmação "sem bump de versão" e documente que o job usa
breaking-only+fail-on-diff entre api-base.json e api-current/api-spec.json), or
implemente a validação faltante no pipeline (alterar
.github/workflows/ci-backend.yml para também verificar o versionamento — e.g.
comparar a versão semântica no spec ou exigir um bump commit/PR label — antes de
decidir falhar o build).

In `@ReadLog.cs`:
- Around line 9-17: Substitua o caminho absoluto usado em File.ReadAllText por
um caminho parametrizável (passado via argumento, config ou environment
variable) e adicione validação e tratamento de erro: antes de File.ReadAllText
verifique File.Exists e envolva a leitura em try/catch para logar/retornar erro
em vez de lançar; para os usos de Regex.Match (variáveis match e match2)
verifique match.Success ou match2.Success antes de chamar Console.WriteLine para
evitar imprimir strings vazias e forneça um comportamento claro de fallback (por
exemplo, mensagem informativa ou código de erro) quando nenhum match for
encontrado.

In `@src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs`:
- Around line 44-46: No método que obtém o claim do utilizador em
RatingsEndpoints.cs (onde é usado httpContext.User.FindFirst e a variável
customerIdClaim), substitua a única verificação de ClaimTypes.NameIdentifier por
uma cadeia de fallback que tenta os claims "sub" → "id" →
ClaimTypes.NameIdentifier, atribuindo o Value resultante à customerIdClaimValue
e então aplicar Guid.TryParse nesse value para obter customerId; isso garante
compatibilidade com MapInboundClaims = false e segue o mesmo padrão usado em
BaseEndpoint e PermissionHandlers.

In `@tests/MeAjudaAi.E2E.Tests/Base/BaseTestContainerTest.cs`:
- Around line 46-50: A documentação XML da propriedade EnableEventsAndMessageBus
está em inglês; altere o summary e quaisquer comentários XML associados para
português (por exemplo, substituir "Override in derived classes to enable
synchronous in-memory message bus and domain events. Used for tests that depend
on cross-module integration events (e.g. Ratings -> SearchProviders)." por uma
versão em português), mantendo o texto informativo sobre propósito e
comportamento da propriedade protected virtual bool EnableEventsAndMessageBus =>
false; e preservando a estrutura XML (/// <summary> ... </summary>).

---

Outside diff comments:
In `@src/Shared/Exceptions/GlobalExceptionHandler.cs`:
- Around line 129-137: O trecho do GlobalExceptionHandler que monta o tuple para
o case default está incluindo exception.Message e exception.ToString() no
ProblemDetails.Detail, vazando stack traces; substitua esse detalhe por uma
mensagem genérica em português (por exemplo: "Ocorreu um erro inesperado no
servidor.") e mantenha os detalhes técnicos apenas nos logs (não alterar as
linhas de logging existentes), alterando a construção retornada no padrão "_" (o
tuple em questão) para remover qualquer referência a
exception.Message/exception.ToString() e usar só a mensagem genérica.

---

Duplicate comments:
In @.github/workflows/ci-backend.yml:
- Around line 331-357: Do not interpolate github.base_ref/github.head_ref
directly in the shell; instead expose them via env (e.g., BASE_REF and HEAD_REF)
and reference those env vars in the steps, stop using git stash/git checkout in
the current worktree and create a separate git worktree to check out and build
the base branch (use git worktree add <dir> origin/$BASE_REF), run dotnet build
and swagger tofile inside that worktree to produce api-base.json, remove the
fragile `|| echo "{}" > api-base.json` fallback so failures are visible, and add
an explicit check that api-base.json exists (fail the job if missing) before
proceeding; refer to the symbols api-base.json, github.base_ref/github.head_ref,
git worktree/git stash/git checkout, dotnet build and swagger tofile to locate
the relevant steps to change.

---

Nitpick comments:
In `@ReadLogApp/Program.cs`:
- Around line 1-19: O Program.Main em Program.cs duplica a lógica de parsing
presente em ReadLog.cs e ainda usa um caminho absoluto; substitua esse arquivo
para reaproveitar a rotina única em ReadLog.cs: remove a lógica de Regex/IO de
Program.Main, invocar a função pública existente em ReadLog.cs (por exemplo
ReadLog.Parse/ReadLog.ExtractError ou outro símbolo público correspondente) e
passar o caminho do ficheiro via argumento/configuração em vez de
@"c:\Code\..."; se ReadLog.cs não expor uma API apropriada, refatore ReadLog.cs
para expor um método público (por exemplo ParseLog(string path) ou
ExtractError(string text)) e faça Program.Main apenas orquestrar leitura do
caminho e chamar esse método.

In `@src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs`:
- Around line 93-94: Move the DTOs CreateReviewRequest and
ProviderReviewResponse out of RatingsEndpoints.cs into the shared contracts
project/namespace (e.g., Shared.Contracts) so frontend and other services can
reuse them; create equivalent types in Shared.Contracts, update their namespace
and any using/imports in RatingsEndpoints.cs and related files to reference
Shared.Contracts, remove the locally defined records to avoid duplicate types,
and ensure any serialization attributes or field names are preserved so
consumers remain compatible.

In `@tests/MeAjudaAi.E2E.Tests/Base/BaseTestContainerTest.cs`:
- Around line 250-272: Extract the duplicated logic that finds and removes
existing service descriptors and then registers a replacement into a small
helper method (e.g., RemoveAndReplaceService or ReplaceServiceDescriptor) and
call it for both IMessageBus and IDomainEventProcessor branches; the helper
should accept IServiceCollection, the service Type
(typeof(MeAjudaAi.Shared.Messaging.IMessageBus) /
typeof(MeAjudaAi.Shared.Events.IDomainEventProcessor)) and the replacement
implementation Type (SynchronousInMemoryMessageBus, MockMessageBus,
DomainEventProcessor, MockDomainEventProcessor) plus desired lifetime, perform
the services.Where(...).ToList()/Remove loop, then add the new registration
(AddSingleton/AddScoped) — replace the duplicated loops in the if
(EnableEventsAndMessageBus) / else blocks with calls to this helper.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3e63529a-b9f8-4087-a239-189093f8437d

📥 Commits

Reviewing files that changed from the base of the PR and between b6f376a and 752d016.

⛔ Files ignored due to path filters (21)
  • ReadLogApp/packages.lock.json is excluded by !**/packages.lock.json
  • src/Bootstrapper/MeAjudaAi.ApiService/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Communications/Tests/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Documents/Tests/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Locations/Tests/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Providers/Tests/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Ratings/API/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Ratings/Application/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Ratings/Infrastructure/Persistence/Migrations/20260413211313_InitialRatings.Designer.cs is excluded by !**/Migrations/**, !**/*.Designer.cs
  • src/Modules/Ratings/Infrastructure/Persistence/Migrations/20260413211313_InitialRatings.cs is excluded by !**/Migrations/**
  • src/Modules/Ratings/Infrastructure/Persistence/Migrations/RatingsDbContextModelSnapshot.cs is excluded by !**/Migrations/**
  • src/Modules/Ratings/Infrastructure/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Ratings/Tests/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/SearchProviders/Tests/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/ServiceCatalogs/Tests/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Users/Tests/packages.lock.json is excluded by !**/packages.lock.json
  • tests/MeAjudaAi.ApiService.Tests/packages.lock.json is excluded by !**/packages.lock.json
  • tests/MeAjudaAi.Architecture.Tests/packages.lock.json is excluded by !**/packages.lock.json
  • tests/MeAjudaAi.E2E.Tests/packages.lock.json is excluded by !**/packages.lock.json
  • tests/MeAjudaAi.Integration.Tests/packages.lock.json is excluded by !**/packages.lock.json
  • tests/MeAjudaAi.Shared.Tests/packages.lock.json is excluded by !**/packages.lock.json
📒 Files selected for processing (18)
  • .github/workflows/ci-backend.yml
  • ReadLog.cs
  • ReadLogApp/Program.cs
  • ReadLogApp/ReadLogApp.csproj
  • docs/roadmap.md
  • src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs
  • src/Modules/Ratings/Application/Handlers/CreateReviewCommandHandler.cs
  • src/Modules/Ratings/Application/MeAjudaAi.Modules.Ratings.Application.csproj
  • src/Modules/Ratings/Infrastructure/Events/Handlers/ReviewRejectedDomainEventHandler.cs
  • src/Modules/Ratings/Infrastructure/Extensions.cs
  • src/Modules/Ratings/Infrastructure/Persistence/RatingsDbContext.cs
  • src/Shared/Exceptions/ExceptionsExtensions.cs
  • src/Shared/Exceptions/GlobalExceptionHandler.cs
  • src/Shared/Extensions/ServiceCollectionExtensions.cs
  • tests/MeAjudaAi.E2E.Tests/Base/BaseTestContainerTest.cs
  • tests/MeAjudaAi.E2E.Tests/Base/TestContainerFixture.cs
  • tests/MeAjudaAi.E2E.Tests/Infrastructure/SynchronousInMemoryMessageBus.cs
  • tests/MeAjudaAi.E2E.Tests/Modules/Ratings/RatingsEndToEndTests.cs
✅ Files skipped from review due to trivial changes (4)
  • ReadLogApp/ReadLogApp.csproj
  • src/Shared/Exceptions/ExceptionsExtensions.cs
  • src/Modules/Ratings/Application/MeAjudaAi.Modules.Ratings.Application.csproj
  • tests/MeAjudaAi.E2E.Tests/Modules/Ratings/RatingsEndToEndTests.cs
🚧 Files skipped from review as they are similar to previous changes (6)
  • tests/MeAjudaAi.E2E.Tests/Base/TestContainerFixture.cs
  • src/Modules/Ratings/Infrastructure/Extensions.cs
  • tests/MeAjudaAi.E2E.Tests/Infrastructure/SynchronousInMemoryMessageBus.cs
  • src/Modules/Ratings/Infrastructure/Persistence/RatingsDbContext.cs
  • src/Modules/Ratings/Infrastructure/Events/Handlers/ReviewRejectedDomainEventHandler.cs
  • src/Modules/Ratings/Application/Handlers/CreateReviewCommandHandler.cs

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs`:
- Around line 26-35: A rota pública GetReviewByIdAsync está retornando reviews
sem checar status; atualize a lógica para só expor reviews com
EReviewStatus.Approved: no endpoint GetReviewByIdAsync (ou no método do
repositório que ele chama, ex.: GetByIdAsync) carregue o review e verifique
review.Status == EReviewStatus.Approved antes de retorná‑lo, e se não for
Approved retorne 404 NotFound; mantenha GetProviderReviewsAsync como está (já
filtra Approved) e garanta o mesmo contrato de segurança reaplicando o filtro no
repositório se for usado por ambos.

In `@src/Modules/Ratings/Application/Handlers/CreateReviewCommandHandler.cs`:
- Around line 56-64: Remove the DB-specific catch from
CreateReviewCommandHandler (the try/catch around repository.AddAsync) and
instead translate the low-level DbUpdateException/PostgresException into a
domain-specific exception inside the persistence layer: in the repository
implementation that backs repository.AddAsync catch
DbUpdateException/PostgresException, inspect the exception details (SqlState
and/or constraint name) to confirm the (ProviderId, CustomerId) unique
constraint was violated and throw a new DuplicateReviewException; then update
CreateReviewCommandHandler to catch DuplicateReviewException (not
PostgresException/DbUpdateException) and map it to the user-facing
InvalidOperationException message.

In
`@src/Modules/ServiceCatalogs/Application/Queries/Service/GetServicesByCategoryQuery.cs`:
- Around line 7-11: Altere a documentação XML do record
GetServicesByCategoryQuery para português: substitua o <summary> em inglês por
uma descrição em português ("Consulta para recuperar todos os serviços de uma
categoria específica") e adicione/atualize a tag <param name=\"CategoryId\">
explicando em português o propósito do parâmetro CategoryId; mantenha o tipo e
assinatura do record (GetServicesByCategoryQuery(Guid CategoryId) :
Query<Result<IReadOnlyList<ServiceListDto>>>) sem alterações.

In `@src/Web/MeAjudaAi.Web.Admin/hooks/admin/use-services.ts`:
- Around line 29-34: The frontend is sending an unused activeOnly parameter to
the backend; update the useServices hook to remove activeOnly from its signature
and calls: change useServices to accept only categoryId, remove activeOnly from
the serviceKeys.list call and from the apiCategoryGet invocation (leave
apiServicesGet as-is), and update any callers of useServices to stop passing
activeOnly so frontend and backend stay consistent with
GetServicesByCategoryEndpoint/ GetServicesByCategoryQuery.

In `@tests/MeAjudaAi.E2E.Tests/Base/BaseTestContainerTest.cs`:
- Around line 250-253: When EnableEventsAndMessageBus is true the test setup
swaps IMessageBus for SynchronousInMemoryMessageBus which currently swallows
exceptions from IEventHandler; change this so the opt-in test bus propagates
handler failures: either modify
MeAjudaAi.E2E.Tests.Infrastructure.SynchronousInMemoryMessageBus to rethrow
exceptions from invoked handlers (do not catch or swallow exceptions) or
add/register a new StrictSynchronousInMemoryMessageBus that calls handlers
synchronously and lets exceptions bubble up; update the ReplaceService call that
registers IMessageBus (and keep IDomainEventProcessor registration) to use the
strict implementation so failing event handlers surface as test failures.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 54668925-d31a-4242-9c80-b7d050d26cd5

📥 Commits

Reviewing files that changed from the base of the PR and between 752d016 and 68f53ad.

📒 Files selected for processing (12)
  • .github/workflows/ci-backend.yml
  • docs/roadmap.md
  • src/Contracts/Contracts/Modules/Ratings/DTOs/CreateReviewRequest.cs
  • src/Contracts/Contracts/Modules/Ratings/DTOs/ProviderReviewResponse.cs
  • src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs
  • src/Modules/Ratings/Application/Handlers/CreateReviewCommandHandler.cs
  • src/Modules/ServiceCatalogs/API/Endpoints/Service/GetServicesByCategoryEndpoint.cs
  • src/Modules/ServiceCatalogs/Application/Handlers/Queries/Service/GetServicesByCategoryQueryHandler.cs
  • src/Modules/ServiceCatalogs/Application/Queries/Service/GetServicesByCategoryQuery.cs
  • src/Shared/Exceptions/GlobalExceptionHandler.cs
  • src/Web/MeAjudaAi.Web.Admin/hooks/admin/use-services.ts
  • tests/MeAjudaAi.E2E.Tests/Base/BaseTestContainerTest.cs
✅ Files skipped from review due to trivial changes (4)
  • src/Shared/Exceptions/GlobalExceptionHandler.cs
  • src/Contracts/Contracts/Modules/Ratings/DTOs/ProviderReviewResponse.cs
  • .github/workflows/ci-backend.yml
  • src/Contracts/Contracts/Modules/Ratings/DTOs/CreateReviewRequest.cs

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (5)
tests/MeAjudaAi.E2E.Tests/Base/BaseTestContainerTest.cs (2)

402-412: Considere extrair ApplyMigrationForContext para uma classe utilitária compartilhada.

Este método é duplicado em TestContainerFixture.cs (linhas 349-361). Embora a duplicação seja aceitável em código de teste, extrair para uma classe helper compartilhada reduziria a superfície de manutenção.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/MeAjudaAi.E2E.Tests/Base/BaseTestContainerTest.cs` around lines 402 -
412, Extraia o método ApplyMigrationForContext para uma classe utilitária
compartilhada (por exemplo MigrationTestHelper) e substitua as duas ocorrências
atuais (em BaseTestContainerTest e em TestContainerFixture) por chamadas a essa
nova classe; preserve a assinatura async Task
ApplyMigrationForContext(DbContext) e a lógica de obter schema (GetSchemaName) —
se GetSchemaName for privado, mova-o também ou torne-o acessível/public/internal
à utilidade; mantenha o comportamento de criar schema com ExecuteSqlRawAsync e
chamar MigrateAsync para não alterar o comportamento dos testes.

592-606: GetSchemaName está duplicado em TestContainerFixture.cs.

Este método idêntico existe nas linhas 308-322 de TestContainerFixture.cs. Considere extrair para uma classe utilitária compartilhada no namespace de testes E2E para evitar divergências futuras.

♻️ Sugestão de refatoração

Criar uma classe estática compartilhada:

// tests/MeAjudaAi.E2E.Tests/Infrastructure/DbContextSchemaHelper.cs
namespace MeAjudaAi.E2E.Tests.Infrastructure;

public static class DbContextSchemaHelper
{
    public static string GetSchemaName(string contextName)
    {
        return contextName switch
        {
            "UsersDbContext" => "users",
            "ProvidersDbContext" => "providers",
            "DocumentsDbContext" => "documents",
            "ServiceCatalogsDbContext" => "service_catalogs",
            "LocationsDbContext" => "locations",
            "CommunicationsDbContext" => "communications",
            "SearchProvidersDbContext" => "search_providers",
            "RatingsDbContext" => "ratings",
            _ => "public"
        };
    }
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/MeAjudaAi.E2E.Tests/Base/BaseTestContainerTest.cs` around lines 592 -
606, GetSchemaName is duplicated between BaseTestContainerTest and
TestContainerFixture; extract it into a single static helper (e.g.,
DbContextSchemaHelper) in the E2E test namespace and have both
BaseTestContainerTest and TestContainerFixture call
DbContextSchemaHelper.GetSchemaName instead, then remove the duplicate method
from both classes so there is a single source of truth for the schema mapping.
tests/MeAjudaAi.E2E.Tests/Base/TestContainerFixture.cs (1)

10-10: Remova a importação using Moq; não utilizada na linha 10.

Este arquivo não usa nenhum tipo do Moq, portanto a importação é desnecessária.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/MeAjudaAi.E2E.Tests/Base/TestContainerFixture.cs` at line 10, Remova a
diretiva de using desnecessária "using Moq;" do arquivo que contém a classe
TestContainerFixture, pois nenhum tipo do Moq é utilizado neste arquivo;
simplesmente delete ou comente a linha "using Moq;" para eliminar a importação
não utilizada.
src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs (2)

44-47: Extraia a resolução do customerId para um helper/constante compartilhado.

A ordem de fallback está correta, mas o endpoint ainda ficou acoplado a strings mágicas de autenticação. Vale centralizar isso num helper comum e, no mínimo, reutilizar as constantes já existentes para os claims padronizados, para não deixar essa regra se espalhar em múltiplos handlers.

Based on learnings: priorizar reutilização de constantes/shared para manter o código alinhado entre módulos.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs` around lines 44 - 47,
The customerId extraction in RatingsEndpoints.cs is using inline "sub" and "id"
magic strings and should be moved to a shared helper/constant: create a central
helper method (e.g., ClaimHelpers.GetCustomerId(HttpContext) or a shared
ClaimsUtil.GetUserId(ClaimsPrincipal)) that encapsulates the fallback order (sub
-> id -> ClaimTypes.NameIdentifier) and reuse existing claim constants for
standard claims; update the endpoint to call that helper and remove the inline
FindFirst calls so all modules use the same resolved logic and constants.

32-35: Paginar esse GET /provider/{providerId} antes de expô-lo anonimamente.

Hoje a rota pública sempre carrega e retorna a coleção inteira de reviews aprovados. Conforme um provider acumular avaliações, isso vira payload grande e query sem limite. Eu colocaria page/pageSize (ou cursor) no contrato agora, enquanto a superfície ainda é nova.

Also applies to: 83-96

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs` around lines 32 - 35,
The GetProviderReviews endpoint (group.MapGet("/provider/{providerId:guid}",
GetProviderReviewsAsync) / method GetProviderReviewsAsync) currently returns all
approved reviews anonymously; add pagination parameters (e.g., page, pageSize or
a cursor) to the route and method signature, enforce sane defaults and maximum
pageSize, apply the pagination to the DB/query logic so only the requested slice
is returned, change the Produces type to a paged response DTO (e.g.,
PagedResult<ProviderReviewResponse> or include total/count) and only mark the
route AllowAnonymous after pagination and limits are implemented; apply the same
pagination changes to the related endpoints referenced around lines 83-96.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/ci-backend.yml:
- Around line 364-370: The CI step named "Check for Breaking Changes" currently
sets continue-on-error: true which neutralizes the fail-on: 'ERR' gate; if you
intend to block merges on breaking API changes, remove the continue-on-error:
true entry so the step fails the workflow on ERR, otherwise keep it but update
documentation to state this is an audit-only check; locate the step by its name
"Check for Breaking Changes" and edit the continue-on-error key accordingly.
- Around line 345-352: The base-branch build invocation for MeAjudaAi.ApiService
is missing the GenerateDocumentationFile flag, causing missing XML docs and
inconsistent Swagger output; update the dotnet build command that targets
src/Bootstrapper/MeAjudaAi.ApiService/MeAjudaAi.ApiService.csproj (the one used
before swagger tofile) to include /p:GenerateDocumentationFile=true (or set
GenerateDocumentationFile in the project file) so it matches the PR build and
ensures the XML docs exist for swagger generation.

In `@src/Modules/Ratings/Domain/Exceptions/DuplicateReviewException.cs`:
- Around line 7-8: The DuplicateReviewException constructor is embedding
providerId and customerId into the exception Message; change
DuplicateReviewException(Guid providerId, Guid customerId) so it passes a
generic, English internal message (e.g., "Duplicate review detected" or
"DuplicateReview") to the base instead of interpolating the GUIDs, leaving any
user-facing/Portuguese formatting to the handler that translates the exception
for API responses; ensure the exception class no longer includes or stores
providerId/customerId in its Message or public properties.

In
`@src/Modules/Ratings/Infrastructure/Persistence/Repositories/ReviewRepository.cs`:
- Around line 20-22: In the catch for DbUpdateException in ReviewRepository (the
block matching PostgresException with SqlState "23505"), check the
PostgresException.ConstraintName equals "IX_reviews_provider_id_customer_id"
before mapping to DuplicateReviewException(review.ProviderId,
review.CustomerId); if the constraint name does not match (or is null), rethrow
the original exception so other unique-violation causes (PK or future
constraints) are not mis-mapped; ensure you access ex.InnerException as
PostgresException safely (null-check/cast) before comparing ConstraintName.

In `@src/Web/MeAjudaAi.Web.Admin/__tests__/hooks/admin/use-services.test.ts`:
- Around line 37-40: The test fails because useServices calls api.apiCategoryGet
without the query.activeOnly parameter; update the useServices hook (function
useServices) to include the query object when invoking api.apiCategoryGet (e.g.,
api.apiCategoryGet({ path: { categoryId }, query: { activeOnly: false } }) or
pass the activeOnly variable) so the call signature matches the test's
expectation of a query.activeOnly property.

In `@tests/MeAjudaAi.E2E.Tests/Base/TestContainerFixture.cs`:
- Around line 247-251: Alinhe o registro de messaging em TestContainerFixture
com a lógica condicional de BaseTestContainerTest: em vez de sempre adicionar
SynchronousInMemoryMessageBus, respeite a flag EnableEventsAndMessageBus (ou
exponha equivalente) e registre IDomainEventProcessor apropriadamente — quando
EnableEventsAndMessageBus for true, registre SynchronousInMemoryMessageBus e um
processor concreto; quando for false, registre mocks/fakes de
IDomainEventProcessor (ou deixe o registro existente de mocks) para manter
comportamento consistente entre TestContainerFixture, BaseTestContainerTest,
EnableEventsAndMessageBus, IDomainEventProcessor e
SynchronousInMemoryMessageBus.
- Line 174: TestContainerFixture and BaseTestContainerTest are out of sync on
the "Cache:Enabled" setting (TestContainerFixture sets ["Cache:Enabled"] =
"true" while BaseTestContainerTest sets it to "false"); decide whether Redis
should be enabled for tests and make both fixtures use the same value (update
the ["Cache:Enabled"] entry in TestContainerFixture or BaseTestContainerTest
accordingly), and if the false value was intended as temporary, update or remove
the "Desabilitar Redis por enquanto" comment in BaseTestContainerTest to reflect
the chosen stable behavior so tests using either fixture behave consistently.

---

Nitpick comments:
In `@src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs`:
- Around line 44-47: The customerId extraction in RatingsEndpoints.cs is using
inline "sub" and "id" magic strings and should be moved to a shared
helper/constant: create a central helper method (e.g.,
ClaimHelpers.GetCustomerId(HttpContext) or a shared
ClaimsUtil.GetUserId(ClaimsPrincipal)) that encapsulates the fallback order (sub
-> id -> ClaimTypes.NameIdentifier) and reuse existing claim constants for
standard claims; update the endpoint to call that helper and remove the inline
FindFirst calls so all modules use the same resolved logic and constants.
- Around line 32-35: The GetProviderReviews endpoint
(group.MapGet("/provider/{providerId:guid}", GetProviderReviewsAsync) / method
GetProviderReviewsAsync) currently returns all approved reviews anonymously; add
pagination parameters (e.g., page, pageSize or a cursor) to the route and method
signature, enforce sane defaults and maximum pageSize, apply the pagination to
the DB/query logic so only the requested slice is returned, change the Produces
type to a paged response DTO (e.g., PagedResult<ProviderReviewResponse> or
include total/count) and only mark the route AllowAnonymous after pagination and
limits are implemented; apply the same pagination changes to the related
endpoints referenced around lines 83-96.

In `@tests/MeAjudaAi.E2E.Tests/Base/BaseTestContainerTest.cs`:
- Around line 402-412: Extraia o método ApplyMigrationForContext para uma classe
utilitária compartilhada (por exemplo MigrationTestHelper) e substitua as duas
ocorrências atuais (em BaseTestContainerTest e em TestContainerFixture) por
chamadas a essa nova classe; preserve a assinatura async Task
ApplyMigrationForContext(DbContext) e a lógica de obter schema (GetSchemaName) —
se GetSchemaName for privado, mova-o também ou torne-o acessível/public/internal
à utilidade; mantenha o comportamento de criar schema com ExecuteSqlRawAsync e
chamar MigrateAsync para não alterar o comportamento dos testes.
- Around line 592-606: GetSchemaName is duplicated between BaseTestContainerTest
and TestContainerFixture; extract it into a single static helper (e.g.,
DbContextSchemaHelper) in the E2E test namespace and have both
BaseTestContainerTest and TestContainerFixture call
DbContextSchemaHelper.GetSchemaName instead, then remove the duplicate method
from both classes so there is a single source of truth for the schema mapping.

In `@tests/MeAjudaAi.E2E.Tests/Base/TestContainerFixture.cs`:
- Line 10: Remova a diretiva de using desnecessária "using Moq;" do arquivo que
contém a classe TestContainerFixture, pois nenhum tipo do Moq é utilizado neste
arquivo; simplesmente delete ou comente a linha "using Moq;" para eliminar a
importação não utilizada.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9265dadd-0369-4179-9ca3-44871de25aeb

📥 Commits

Reviewing files that changed from the base of the PR and between 68f53ad and bcd8f72.

📒 Files selected for processing (13)
  • .github/workflows/ci-backend.yml
  • src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs
  • src/Modules/Ratings/Application/Handlers/CreateReviewCommandHandler.cs
  • src/Modules/Ratings/Domain/Exceptions/DuplicateReviewException.cs
  • src/Modules/Ratings/Infrastructure/Persistence/Repositories/ReviewRepository.cs
  • src/Modules/ServiceCatalogs/API/Endpoints/Service/GetServicesByCategoryEndpoint.cs
  • src/Modules/ServiceCatalogs/Application/Queries/Service/GetServicesByCategoryQuery.cs
  • src/Web/MeAjudaAi.Web.Admin/__tests__/hooks/admin/use-services.test.ts
  • src/Web/MeAjudaAi.Web.Admin/hooks/admin/use-services.ts
  • tests/MeAjudaAi.E2E.Tests/Base/BaseTestContainerTest.cs
  • tests/MeAjudaAi.E2E.Tests/Base/TestContainerFixture.cs
  • tests/MeAjudaAi.E2E.Tests/Infrastructure/SynchronousInMemoryMessageBus.cs
  • tests/MeAjudaAi.E2E.Tests/Modules/Ratings/RatingsEndToEndTests.cs
✅ Files skipped from review due to trivial changes (2)
  • src/Web/MeAjudaAi.Web.Admin/hooks/admin/use-services.ts
  • src/Modules/ServiceCatalogs/Application/Queries/Service/GetServicesByCategoryQuery.cs
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/Modules/ServiceCatalogs/API/Endpoints/Service/GetServicesByCategoryEndpoint.cs
  • src/Modules/Ratings/Application/Handlers/CreateReviewCommandHandler.cs
  • tests/MeAjudaAi.E2E.Tests/Infrastructure/SynchronousInMemoryMessageBus.cs
  • tests/MeAjudaAi.E2E.Tests/Modules/Ratings/RatingsEndToEndTests.cs

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (3)
tests/MeAjudaAi.E2E.Tests/Base/Helpers/MigrationTestHelper.cs (1)

11-11: Comentário deve estar em português.

Conforme a política de idiomas do repositório, comentários devem ser escritos em português.

💡 Sugestão
-        // Ensure schema exists before migrating
+        // Garante que o schema existe antes de aplicar migrações

Based on learnings: "Comments and XML summaries must be in Portuguese."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/MeAjudaAi.E2E.Tests/Base/Helpers/MigrationTestHelper.cs` at line 11, O
comentário "// Ensure schema exists before migrating" em MigrationTestHelper.cs
deve ser traduzido para português seguindo a política do repositório; atualize
esse comentário para algo como "// Garantir que o esquema exista antes da
migração" (ou outra redação em português equivalente) mantendo o local e o
contexto dentro da classe/arquivo MigrationTestHelper para preservar clareza.
tests/MeAjudaAi.E2E.Tests/Base/BaseTestContainerTest.cs (2)

390-408: Considere documentar quando chamar CleanupDatabaseAsync.

O método está disponível para classes derivadas, mas não é invocado automaticamente em InitializeAsync nem em DisposeAsync. Se o isolamento entre testes for necessário, as classes derivadas precisam chamar explicitamente await CleanupDatabaseAsync(). Considere adicionar um comentário na documentação XML indicando quando e como usar este método.

💡 Sugestão de documentação
     /// <summary>
     /// Limpa todas as tabelas de todos os contextos registrados para garantir isolamento entre testes.
+    /// Deve ser chamado explicitamente no início de cada teste ou em InitializeAsync das classes derivadas
+    /// quando isolamento completo entre testes for necessário.
     /// </summary>
     protected async Task CleanupDatabaseAsync()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/MeAjudaAi.E2E.Tests/Base/BaseTestContainerTest.cs` around lines 390 -
408, Add an XML documentation comment to the protected method
CleanupDatabaseAsync in BaseTestContainerTest that explains it is not called
automatically from InitializeAsync or DisposeAsync and must be invoked
explicitly by derived tests when test isolation requires clearing database and
Redis state; mention typical usage (call at start or end of a test/fixture,
e.g., await CleanupDatabaseAsync()) and side effects (clears all registered
DbContexts via CleanupContext<T> and flushes Redis if _redisContainer != null)
so inheritors know when and how to invoke it.

382-385: Código morto: método local não utilizado.

O método ApplyMigrationForContext local nunca é chamado. Todas as chamadas em ApplyMigrationsAsync (linhas 355-367) vão diretamente para MigrationTestHelper.ApplyMigrationForContext. Este método pode ser removido.

🧹 Sugestão de remoção
-    private static async Task ApplyMigrationForContext(DbContext context)
-    {
-        await MigrationTestHelper.ApplyMigrationForContext(context);
-    }
-
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/MeAjudaAi.E2E.Tests/Base/BaseTestContainerTest.cs` around lines 382 -
385, Remove the dead private helper ApplyMigrationForContext method: delete the
method declaration "private static async Task ApplyMigrationForContext(DbContext
context)" from BaseTestContainerTest.cs since all callers in
ApplyMigrationsAsync already call MigrationTestHelper.ApplyMigrationForContext
directly; ensure there are no remaining references to ApplyMigrationForContext
and run tests to confirm no regressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/ci-backend.yml:
- Around line 295-298: The OpenAPI breaking-change job
"openapi-breaking-change-check" is not triggered for changes to contract DTOs,
so add the path filter "src/Contracts/**" to the workflow triggers for both push
and pull_request so PRs touching MeAjudaAi.Contracts.csproj (referenced by
MeAjudaAi.ApiService) will run this job; update the workflow's push and
pull_request paths sections to include that pattern.
- Around line 331-358: The workflow currently prefers using the checked-in file
api/api-spec.json (via git show origin/$BASE_REF:api/api-spec.json) which can be
stale; change the Checkout Base Branch Spec step to always generate the base
spec from the base branch build instead of reading api/api-spec.json: when
handling origin/$BASE_REF use git worktree add ../base-branch, cd
../base-branch, set ASPNETCORE_ENVIRONMENT=Testing, run dotnet build for
src/Bootstrapper/MeAjudaAi.ApiService/MeAjudaAi.ApiService.csproj with -c
Release -p:GenerateDocumentationFile=true, run swagger tofile to produce
$GITHUB_WORKSPACE/api-base.json from the built dll
(src/Bootstrapper/MeAjudaAi.ApiService/bin/Release/net10.0/MeAjudaAi.ApiService.dll
v1), then cd back and git worktree remove ../base-branch --force so the base
spec is always generated from the compiled base branch rather than a possibly
stale api/api-spec.json.

In `@src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs`:
- Line 88: Substitua o comentário inline atual "// Enforce limits" por sua
versão em português (por exemplo "// Aplicar limites" ou "// Impor limites") no
mesmo local; mantenha os textos de log em inglês, mas converta comentários e
documentação XML para português conforme a convenção do projeto para arquivos C#
(procure pela string literal "// Enforce limits" para localizar o ponto exato).
- Around line 89-90: Ajuste a validação de paginação em RatingsEndpoints.cs:
além do teto atual em pageSize, aplique também um limite inferior (ex.: 1) para
evitar valores 0 ou negativos; substitua a linha que ajusta pageSize por uma
expressão que garanta 1 <= pageSize <= 100 (por exemplo usando pageSize =
pageSize < 1 ? 1 : pageSize > 100 ? 100 : pageSize) e mantenha a normalização de
page (page = page < 1 ? 1 : page) para prevenir páginas inválidas.

In `@src/Shared/Utilities/ClaimHelpers.cs`:
- Line 21: A chamada public static Guid? GetUserIdGuid(HttpContext context) =>
GetUserIdGuid(context.User); precisa de uma guarda explícita para context nulo;
altere GetUserIdGuid(HttpContext context) para validar if (context == null)
throw new ArgumentNullException(nameof(context)); antes de chamar
GetUserIdGuid(context.User) (e mantenha a delegação para a sobrecarga
GetUserIdGuid(ClaimsPrincipal) para reutilizar a lógica existente).

---

Nitpick comments:
In `@tests/MeAjudaAi.E2E.Tests/Base/BaseTestContainerTest.cs`:
- Around line 390-408: Add an XML documentation comment to the protected method
CleanupDatabaseAsync in BaseTestContainerTest that explains it is not called
automatically from InitializeAsync or DisposeAsync and must be invoked
explicitly by derived tests when test isolation requires clearing database and
Redis state; mention typical usage (call at start or end of a test/fixture,
e.g., await CleanupDatabaseAsync()) and side effects (clears all registered
DbContexts via CleanupContext<T> and flushes Redis if _redisContainer != null)
so inheritors know when and how to invoke it.
- Around line 382-385: Remove the dead private helper ApplyMigrationForContext
method: delete the method declaration "private static async Task
ApplyMigrationForContext(DbContext context)" from BaseTestContainerTest.cs since
all callers in ApplyMigrationsAsync already call
MigrationTestHelper.ApplyMigrationForContext directly; ensure there are no
remaining references to ApplyMigrationForContext and run tests to confirm no
regressions.

In `@tests/MeAjudaAi.E2E.Tests/Base/Helpers/MigrationTestHelper.cs`:
- Line 11: O comentário "// Ensure schema exists before migrating" em
MigrationTestHelper.cs deve ser traduzido para português seguindo a política do
repositório; atualize esse comentário para algo como "// Garantir que o esquema
exista antes da migração" (ou outra redação em português equivalente) mantendo o
local e o contexto dentro da classe/arquivo MigrationTestHelper para preservar
clareza.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7b00debf-056d-444f-af03-8ec2cff7914b

📥 Commits

Reviewing files that changed from the base of the PR and between bcd8f72 and 540fbab.

📒 Files selected for processing (12)
  • .github/workflows/ci-backend.yml
  • src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs
  • src/Modules/Ratings/Domain/Repositories/IReviewRepository.cs
  • src/Modules/Ratings/Infrastructure/Persistence/Repositories/ReviewRepository.cs
  • src/Modules/SearchProviders/Tests/Unit/API/ModuleExtensionsTests.cs
  • src/Shared/Utilities/ClaimHelpers.cs
  • src/Web/MeAjudaAi.Web.Admin/hooks/admin/use-services.ts
  • tests/MeAjudaAi.Architecture.Tests/MeAjudaAi.Architecture.Tests.csproj
  • tests/MeAjudaAi.E2E.Tests/Base/BaseTestContainerTest.cs
  • tests/MeAjudaAi.E2E.Tests/Base/Helpers/DbContextSchemaHelper.cs
  • tests/MeAjudaAi.E2E.Tests/Base/Helpers/MigrationTestHelper.cs
  • tests/MeAjudaAi.E2E.Tests/Base/TestContainerFixture.cs
✅ Files skipped from review due to trivial changes (3)
  • src/Modules/SearchProviders/Tests/Unit/API/ModuleExtensionsTests.cs
  • tests/MeAjudaAi.E2E.Tests/Base/Helpers/DbContextSchemaHelper.cs
  • src/Modules/Ratings/Domain/Repositories/IReviewRepository.cs
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/Web/MeAjudaAi.Web.Admin/hooks/admin/use-services.ts
  • src/Modules/Ratings/Infrastructure/Persistence/Repositories/ReviewRepository.cs
  • tests/MeAjudaAi.E2E.Tests/Base/TestContainerFixture.cs

- Fix syntax error in TestContainerFixture.cs
- Move e2e mock classes to standalone Mocks.cs to resolve visibility errors
- Update ci-backend.yml triggers and enforce openapi spec generation from source
- Apply pagination limits and translate comments in RatingsEndpoints.cs
- Add null guard to ClaimHelpers.GetUserIdGuid
- Improve CleanupDatabaseAsync documentation and remove dead code in e2e base
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

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 (3)
tests/MeAjudaAi.E2E.Tests/Base/Mocks.cs (2)

6-8: Padronize a documentação XML para português.

A documentação XML está em inglês, mas deve seguir a política de idioma do repositório (comentários/XML em português).

💡 Sugestão de ajuste
 /// <summary>
-/// Mock implementation of IMessageBus for E2E tests.
-/// Does not process events to avoid deadlocks - tests should use APIs directly.
+/// Implementação mock de IMessageBus para testes E2E.
+/// Não processa eventos para evitar deadlocks - testes devem usar APIs diretamente.
 /// </summary>

Based on learnings: "Adopt a language policy across the codebase: ... Comments and XML summaries (...) must be in Portuguese."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/MeAjudaAi.E2E.Tests/Base/Mocks.cs` around lines 6 - 8, Substitua os
comentários XML em inglês por português neste arquivo de mocks: traduza o resumo
que atualmente descreve a "Mock implementation of IMessageBus for E2E tests"
para algo como "Implementação mock de IMessageBus para testes E2E" e adapte a
linha sobre não processar eventos para "Não processa eventos para evitar
deadlocks - os testes devem usar as APIs diretamente"; verifique e padronize
quaisquer outros comentários/XML no arquivo (referência: IMessageBus, Mocks.cs)
para seguir a política de idioma do repositório.

30-32: Padronize a documentação XML para português.

Mesma questão do MockMessageBus - a documentação XML deve estar em português.

💡 Sugestão de ajuste
 /// <summary>
-/// Mock implementation of IDomainEventProcessor for E2E tests
-/// Does not process domain events to avoid integration event publication
+/// Implementação mock de IDomainEventProcessor para testes E2E.
+/// Não processa eventos de domínio para evitar publicação de eventos de integração.
 /// </summary>

Based on learnings: "Adopt a language policy across the codebase: ... Comments and XML summaries (...) must be in Portuguese."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/MeAjudaAi.E2E.Tests/Base/Mocks.cs` around lines 30 - 32, Padronize a
documentação XML para português substituindo o resumo atual em inglês pela
versão em português na implementação mock de IDomainEventProcessor encontrada em
Mocks.cs; edite o conteúdo de <summary> (e qualquer outro comentário XML) para
algo como "Implementação mock de IDomainEventProcessor usada em testes E2E; não
processa eventos de domínio para evitar publicação de eventos de integração" e
aplique a mesma padronização ao MockMessageBus para manter consistência entre os
mocks.
src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs (1)

69-69: Considere usar conversão explícita para melhor clareza.

A chamada em Line 69 funciona graças ao operador implícito implicit operator ReviewId(Guid guid) no ValueObject. Porém, uma conversão explícita torna a intenção mais clara:

var review = await repository.GetByIdAsync((ReviewId)id, cancellationToken);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs` at line 69, The
current call to repository.GetByIdAsync uses the implicit ReviewId conversion;
change it to use an explicit cast to ReviewId for clarity by passing id as an
explicitly cast ReviewId when calling GetByIdAsync (i.e., cast id to ReviewId)
so the intent is obvious; update the call site in RatingsEndpoints.cs where
repository.GetByIdAsync is invoked to pass the explicit ReviewId value instead
of relying on the implicit operator.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs`:
- Around line 60-72: The POST currently returns
Results.Created("/api/v1/ratings/{reviewId}", reviewId) but GetReviewByIdAsync
filters to only Approved, causing a client to get 404 after following Location;
update the flow so the Location does not point to the final approved resource:
either return 202 Accepted instead of 201 Created and set Location to a status
endpoint (e.g., /api/v1/ratings/{reviewId}/status) that can report
Pending→Approved, or implement the status endpoint and keep Created pointing
there; modify the Results.Created call and/or add a new GET status handler so
GetReviewByIdAsync remains scoped to Approved while clients can poll status.

---

Nitpick comments:
In `@src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs`:
- Line 69: The current call to repository.GetByIdAsync uses the implicit
ReviewId conversion; change it to use an explicit cast to ReviewId for clarity
by passing id as an explicitly cast ReviewId when calling GetByIdAsync (i.e.,
cast id to ReviewId) so the intent is obvious; update the call site in
RatingsEndpoints.cs where repository.GetByIdAsync is invoked to pass the
explicit ReviewId value instead of relying on the implicit operator.

In `@tests/MeAjudaAi.E2E.Tests/Base/Mocks.cs`:
- Around line 6-8: Substitua os comentários XML em inglês por português neste
arquivo de mocks: traduza o resumo que atualmente descreve a "Mock
implementation of IMessageBus for E2E tests" para algo como "Implementação mock
de IMessageBus para testes E2E" e adapte a linha sobre não processar eventos
para "Não processa eventos para evitar deadlocks - os testes devem usar as APIs
diretamente"; verifique e padronize quaisquer outros comentários/XML no arquivo
(referência: IMessageBus, Mocks.cs) para seguir a política de idioma do
repositório.
- Around line 30-32: Padronize a documentação XML para português substituindo o
resumo atual em inglês pela versão em português na implementação mock de
IDomainEventProcessor encontrada em Mocks.cs; edite o conteúdo de <summary> (e
qualquer outro comentário XML) para algo como "Implementação mock de
IDomainEventProcessor usada em testes E2E; não processa eventos de domínio para
evitar publicação de eventos de integração" e aplique a mesma padronização ao
MockMessageBus para manter consistência entre os mocks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: eda4dbe9-e1d2-4f1a-9a23-ca8bd5ac8381

📥 Commits

Reviewing files that changed from the base of the PR and between 540fbab and cb8c165.

📒 Files selected for processing (7)
  • .github/workflows/ci-backend.yml
  • src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs
  • src/Shared/Utilities/ClaimHelpers.cs
  • tests/MeAjudaAi.E2E.Tests/Base/BaseTestContainerTest.cs
  • tests/MeAjudaAi.E2E.Tests/Base/Helpers/MigrationTestHelper.cs
  • tests/MeAjudaAi.E2E.Tests/Base/Mocks.cs
  • tests/MeAjudaAi.E2E.Tests/Base/TestContainerFixture.cs
✅ Files skipped from review due to trivial changes (1)
  • tests/MeAjudaAi.E2E.Tests/Base/Helpers/MigrationTestHelper.cs
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/Shared/Utilities/ClaimHelpers.cs
  • .github/workflows/ci-backend.yml

…ndings

- Add ReviewStatusResponse and EReviewStatus contract for ratings moderation polling
- Implement GET /api/v1/ratings/{id}/status endpoint
- Update CreateReviewAsync to return Location pointing to status endpoint
- Use explicit ReviewId casts in RatingsEndpoints for clarity
- Translate XML comments in e2e Mocks.cs to Portuguese
- Fix e2e test failures by correctly clearing multiple dependency registrations in TestContainerFixture
- Revert OpenAPI breaking changes in GetServicesByCategory endpoint
@frigini
Copy link
Copy Markdown
Owner Author

frigini commented Apr 14, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 14, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

- Add domain entity tests for Review (edge cases, idempotency)
- Add ReviewId ValueObject tests
- Add integration tests for ReviewRepository (pagination, ordering, edge cases)
- Add edge case tests for OutboxProcessorService
- Implement unit tests for CommunicationsOutboxWorker background service
- Expand CommunicationsModuleApi validation and edge case coverage
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (3)
src/Modules/Ratings/Tests/Unit/Domain/Entities/ReviewTests.cs (2)

129-140: Considere ampliar o teste de motivo inválido para null e whitespace.

Hoje só valida string vazia; o domínio bloqueia null e espaços também. Cobrir esses casos reduz risco de regressão no guard clause.

🧪 Sugestão de cobertura adicional
-[Fact]
-public void Reject_WithEmptyReason_ShouldThrowArgumentException()
+[Theory]
+[InlineData("")]
+[InlineData(" ")]
+[InlineData(null)]
+public void Reject_WithInvalidReason_ShouldThrowArgumentException(string? invalidReason)
 {
     // Arrange
     var review = Review.Create(Guid.NewGuid(), Guid.NewGuid(), 1, "Comment");

     // Act
-    Action action = () => review.Reject("");
+    Action action = () => review.Reject(invalidReason!);

     // Assert
     action.Should().Throw<ArgumentException>().WithMessage("*Motivo*");
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Ratings/Tests/Unit/Domain/Entities/ReviewTests.cs` around lines
129 - 140, Atualize os testes em ReviewTests.cs para cobrir casos de motivo
inválido além da string vazia: adicione cenários que verifiquem que
review.Reject(null) e review.Reject("   ") também lançam ArgumentException (tal
como já feito para review.Reject("")). Localize os usos de Review.Create(...) e
o método Reject(...) no teste atual e crie métodos de teste adicionais (por
exemplo Reject_WithNullReason_ShouldThrowArgumentException e
Reject_WithWhitespaceReason_ShouldThrowArgumentException) que assertam
.Should().Throw<ArgumentException>().WithMessage("*Motivo*").

69-80: Fortaleça a asserção do evento de aprovação para evitar falso positivo.

Na Line 79, Contain(...) passa mesmo se mais de um ReviewApprovedDomainEvent for publicado. Para este teste, vale isolar os eventos e exigir exatamente um evento de aprovação.

🔧 Sugestão de ajuste
 [Fact]
 public void Approve_WhenPending_ShouldChangeStatusToApprovedAndAddDomainEvent()
 {
     // Arrange
     var review = Review.Create(Guid.NewGuid(), Guid.NewGuid(), 5, "Comment");
+    review.ClearDomainEvents();

     // Act
     review.Approve();

     // Assert
     review.Status.Should().Be(EReviewStatus.Approved);
-    review.DomainEvents.Should().Contain(e => e is ReviewApprovedDomainEvent);
+    review.DomainEvents.Should().ContainSingle(e => e is ReviewApprovedDomainEvent);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Ratings/Tests/Unit/Domain/Entities/ReviewTests.cs` around lines
69 - 80, O teste
Approve_WhenPending_ShouldChangeStatusToApprovedAndAddDomainEvent está frágil
porque usar DomainEvents.Should().Contain(e => e is ReviewApprovedDomainEvent)
aceita múltiplos eventos; atualize a asserção para filtrar os eventos do review
(Review.DomainEvents) por tipo ReviewApprovedDomainEvent (por exemplo via
OfType/Where) e exigir exatamente um evento desse tipo (ContainSingle/Count ==
1) e, opcionalmente, verificar propriedades do evento para garantir que é o
esperado após chamar Review.Approve().
src/Modules/Ratings/Tests/Unit/Domain/ValueObjects/ReviewIdTests.cs (1)

15-15: Evite acoplamento do teste ao texto localizado da exceção.

Em Line 15, o WithMessage("*vazio*") deixa o teste frágil a mudanças de idioma/wording sem mudança de comportamento. Prefira validar apenas o tipo da exceção (ou outro contrato estável).

💡 Ajuste sugerido
-        action.Should().Throw<ArgumentException>().WithMessage("*vazio*");
+        action.Should().Throw<ArgumentException>();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Ratings/Tests/Unit/Domain/ValueObjects/ReviewIdTests.cs` at line
15, The test currently asserts the localized exception message using
WithMessage("*vazio*"), making it brittle; change the assertion to only verify
the exception type (e.g., keep action.Should().Throw<ArgumentException>() and
remove WithMessage) or assert a stable contract (like a specific exception
subclass or an error code) in the ReviewIdTests.cs test where the variable
action is used so the test no longer depends on localized wording.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/Modules/Communications/Tests/Unit/Application/ModuleApi/CommunicationsModuleApiTests.cs`:
- Around line 117-130: Add tests that pass a null DTO to the public API methods
to preserve coverage for the null-guard branch: add a Theory or Fact in
CommunicationsModuleApiTests that calls _api.SendSmsAsync(null) and asserts
IsSuccess is false (referencing SendSmsAsync and SmsMessageDto) and likewise add
a test calling _api.SendPushAsync(null) asserting failure (referencing
SendPushAsync and PushMessageDto); place these alongside the existing null-field
tests so regressions to the null-DTO guard clauses in CommunicationsModuleApi.cs
are detected.

In
`@src/Modules/Communications/Tests/Unit/Application/Services/CommunicationsOutboxWorkerTests.cs`:
- Around line 47-49: Replace fragile Task.Delay usage in
ExecuteAsync_WhenExceptionOccurs_ShouldContinue (and similar tests) with
deterministic synchronization: remove Task.Delay calls around
worker.StartAsync/StopAsync and instead use TaskCompletionSource or mock
callbacks to signal each worker iteration; instrument the mock (or the worker
loop) to SetResult on a TCS after each iteration, await that TCS twice (or as
many iterations as you need) to prove the loop continued after the exception,
then call worker.StopAsync. Update assertions to verify the logger/mocked
handler was invoked for multiple iterations (e.g., called >= 2) rather than only
once. Locate changes around worker.StartAsync, worker.StopAsync, and the test
method ExecuteAsync_WhenExceptionOccurs_ShouldContinue to implement the
TCS-based synchronization and deterministic iteration counting.

In
`@src/Modules/Communications/Tests/Unit/Application/Services/OutboxProcessorServiceTests.cs`:
- Around line 125-145: The test currently cancels cts before calling
ProcessPendingMessagesAsync so the method can short-circuit and never call
_emailSenderMock.SendAsync; instead, remove the early cts.Cancel() and simulate
cancellation from within the SendAsync callback (or have SendAsync throw an
OperationCanceledException) so the cancellation happens during dispatch, then
assert SendAsync was called once and the message remains Pending; refer to
ProcessPendingMessagesAsync, the CancellationTokenSource cts and the
_emailSenderMock.Setup(x => x.SendAsync(...)) to implement the change.

---

Nitpick comments:
In `@src/Modules/Ratings/Tests/Unit/Domain/Entities/ReviewTests.cs`:
- Around line 129-140: Atualize os testes em ReviewTests.cs para cobrir casos de
motivo inválido além da string vazia: adicione cenários que verifiquem que
review.Reject(null) e review.Reject("   ") também lançam ArgumentException (tal
como já feito para review.Reject("")). Localize os usos de Review.Create(...) e
o método Reject(...) no teste atual e crie métodos de teste adicionais (por
exemplo Reject_WithNullReason_ShouldThrowArgumentException e
Reject_WithWhitespaceReason_ShouldThrowArgumentException) que assertam
.Should().Throw<ArgumentException>().WithMessage("*Motivo*").
- Around line 69-80: O teste
Approve_WhenPending_ShouldChangeStatusToApprovedAndAddDomainEvent está frágil
porque usar DomainEvents.Should().Contain(e => e is ReviewApprovedDomainEvent)
aceita múltiplos eventos; atualize a asserção para filtrar os eventos do review
(Review.DomainEvents) por tipo ReviewApprovedDomainEvent (por exemplo via
OfType/Where) e exigir exatamente um evento desse tipo (ContainSingle/Count ==
1) e, opcionalmente, verificar propriedades do evento para garantir que é o
esperado após chamar Review.Approve().

In `@src/Modules/Ratings/Tests/Unit/Domain/ValueObjects/ReviewIdTests.cs`:
- Line 15: The test currently asserts the localized exception message using
WithMessage("*vazio*"), making it brittle; change the assertion to only verify
the exception type (e.g., keep action.Should().Throw<ArgumentException>() and
remove WithMessage) or assert a stable contract (like a specific exception
subclass or an error code) in the ReviewIdTests.cs test where the variable
action is used so the test no longer depends on localized wording.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cd3fcc89-2aeb-491f-b69a-319673714e1c

📥 Commits

Reviewing files that changed from the base of the PR and between cb8c165 and f928b17.

📒 Files selected for processing (12)
  • src/Contracts/Contracts/Modules/Ratings/DTOs/ReviewStatusResponse.cs
  • src/Contracts/Contracts/Modules/Ratings/Enums/EReviewStatus.cs
  • src/Modules/Communications/Tests/Unit/Application/ModuleApi/CommunicationsModuleApiTests.cs
  • src/Modules/Communications/Tests/Unit/Application/Services/CommunicationsOutboxWorkerTests.cs
  • src/Modules/Communications/Tests/Unit/Application/Services/OutboxProcessorServiceTests.cs
  • src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs
  • src/Modules/Ratings/Tests/Integration/Persistence/Repositories/ReviewRepositoryTests.cs
  • src/Modules/Ratings/Tests/Unit/Domain/Entities/ReviewTests.cs
  • src/Modules/Ratings/Tests/Unit/Domain/ValueObjects/ReviewIdTests.cs
  • src/Modules/ServiceCatalogs/API/Endpoints/Service/GetServicesByCategoryEndpoint.cs
  • tests/MeAjudaAi.E2E.Tests/Base/Mocks.cs
  • tests/MeAjudaAi.E2E.Tests/Base/TestContainerFixture.cs
✅ Files skipped from review due to trivial changes (2)
  • src/Contracts/Contracts/Modules/Ratings/Enums/EReviewStatus.cs
  • tests/MeAjudaAi.E2E.Tests/Base/Mocks.cs
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/Modules/ServiceCatalogs/API/Endpoints/Service/GetServicesByCategoryEndpoint.cs
  • src/Modules/Ratings/Tests/Integration/Persistence/Repositories/ReviewRepositoryTests.cs
  • src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs

Filipe Frigini added 2 commits April 14, 2026 12:31
- Make CommunicationsOutboxWorker public to allow access from the test project
- Add null DTO guard tests to CommunicationsModuleApiTests
- Implement deterministic synchronization in CommunicationsOutboxWorkerTests using TCS
- Improve OutboxProcessorService cancellation test to trigger during dispatch
- Tighten ReviewTests domain event assertions and expand validation scenarios
- Remove brittle localized message assertion from ReviewIdTests
- Allow configurable checkInterval in CommunicationsOutboxWorker for faster testing
- Fix ReviewRepositoryTests data order to match descending sorting
@frigini
Copy link
Copy Markdown
Owner Author

frigini commented Apr 14, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 14, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

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 (3)
src/Modules/Communications/Tests/Unit/Application/ModuleApi/CommunicationsModuleApiTests.cs (2)

205-218: Recupere a verificação de curto-circuito para paginação inválida.

A asserção de falha está correta, mas sem Verify(..., Times.Never) para SearchAsync o teste não garante que a validação interrompe o fluxo antes do repositório.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Communications/Tests/Unit/Application/ModuleApi/CommunicationsModuleApiTests.cs`
around lines 205 - 218, The test currently asserts failure for invalid
pagination but does not verify the repository was never called; update the unit
test GetLogsAsync_WithInvalidPaginationValues_ShouldReturnFailure to also assert
that the repository SearchAsync was not invoked (Verify(..., Times.Never)) so
the validation short-circuits before calling SearchAsync; reference the
CommunicationLogQuery used in the test and the mocked repository method
SearchAsync to add the verification.

117-130: Fortaleça os testes negativos com asserções de “sem efeito colateral”.

Nesses cenários, validar só IsSuccess == false pode deixar passar regressão onde a API falha, mas ainda grava na outbox. Recomendo adicionar Verify(..., Times.Never) para AddAsync (e, se aplicável, SaveChangesAsync) em cada teste negativo.

💡 Exemplo de ajuste (aplicar o mesmo padrão nos demais testes negativos)
 // Assert
 result.IsSuccess.Should().BeFalse();
+_outboxRepositoryMock.Verify(
+    x => x.AddAsync(It.IsAny<OutboxMessage>(), It.IsAny<CancellationToken>()),
+    Times.Never);
+_outboxRepositoryMock.Verify(
+    x => x.SaveChangesAsync(It.IsAny<CancellationToken>()),
+    Times.Never);

Also applies to: 153-163, 165-179, 182-192

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Communications/Tests/Unit/Application/ModuleApi/CommunicationsModuleApiTests.cs`
around lines 117 - 130, O teste SendSmsAsync_WithInvalidData_ShouldReturnFailure
apenas verifica IsSuccess==false; atualize-o para também garantir "sem efeito
colateral" verificando que o repositório/outbox não recebeu chamadas: após
chamar _api.SendSmsAsync(dto) adicione Verify(..., Times.Never) para o método
AddAsync do outbox/repository (e, se aplicável, Verify(..., Times.Never) para
SaveChangesAsync do unit of work/DbContext mock). Aplique o mesmo padrão nos
outros testes negativos referenciados (linhas 153-163, 165-179, 182-192), usando
os mesmos identificadores de mocks usados nos testes (por ex. AddAsync e
SaveChangesAsync).
src/Modules/Communications/Application/Services/CommunicationsOutboxWorker.cs (1)

11-11: Evite ampliar a superfície pública apenas para testes.

Na Line 11, o tipo passou a public sealed; como este worker parece detalhe interno de infraestrutura do módulo, prefira manter internal sealed e liberar acesso para testes via InternalsVisibleTo para preservar boundary/API.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Communications/Application/Services/CommunicationsOutboxWorker.cs`
at line 11, O tipo CommunicationsOutboxWorker foi marcado como public sealed;
para não alargar a superfície pública troque a declaração para internal sealed e
mantenha a classe visível aos testes usando InternalsVisibleTo no assembly de
testes, garantindo que apenas o assembly de testes tenha acesso interno;
atualize a declaração de CommunicationsOutboxWorker para internal sealed e
adicione a entrada InternalsVisibleTo("NomeDoAssemblyDeTestes") no
AssemblyInfo/Project file do projeto.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/Modules/Communications/Application/Services/CommunicationsOutboxWorker.cs`:
- Around line 14-16: O construtor está aceitando qualquer TimeSpan via parâmetro
checkInterval e atribuindo a _checkInterval, o que permite TimeSpan.Zero ou
valores negativos que fazem Task.Delay em ExecuteAsync lançar
ArgumentOutOfRangeException fora do bloco try/catch; valide o parâmetro
checkInterval no construtor da classe CommunicationsOutboxWorker: rejeite (throw
ArgumentException) TimeSpan.Zero e quaisquer valores negativos exceto
Timeout.InfiniteTimeSpan, ou normalize para um mínimo aceitável (por exemplo
TimeSpan.FromSeconds(1)) antes de atribuir a _checkInterval; assegure que a
validação refere-se explicitamente ao parâmetro checkInterval e ao campo
_checkInterval e que ExecuteAsync usa somente esse valor validado.

---

Nitpick comments:
In
`@src/Modules/Communications/Application/Services/CommunicationsOutboxWorker.cs`:
- Line 11: O tipo CommunicationsOutboxWorker foi marcado como public sealed;
para não alargar a superfície pública troque a declaração para internal sealed e
mantenha a classe visível aos testes usando InternalsVisibleTo no assembly de
testes, garantindo que apenas o assembly de testes tenha acesso interno;
atualize a declaração de CommunicationsOutboxWorker para internal sealed e
adicione a entrada InternalsVisibleTo("NomeDoAssemblyDeTestes") no
AssemblyInfo/Project file do projeto.

In
`@src/Modules/Communications/Tests/Unit/Application/ModuleApi/CommunicationsModuleApiTests.cs`:
- Around line 205-218: The test currently asserts failure for invalid pagination
but does not verify the repository was never called; update the unit test
GetLogsAsync_WithInvalidPaginationValues_ShouldReturnFailure to also assert that
the repository SearchAsync was not invoked (Verify(..., Times.Never)) so the
validation short-circuits before calling SearchAsync; reference the
CommunicationLogQuery used in the test and the mocked repository method
SearchAsync to add the verification.
- Around line 117-130: O teste SendSmsAsync_WithInvalidData_ShouldReturnFailure
apenas verifica IsSuccess==false; atualize-o para também garantir "sem efeito
colateral" verificando que o repositório/outbox não recebeu chamadas: após
chamar _api.SendSmsAsync(dto) adicione Verify(..., Times.Never) para o método
AddAsync do outbox/repository (e, se aplicável, Verify(..., Times.Never) para
SaveChangesAsync do unit of work/DbContext mock). Aplique o mesmo padrão nos
outros testes negativos referenciados (linhas 153-163, 165-179, 182-192), usando
os mesmos identificadores de mocks usados nos testes (por ex. AddAsync e
SaveChangesAsync).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 61fc6be0-8fa7-47c4-81ca-b4bac3de0bf7

📥 Commits

Reviewing files that changed from the base of the PR and between cb8c165 and eb88910.

📒 Files selected for processing (13)
  • src/Contracts/Contracts/Modules/Ratings/DTOs/ReviewStatusResponse.cs
  • src/Contracts/Contracts/Modules/Ratings/Enums/EReviewStatus.cs
  • src/Modules/Communications/Application/Services/CommunicationsOutboxWorker.cs
  • src/Modules/Communications/Tests/Unit/Application/ModuleApi/CommunicationsModuleApiTests.cs
  • src/Modules/Communications/Tests/Unit/Application/Services/CommunicationsOutboxWorkerTests.cs
  • src/Modules/Communications/Tests/Unit/Application/Services/OutboxProcessorServiceTests.cs
  • src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs
  • src/Modules/Ratings/Tests/Integration/Persistence/Repositories/ReviewRepositoryTests.cs
  • src/Modules/Ratings/Tests/Unit/Domain/Entities/ReviewTests.cs
  • src/Modules/Ratings/Tests/Unit/Domain/ValueObjects/ReviewIdTests.cs
  • src/Modules/ServiceCatalogs/API/Endpoints/Service/GetServicesByCategoryEndpoint.cs
  • tests/MeAjudaAi.E2E.Tests/Base/Mocks.cs
  • tests/MeAjudaAi.E2E.Tests/Base/TestContainerFixture.cs
✅ Files skipped from review due to trivial changes (4)
  • src/Contracts/Contracts/Modules/Ratings/Enums/EReviewStatus.cs
  • src/Contracts/Contracts/Modules/Ratings/DTOs/ReviewStatusResponse.cs
  • tests/MeAjudaAi.E2E.Tests/Base/Mocks.cs
  • src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/Modules/ServiceCatalogs/API/Endpoints/Service/GetServicesByCategoryEndpoint.cs
  • src/Modules/Ratings/Tests/Unit/Domain/ValueObjects/ReviewIdTests.cs
  • src/Modules/Communications/Tests/Unit/Application/Services/CommunicationsOutboxWorkerTests.cs
  • tests/MeAjudaAi.E2E.Tests/Base/TestContainerFixture.cs
  • src/Modules/Communications/Tests/Unit/Application/Services/OutboxProcessorServiceTests.cs
  • src/Modules/Ratings/Tests/Integration/Persistence/Repositories/ReviewRepositoryTests.cs
  • src/Modules/Ratings/Tests/Unit/Domain/Entities/ReviewTests.cs

@frigini
Copy link
Copy Markdown
Owner Author

frigini commented Apr 14, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 14, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
src/Modules/Communications/Application/Services/CommunicationsOutboxWorker.cs (1)

64-76: ⚠️ Potential issue | 🟡 Minor

LogInformation de parada pode não executar no cancelamento durante o delay.

Hoje o Task.Delay está fora do try/catch; se o token cancelar nesse ponto, o loop encerra por exceção antes do log da Line 76.

💡 Ajuste sugerido
         while (!stoppingToken.IsCancellationRequested)
         {
             try
             {
                 using var scope = _scopeFactory.CreateScope();
@@
                 if (processedCount > 0)
                 {
                     _logger.LogInformation("Processed {Count} outbox messages.", processedCount);
                 }
+
+                await Task.Delay(_checkInterval, stoppingToken);
             }
-            catch (OperationCanceledException)
+            catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
             {
-                // Normal shutdown
+                break;
             }
             catch (Exception ex)
             {
                 _logger.LogError(ex, "Error occurred while processing communications outbox.");
             }
-
-            await Task.Delay(_checkInterval, stoppingToken);
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Communications/Application/Services/CommunicationsOutboxWorker.cs`
around lines 64 - 76, No momento o Task.Delay(_checkInterval, stoppingToken)
está fora do bloco try/catch dentro de ExecuteAsync (CommunicationsOutboxWorker)
e se o token for cancelado durante o delay a exceção impede o log final; move o
await Task.Delay(_checkInterval, stoppingToken) para dentro do try que contém o
loop processing logic or wrap the delay in its own try/catch catching
OperationCanceledException and breaking the loop so execution continues to the
final _logger.LogInformation("Communications Outbox Worker stopped."); ensure
you reference the existing OperationCanceledException handling and the _logger
usage in CommunicationsOutboxWorker/ExecuteAsync so the worker cleanly exits and
always reaches the stop log.
♻️ Duplicate comments (1)
docs/roadmap.md (1)

9-10: ⚠️ Potential issue | 🟡 Minor

Status da Sprint 10 está temporalmente inconsistente

Em Line 10 e Line 17, a Sprint 10 aparece como concluída, mas o período informado é 12 Abr - 26 Abr 2026 e, em 14 Abr 2026, ela ainda está em andamento. Ajuste para evitar desalinhamento de planejamento.

💡 Ajuste sugerido
-**Status**: ✅ Concluída
+**Status**: 🔄 Em andamento
...
-## 🚀 Sprint 10 - Qualidade & Onboarding (12 Abr - 26 Abr 2026) ✅ [CONCLUÍDO]
+## 🚀 Sprint 10 - Qualidade & Onboarding (12 Abr - 26 Abr 2026) 🔄 [EM ANDAMENTO]

Also applies to: 17-17

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/roadmap.md` around lines 9 - 10, O bloco que declara "**Sprint Atual**:
10 (Qualidade & Onboarding)" e "**Status**: ✅ Concluída" está inconsistente com
o período "12 Abr - 26 Abr 2026" e a data de referência "14 Abr 2026" — atualize
o texto referente à Sprint 10 para refletir corretamente que ela está "Em
andamento" (ou ajuste o intervalo de datas para um período já concluído), e
aplique a mesma correção onde o mesmo par de linhas aparecer (o segundo bloco
duplicado com status na Sprint 10) para manter ambos os trechos sincronizados.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/Modules/Communications/Tests/Unit/Application/ModuleApi/CommunicationsModuleApiTests.cs`:
- Around line 203-210: No teste GetLogsAsync_WithNullQuery_ShouldReturnFailure
apenas valida IsSuccess, falta assegurar que o repositório não foi chamado;
atualize o teste para, além de assertar result.IsSuccess == false, verificar que
o mock do repositório (o objeto usado pela API que expõe SearchAsync) nunca
recebeu uma chamada a SearchAsync quando _api.GetLogsAsync(null!) é executado,
usando a API de verificação do mock (por exemplo Verify/VerifyNoOtherCalls) para
garantir que SearchAsync não foi invocado.

---

Outside diff comments:
In
`@src/Modules/Communications/Application/Services/CommunicationsOutboxWorker.cs`:
- Around line 64-76: No momento o Task.Delay(_checkInterval, stoppingToken) está
fora do bloco try/catch dentro de ExecuteAsync (CommunicationsOutboxWorker) e se
o token for cancelado durante o delay a exceção impede o log final; move o await
Task.Delay(_checkInterval, stoppingToken) para dentro do try que contém o loop
processing logic or wrap the delay in its own try/catch catching
OperationCanceledException and breaking the loop so execution continues to the
final _logger.LogInformation("Communications Outbox Worker stopped."); ensure
you reference the existing OperationCanceledException handling and the _logger
usage in CommunicationsOutboxWorker/ExecuteAsync so the worker cleanly exits and
always reaches the stop log.

---

Duplicate comments:
In `@docs/roadmap.md`:
- Around line 9-10: O bloco que declara "**Sprint Atual**: 10 (Qualidade &
Onboarding)" e "**Status**: ✅ Concluída" está inconsistente com o período "12
Abr - 26 Abr 2026" e a data de referência "14 Abr 2026" — atualize o texto
referente à Sprint 10 para refletir corretamente que ela está "Em andamento" (ou
ajuste o intervalo de datas para um período já concluído), e aplique a mesma
correção onde o mesmo par de linhas aparecer (o segundo bloco duplicado com
status na Sprint 10) para manter ambos os trechos sincronizados.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f80b2ec1-9b50-43d5-b056-8df2b78194ac

📥 Commits

Reviewing files that changed from the base of the PR and between eb88910 and 02b0460.

📒 Files selected for processing (7)
  • docs/roadmap.md
  • src/Modules/Communications/Application/AssemblyInfo.cs
  • src/Modules/Communications/Application/MeAjudaAi.Modules.Communications.Application.csproj
  • src/Modules/Communications/Application/Services/CommunicationsOutboxWorker.cs
  • src/Modules/Communications/Tests/Unit/Application/ModuleApi/CommunicationsModuleApiTests.cs
  • src/Modules/Ratings/API/API.Client/Ratings/GetReviewById.bru
  • src/Modules/Ratings/API/API.Client/Ratings/GetReviewStatus.bru
✅ Files skipped from review due to trivial changes (2)
  • src/Modules/Ratings/API/API.Client/Ratings/GetReviewStatus.bru
  • src/Modules/Ratings/API/API.Client/Ratings/GetReviewById.bru

Filipe Frigini added 2 commits April 14, 2026 15:59
…fications

- Moves Task.Delay inside the try block in CommunicationsOutboxWorker to ensure clean exit logging when cancelled.
- Adds VerifyNoOtherCalls to GetLogsAsync null query test to confirm no repository interaction occurs on failure.
@github-actions
Copy link
Copy Markdown

Code Coverage Report

Code Coverage

Project Package Line Rate Branch Rate Health
MeAjudaAi.Web.Admin components/layout 72% 64%
MeAjudaAi.Web.Admin components/providers 93% 92%
MeAjudaAi.Web.Admin components/ui 100% 86%
MeAjudaAi.Web.Admin hooks/admin 95% 79%
MeAjudaAi.Web.Admin Summary 94% (253 / 269) 80% (118 / 147) -
MeAjudaAi.Web.Customer components/auth 62% 57%
MeAjudaAi.Web.Customer components/home 100% 100%
MeAjudaAi.Web.Customer components/layout 63% 36%
MeAjudaAi.Web.Customer components/profile 93% 94%
MeAjudaAi.Web.Customer components/providers 61% 64%
MeAjudaAi.Web.Customer components/reviews 64% 70%
MeAjudaAi.Web.Customer components/search 88% 70%
MeAjudaAi.Web.Customer components/service 100% 100%
MeAjudaAi.Web.Customer components/ui 97% 97%
MeAjudaAi.Web.Customer hooks 88% 63%
MeAjudaAi.Web.Customer lib/api 83% 80%
MeAjudaAi.Web.Customer lib/constants 100% 100%
MeAjudaAi.Web.Customer lib/schemas 92% 80%
MeAjudaAi.Web.Customer lib/services 94% 86%
MeAjudaAi.Web.Customer lib/utils 77% 90%
MeAjudaAi.Web.Customer Summary 80% (660 / 816) 74% (576 / 770) -
MeAjudaAi.Web.Provider root 53% 36%
MeAjudaAi.Web.Provider components/dashboard 100% 100%
MeAjudaAi.Web.Provider components/layout 100% 100%
MeAjudaAi.Web.Provider components/profile 100% 100%
MeAjudaAi.Web.Provider components/providers 100% 100%
MeAjudaAi.Web.Provider components/ui 100% 90%
MeAjudaAi.Web.Provider lib/api 100% 93%
MeAjudaAi.Web.Provider lib/constants 100% 100%
MeAjudaAi.Web.Provider lib/schemas 87% 75%
MeAjudaAi.Web.Provider lib/services 100% 100%
MeAjudaAi.Web.Provider lib/utils 96% 98%
MeAjudaAi.Web.Provider Summary 86% (277 / 321) 78% (357 / 457) -
Overall Summary 84% (1190 / 1406) 76% (1051 / 1374) -

@github-actions
Copy link
Copy Markdown

Code Coverage

Package Line Rate Branch Rate Complexity Health
MeAjudaAi.ApiService 87% 71% 443
MeAjudaAi.Modules.Communications.API 100% 100% 4
MeAjudaAi.Modules.Communications.Application 89% 37% 43
MeAjudaAi.Modules.Communications.Domain 90% 100% 17
MeAjudaAi.Modules.Communications.Infrastructure 98% 100% 20
MeAjudaAi.Modules.Documents.API 100% 100% 8
MeAjudaAi.Modules.Documents.Application 97% 49% 66
MeAjudaAi.Modules.Documents.Domain 99% 100% 32
MeAjudaAi.Modules.Documents.Infrastructure 91% 72% 98
MeAjudaAi.Modules.Locations.API 100% 100% 18
MeAjudaAi.Modules.Locations.Application 100% 100% 17
MeAjudaAi.Modules.Locations.Domain 95% 82% 128
MeAjudaAi.Modules.Locations.Infrastructure 93% 60% 41
MeAjudaAi.Modules.Providers.API 99% 100% 43
MeAjudaAi.Modules.Providers.Application 98% 62% 145
MeAjudaAi.Modules.Providers.Domain 90% 82% 288
MeAjudaAi.Modules.Providers.Infrastructure 100% 100% 25
MeAjudaAi.Modules.Ratings.API 100% 100% 3
MeAjudaAi.Modules.Ratings.Application 100% 100% 6
MeAjudaAi.Modules.Ratings.Domain 95% 100% 27
MeAjudaAi.Modules.Ratings.Infrastructure 69% 0% 15
MeAjudaAi.Modules.SearchProviders.API 100% 100% 3
MeAjudaAi.Modules.SearchProviders.Application 91% 54% 37
MeAjudaAi.Modules.SearchProviders.Domain 96% 82% 51
MeAjudaAi.Modules.SearchProviders.Infrastructure 98% 95% 36
MeAjudaAi.Modules.ServiceCatalogs.API 100% 100% 20
MeAjudaAi.Modules.ServiceCatalogs.Application 100% 100% 37
MeAjudaAi.Modules.ServiceCatalogs.Domain 96% 93% 70
MeAjudaAi.Modules.ServiceCatalogs.Infrastructure 96% 100% 9
MeAjudaAi.Modules.Users.API 100% 100% 20
MeAjudaAi.Modules.Users.Application 97% 73% 76
MeAjudaAi.Modules.Users.Domain 94% 95% 145
MeAjudaAi.Modules.Users.Infrastructure 90% 56% 94
MeAjudaAi.Shared 74% 65% 886
Summary 91% (8628 / 9526) 72% (1476 / 2050) 2971

Minimum allowed line rate is 90%

@frigini
Copy link
Copy Markdown
Owner Author

frigini commented Apr 14, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 14, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@frigini frigini merged commit 63b2753 into master Apr 14, 2026
10 checks passed
@frigini frigini deleted the feature/sprint-10-quality-onboarding branch April 14, 2026 19:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant