Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 94 additions & 1 deletion MeAjudaAi.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18
VisualStudioVersion = 18.0.11205.157 d18.0
VisualStudioVersion = 18.0.11205.157
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
EndProject
Expand Down Expand Up @@ -145,6 +145,28 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{4726175B
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MeAjudaAi.Modules.Search.Tests", "src\Modules\Search\Tests\MeAjudaAi.Modules.Search.Tests.csproj", "{C7F6B6F4-4F9C-C844-500C-87E3802A6C4B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Catalogs", "Catalogs", "{8B551008-B254-EBAF-1B6D-AB7C420234EA}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Domain", "Domain", "{B346CC0B-427A-E442-6F5D-8AAE1AB081D6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MeAjudaAi.Modules.Catalogs.Domain", "src\Modules\Catalogs\Domain\MeAjudaAi.Modules.Catalogs.Domain.csproj", "{DC1D1ACD-A21E-4BA0-A22D-77450234BD2A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Application", "Application", "{1510B873-F5F8-8A20-05CA-B70BA1F93C8F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MeAjudaAi.Modules.Catalogs.Application", "src\Modules\Catalogs\Application\MeAjudaAi.Modules.Catalogs.Application.csproj", "{44577491-2FC0-4F52-AF5C-2BC9B323CDB7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Infrastructure", "Infrastructure", "{8D23D6D3-2B2E-7F09-866F-FA51CC0FC081}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MeAjudaAi.Modules.Catalogs.Infrastructure", "src\Modules\Catalogs\Infrastructure\MeAjudaAi.Modules.Catalogs.Infrastructure.csproj", "{3B6D6C13-1E04-47B9-B44E-36D25DF913C7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "API", "API", "{A63FE417-CEAA-2A64-637A-6EABC61CE16D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MeAjudaAi.Modules.Catalogs.API", "src\Modules\Catalogs\API\MeAjudaAi.Modules.Catalogs.API.csproj", "{30A2D3C4-AF98-40A1-AA90-ED7C5FE090F8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{BDD25844-1435-F5BA-1F9B-EFB3B12C916F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MeAjudaAi.Modules.Catalogs.Tests", "src\Modules\Catalogs\Tests\MeAjudaAi.Modules.Catalogs.Tests.csproj", "{2C85E336-66A2-4B4F-845A-DBA2A6520162}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -551,6 +573,66 @@ Global
{C7F6B6F4-4F9C-C844-500C-87E3802A6C4B}.Release|x64.Build.0 = Release|Any CPU
{C7F6B6F4-4F9C-C844-500C-87E3802A6C4B}.Release|x86.ActiveCfg = Release|Any CPU
{C7F6B6F4-4F9C-C844-500C-87E3802A6C4B}.Release|x86.Build.0 = Release|Any CPU
{DC1D1ACD-A21E-4BA0-A22D-77450234BD2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DC1D1ACD-A21E-4BA0-A22D-77450234BD2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DC1D1ACD-A21E-4BA0-A22D-77450234BD2A}.Debug|x64.ActiveCfg = Debug|Any CPU
{DC1D1ACD-A21E-4BA0-A22D-77450234BD2A}.Debug|x64.Build.0 = Debug|Any CPU
{DC1D1ACD-A21E-4BA0-A22D-77450234BD2A}.Debug|x86.ActiveCfg = Debug|Any CPU
{DC1D1ACD-A21E-4BA0-A22D-77450234BD2A}.Debug|x86.Build.0 = Debug|Any CPU
{DC1D1ACD-A21E-4BA0-A22D-77450234BD2A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DC1D1ACD-A21E-4BA0-A22D-77450234BD2A}.Release|Any CPU.Build.0 = Release|Any CPU
{DC1D1ACD-A21E-4BA0-A22D-77450234BD2A}.Release|x64.ActiveCfg = Release|Any CPU
{DC1D1ACD-A21E-4BA0-A22D-77450234BD2A}.Release|x64.Build.0 = Release|Any CPU
{DC1D1ACD-A21E-4BA0-A22D-77450234BD2A}.Release|x86.ActiveCfg = Release|Any CPU
{DC1D1ACD-A21E-4BA0-A22D-77450234BD2A}.Release|x86.Build.0 = Release|Any CPU
{44577491-2FC0-4F52-AF5C-2BC9B323CDB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{44577491-2FC0-4F52-AF5C-2BC9B323CDB7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{44577491-2FC0-4F52-AF5C-2BC9B323CDB7}.Debug|x64.ActiveCfg = Debug|Any CPU
{44577491-2FC0-4F52-AF5C-2BC9B323CDB7}.Debug|x64.Build.0 = Debug|Any CPU
{44577491-2FC0-4F52-AF5C-2BC9B323CDB7}.Debug|x86.ActiveCfg = Debug|Any CPU
{44577491-2FC0-4F52-AF5C-2BC9B323CDB7}.Debug|x86.Build.0 = Debug|Any CPU
{44577491-2FC0-4F52-AF5C-2BC9B323CDB7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{44577491-2FC0-4F52-AF5C-2BC9B323CDB7}.Release|Any CPU.Build.0 = Release|Any CPU
{44577491-2FC0-4F52-AF5C-2BC9B323CDB7}.Release|x64.ActiveCfg = Release|Any CPU
{44577491-2FC0-4F52-AF5C-2BC9B323CDB7}.Release|x64.Build.0 = Release|Any CPU
{44577491-2FC0-4F52-AF5C-2BC9B323CDB7}.Release|x86.ActiveCfg = Release|Any CPU
{44577491-2FC0-4F52-AF5C-2BC9B323CDB7}.Release|x86.Build.0 = Release|Any CPU
{3B6D6C13-1E04-47B9-B44E-36D25DF913C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3B6D6C13-1E04-47B9-B44E-36D25DF913C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3B6D6C13-1E04-47B9-B44E-36D25DF913C7}.Debug|x64.ActiveCfg = Debug|Any CPU
{3B6D6C13-1E04-47B9-B44E-36D25DF913C7}.Debug|x64.Build.0 = Debug|Any CPU
{3B6D6C13-1E04-47B9-B44E-36D25DF913C7}.Debug|x86.ActiveCfg = Debug|Any CPU
{3B6D6C13-1E04-47B9-B44E-36D25DF913C7}.Debug|x86.Build.0 = Debug|Any CPU
{3B6D6C13-1E04-47B9-B44E-36D25DF913C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3B6D6C13-1E04-47B9-B44E-36D25DF913C7}.Release|Any CPU.Build.0 = Release|Any CPU
{3B6D6C13-1E04-47B9-B44E-36D25DF913C7}.Release|x64.ActiveCfg = Release|Any CPU
{3B6D6C13-1E04-47B9-B44E-36D25DF913C7}.Release|x64.Build.0 = Release|Any CPU
{3B6D6C13-1E04-47B9-B44E-36D25DF913C7}.Release|x86.ActiveCfg = Release|Any CPU
{3B6D6C13-1E04-47B9-B44E-36D25DF913C7}.Release|x86.Build.0 = Release|Any CPU
{30A2D3C4-AF98-40A1-AA90-ED7C5FE090F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{30A2D3C4-AF98-40A1-AA90-ED7C5FE090F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{30A2D3C4-AF98-40A1-AA90-ED7C5FE090F8}.Debug|x64.ActiveCfg = Debug|Any CPU
{30A2D3C4-AF98-40A1-AA90-ED7C5FE090F8}.Debug|x64.Build.0 = Debug|Any CPU
{30A2D3C4-AF98-40A1-AA90-ED7C5FE090F8}.Debug|x86.ActiveCfg = Debug|Any CPU
{30A2D3C4-AF98-40A1-AA90-ED7C5FE090F8}.Debug|x86.Build.0 = Debug|Any CPU
{30A2D3C4-AF98-40A1-AA90-ED7C5FE090F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{30A2D3C4-AF98-40A1-AA90-ED7C5FE090F8}.Release|Any CPU.Build.0 = Release|Any CPU
{30A2D3C4-AF98-40A1-AA90-ED7C5FE090F8}.Release|x64.ActiveCfg = Release|Any CPU
{30A2D3C4-AF98-40A1-AA90-ED7C5FE090F8}.Release|x64.Build.0 = Release|Any CPU
{30A2D3C4-AF98-40A1-AA90-ED7C5FE090F8}.Release|x86.ActiveCfg = Release|Any CPU
{30A2D3C4-AF98-40A1-AA90-ED7C5FE090F8}.Release|x86.Build.0 = Release|Any CPU
{2C85E336-66A2-4B4F-845A-DBA2A6520162}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2C85E336-66A2-4B4F-845A-DBA2A6520162}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2C85E336-66A2-4B4F-845A-DBA2A6520162}.Debug|x64.ActiveCfg = Debug|Any CPU
{2C85E336-66A2-4B4F-845A-DBA2A6520162}.Debug|x64.Build.0 = Debug|Any CPU
{2C85E336-66A2-4B4F-845A-DBA2A6520162}.Debug|x86.ActiveCfg = Debug|Any CPU
{2C85E336-66A2-4B4F-845A-DBA2A6520162}.Debug|x86.Build.0 = Debug|Any CPU
{2C85E336-66A2-4B4F-845A-DBA2A6520162}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2C85E336-66A2-4B4F-845A-DBA2A6520162}.Release|Any CPU.Build.0 = Release|Any CPU
{2C85E336-66A2-4B4F-845A-DBA2A6520162}.Release|x64.ActiveCfg = Release|Any CPU
{2C85E336-66A2-4B4F-845A-DBA2A6520162}.Release|x64.Build.0 = Release|Any CPU
{2C85E336-66A2-4B4F-845A-DBA2A6520162}.Release|x86.ActiveCfg = Release|Any CPU
{2C85E336-66A2-4B4F-845A-DBA2A6520162}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -622,6 +704,17 @@ Global
{0A64D976-2B75-C6F2-9C87-3A780C963FA3} = {9BC7D786-47F5-44BB-88A1-DDEB0022FF23}
{4726175B-331E-49FA-A49A-EE5AC30B495A} = {6FF68FBA-C4AF-48EC-AFE2-E320F2195C79}
{C7F6B6F4-4F9C-C844-500C-87E3802A6C4B} = {4726175B-331E-49FA-A49A-EE5AC30B495A}
{8B551008-B254-EBAF-1B6D-AB7C420234EA} = {D55DFAF4-45A1-4C45-AA54-8CE46F0AFB1F}
{B346CC0B-427A-E442-6F5D-8AAE1AB081D6} = {8B551008-B254-EBAF-1B6D-AB7C420234EA}
{DC1D1ACD-A21E-4BA0-A22D-77450234BD2A} = {B346CC0B-427A-E442-6F5D-8AAE1AB081D6}
{1510B873-F5F8-8A20-05CA-B70BA1F93C8F} = {8B551008-B254-EBAF-1B6D-AB7C420234EA}
{44577491-2FC0-4F52-AF5C-2BC9B323CDB7} = {1510B873-F5F8-8A20-05CA-B70BA1F93C8F}
{8D23D6D3-2B2E-7F09-866F-FA51CC0FC081} = {8B551008-B254-EBAF-1B6D-AB7C420234EA}
{3B6D6C13-1E04-47B9-B44E-36D25DF913C7} = {8D23D6D3-2B2E-7F09-866F-FA51CC0FC081}
{A63FE417-CEAA-2A64-637A-6EABC61CE16D} = {8B551008-B254-EBAF-1B6D-AB7C420234EA}
{30A2D3C4-AF98-40A1-AA90-ED7C5FE090F8} = {A63FE417-CEAA-2A64-637A-6EABC61CE16D}
{BDD25844-1435-F5BA-1F9B-EFB3B12C916F} = {8B551008-B254-EBAF-1B6D-AB7C420234EA}
{2C85E336-66A2-4B4F-845A-DBA2A6520162} = {BDD25844-1435-F5BA-1F9B-EFB3B12C916F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {391B5342-8EC5-4DF0-BCDA-6D73F87E8751}
Expand Down
166 changes: 138 additions & 28 deletions docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,54 +218,164 @@ public interface ILocationModuleApi : IModuleApi

---

### 1.6. 🛠️ Módulo Service Catalog (Planejado)
### 1.6. Módulo Service Catalog (Concluído)

**Objetivo**: Gerenciar tipos de serviços que prestadores podem oferecer.
**Status**: Implementado e funcional com testes completos

#### **Arquitetura Proposta**
- **Padrão**: Simple CRUD com hierarquia de categorias
**Objetivo**: Gerenciar tipos de serviços que prestadores podem oferecer através de um catálogo admin-managed.

#### **Entidades de Domínio**
#### **Arquitetura Implementada**
- **Padrão**: DDD + CQRS com hierarquia de categorias
- **Schema**: `catalogs` (isolado)
- **Naming**: snake_case no banco, PascalCase no código

#### **Entidades de Domínio Implementadas**
```csharp
// ServiceCategory: Aggregate Root
public class ServiceCategory
public sealed class ServiceCategory : AggregateRoot<ServiceCategoryId>
{
public Guid CategoryId { get; }
public string Name { get; } // e.g., "Limpeza", "Reparos"
public string Name { get; }
public string? Description { get; }
public bool IsActive { get; }
public int DisplayOrder { get; }

// Domain Events: Created, Updated, Activated, Deactivated
// Business Rules: Nome único, validações de criação/atualização
}

// Service: Aggregate Root
public class Service
public sealed class Service : AggregateRoot<ServiceId>
{
public Guid ServiceId { get; }
public Guid CategoryId { get; }
public string Name { get; } // e.g., "Limpeza de Apartamento", "Conserto de Torneira"
public ServiceCategoryId CategoryId { get; }
public string Name { get; }
public string? Description { get; }
public bool IsActive { get; }
public int DisplayOrder { get; }

// Domain Events: Created, Updated, Activated, Deactivated, CategoryChanged
// Business Rules: Nome único, categoria ativa, validações
}
```

// ProviderService: Entity (linking table)
public class ProviderService
#### **Camadas Implementadas**

**1. Domain Layer** ✅
- `ServiceCategoryId` e `ServiceId` (strongly-typed IDs)
- Agregados com lógica de negócio completa
- 9 Domain Events (lifecycle completo)
- Repositórios: `IServiceCategoryRepository`, `IServiceRepository`
- Exception: `CatalogDomainException`

**2. Application Layer** ✅
- **DTOs**: ServiceCategoryDto, ServiceDto, ServiceListDto, ServiceCategoryWithCountDto
- **Commands** (11 total):
- Categories: Create, Update, Activate, Deactivate, Delete
- Services: Create, Update, ChangeCategory, Activate, Deactivate, Delete
- **Queries** (6 total):
- Categories: GetById, GetAll, GetWithCount
- Services: GetById, GetAll, GetByCategory
- **Handlers**: 11 Command Handlers + 6 Query Handlers
- **Module API**: `CatalogsModuleApi` para comunicação inter-módulos

**3. Infrastructure Layer** ✅
- `CatalogsDbContext` com schema isolation (`catalogs`)
- EF Core Configurations (snake_case, índices otimizados)
- Repositories com SaveChangesAsync integrado
- DI registration com auto-migration support

**4. API Layer** ✅
- **Endpoints REST** usando Minimal APIs pattern:
- `GET /api/v1/catalogs/categories` - Listar categorias
- `GET /api/v1/catalogs/categories/{id}` - Buscar categoria
- `POST /api/v1/catalogs/categories` - Criar categoria
- `PUT /api/v1/catalogs/categories/{id}` - Atualizar categoria
- `POST /api/v1/catalogs/categories/{id}/activate` - Ativar
- `POST /api/v1/catalogs/categories/{id}/deactivate` - Desativar
- `DELETE /api/v1/catalogs/categories/{id}` - Deletar
- `GET /api/v1/catalogs/services` - Listar serviços
- `GET /api/v1/catalogs/services/{id}` - Buscar serviço
- `GET /api/v1/catalogs/services/category/{categoryId}` - Por categoria
- `POST /api/v1/catalogs/services` - Criar serviço
- `PUT /api/v1/catalogs/services/{id}` - Atualizar serviço
- `POST /api/v1/catalogs/services/{id}/change-category` - Mudar categoria
- `POST /api/v1/catalogs/services/{id}/activate` - Ativar
- `POST /api/v1/catalogs/services/{id}/deactivate` - Desativar
- `DELETE /api/v1/catalogs/services/{id}` - Deletar
- **Autorização**: Todos endpoints requerem role Admin
- **Versionamento**: Sistema unificado via BaseEndpoint

**5. Shared.Contracts** ✅
- `ICatalogsModuleApi` - Interface pública
- DTOs: ModuleServiceCategoryDto, ModuleServiceDto, ModuleServiceListDto, ModuleServiceValidationResultDto

#### **API Pública Implementada**
```csharp
public interface ICatalogsModuleApi : IModuleApi
{
public Guid ProviderId { get; }
public Guid ServiceId { get; }
public DateTime AddedAt { get; }
Task<Result<ModuleServiceCategoryDto?>> GetServiceCategoryByIdAsync(Guid categoryId, CancellationToken ct = default);
Task<Result<IReadOnlyList<ModuleServiceCategoryDto>>> GetAllServiceCategoriesAsync(bool activeOnly = true, CancellationToken ct = default);
Task<Result<ModuleServiceDto?>> GetServiceByIdAsync(Guid serviceId, CancellationToken ct = default);
Task<Result<IReadOnlyList<ModuleServiceListDto>>> GetAllServicesAsync(bool activeOnly = true, CancellationToken ct = default);
Task<Result<IReadOnlyList<ModuleServiceDto>>> GetServicesByCategoryAsync(Guid categoryId, bool activeOnly = true, CancellationToken ct = default);
Task<Result<bool>> IsServiceActiveAsync(Guid serviceId, CancellationToken ct = default);
Task<Result<ModuleServiceValidationResultDto>> ValidateServicesAsync(Guid[] serviceIds, CancellationToken ct = default);
}
```

#### **Abordagem de Gestão**
- **Admin-managed catalog**: Admins criam categorias e serviços
- **Provider selection**: Prestadores selecionam de catálogo pré-definido
- **(Futuro)** Sugestões de prestadores para novos serviços → fila de moderação

#### **Implementação**
1. **Schema**: Criar `meajudaai_services` com `service_categories`, `services`, `provider_services`
2. **Admin API**: CRUD endpoints para categorias e serviços
3. **Provider API**: Estender módulo Providers para add/remove serviços do perfil
4. **Validações**: Business rules para evitar duplicatas e serviços inativos
5. **Testes**: Unit tests para domain logic + integration tests para APIs
#### **Status de Compilação**
- ✅ **Domain**: BUILD SUCCEEDED (3 warnings XML documentation)
- ✅ **Application**: BUILD SUCCEEDED (18 warnings SonarLint - não críticos)
- ✅ **Infrastructure**: BUILD SUCCEEDED
- ✅ **API**: BUILD SUCCEEDED
- ✅ **Adicionado à Solution**: 4 projetos integrados

#### **Integração com Outros Módulos**
- **Providers Module** (Planejado): Adicionar ProviderServices linking table
- **Search Module** (Planejado): Denormalizar services nos SearchableProvider
- **Admin Portal**: Endpoints prontos para gestão de catálogo

#### **Próximos Passos (Pós-MVP)**
1. **Testes**: Implementar unit tests e integration tests
2. **Migrations**: Criar e aplicar migration inicial do schema `catalogs`
3. **Bootstrap**: Integrar no Program.cs e AppHost
4. **Provider Integration**: Estender Providers para suportar ProviderServices
5. **Admin UI**: Interface para gestão de catálogo
6. **Seeders**: Popular catálogo inicial com serviços comuns

#### **Considerações Técnicas**
- **SaveChangesAsync**: Integrado nos repositórios (padrão do projeto)
- **Validações**: Nome único por categoria/serviço, categoria ativa para criar serviço
- **Soft Delete**: Não implementado (hard delete com validação de dependências)
- **Cascata**: DeleteServiceCategory valida se há serviços vinculados

#### **Schema do Banco de Dados**
```sql
-- Schema: catalogs
CREATE TABLE catalogs.service_categories (
id UUID PRIMARY KEY,
name VARCHAR(200) NOT NULL UNIQUE,
description TEXT,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
display_order INT NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP
);

CREATE TABLE catalogs.services (
id UUID PRIMARY KEY,
category_id UUID NOT NULL REFERENCES catalogs.service_categories(id),
name VARCHAR(200) NOT NULL UNIQUE,
description TEXT,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
display_order INT NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP
);

CREATE INDEX idx_services_category_id ON catalogs.services(category_id);
CREATE INDEX idx_services_is_active ON catalogs.services(is_active);
CREATE INDEX idx_service_categories_is_active ON catalogs.service_categories(is_active);
```

---

Expand Down
5 changes: 5 additions & 0 deletions docs/testing/integration_tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
## Overview
This document provides comprehensive guidance for writing and maintaining integration tests in the MeAjudaAi platform.

> **📚 Related Documentation**:
> - [Test Infrastructure (TestContainers)](./test_infrastructure.md) - Infraestrutura de containers para testes
> - [Code Coverage Guide](./code_coverage_guide.md) - Guia de cobertura de código
> - [Test Authentication Examples](./test_auth_examples.md) - Exemplos de autenticação em testes

## Integration Testing Strategy

The project implements a **two-level integration testing architecture** to balance test coverage, performance, and isolation:
Expand Down
Loading
Loading