Documentación de Arquitectura de Software y Testing (.NET 10 / Angular)
Manual técnico de referencia con especificación de componentes para el Módulo de Sanidad y estructuras de referencia técnica.
1. Crear un nuevo Endpoint POST en TriajeController
Implementación específica para la API de Triaje:
Ruta: C:\Users\juan9\OneDrive\Desktop\ciclo 7\construncionII\zootech-backend\src\ZooTech.InterfaceAdapters\Modules\Module_Sanidad\Controllers\TriajeController.cs
[HttpPost("nuevo-endpoint")]
[ProducesResponseType(typeof(GeneralResponseDTO<TriajeResponse>), StatusCodes.Status200OK)]
public async Task<IActionResult> CreateNuevoEndpoint([FromBody] TriajeRequest request)
{
var behaviorPipeline = _createTriajeBehaviorPipelineFactory.Create();
var command = new CreateTriajeCommand
{
VacunoId = request.VacunoId,
TipoPesoCode = request.TipoPesoCode,
PesoKg = request.PesoKg,
Observaciones = request.Observaciones,
EncargadoUsuarioId = request.EncargadoUsuarioId,
FechaHora = request.FechaHora
};
var result = await behaviorPipeline.Handle(command, CancellationToken.None);
return Ok(GeneralResponseDTO<TriajeResponse>.CreateSuccess(
new TriajeResponse(result.Id, result.Codigo, result.FechaHora, result.VacunoId, result.TipoPesoCode, result.PesoKg, result.Observaciones, result.EstadoRegistroCode, result.EncargadoUsuarioId, result.CreatedAt),
"Registro creado exitosamente"
));
}
// Especificación del Patrón (Sintaxis de Referencia)
[HttpPost("custom-route")]
public async Task<IActionResult> CustomAction([FromBody] CustomRequest request)
{
var pipeline = _pipelineFactory.Create();
var command = new CustomCommand { Property = request.Property };
var result = await pipeline.Handle(command, CancellationToken.None);
return Ok(GeneralResponseDTO<CustomResponse>.CreateSuccess(result));
}
2. Mostrar la columna Observaciones en el Listado (Campo Existente)
Mapeo específico del campo real observaciones:
Ruta: C:\Users\juan9\OneDrive\Desktop\ciclo 7\construncionII\zootech-backend\src\ZooTech.Infrastructure\Persistence\Entities\triaje.cs
[StringLength(150)]
[Unicode(false)]
public string? observaciones { get; set; }
Ruta: C:\Users\juan9\OneDrive\Desktop\ciclo 7\construncionII\zootech-frontend-1\src\app\modules\sanidad\presentation\triaje-report\reporte-listado-grafico-general\reporte-listado-grafico-general.html
<th>Observaciones</th>
<td>{{ t.observaciones || '-' }}</td>
// Especificación del Patrón (Mapeo de Referencia)
// Entity Property
public string? PropertyName { get; set; }
// HTML Cell Rendering
<th>HeaderName</th>
<td>{{ item.propertyName }}</td>
3. Pruebas Unitarias de Endpoints CRUD (TriajeController)
Ruta: C:\Users\juan9\OneDrive\Desktop\ciclo 7\construncionII\zootech-backend\tests\Unit\ZooTech.InterfaceAdapters.UnitTests\Modules\Module_Sanidad\Controllers\TriajeControllerTests.cs
[Fact]
public async Task Create_MapsRequestAndReturnsCreatedAtAction()
{
CreateTriajeCommand? captured = null;
var controller = CreateController(createFactory: new CreateFactory(command =>
{
captured = command;
return Task.FromResult(new CreateTriajeOutput(9, "TRI009", Now, command.VacunoId, command.TipoPesoCode!, command.PesoKg, command.Observaciones, "ACTIVO", command.EncargadoUsuarioId, Now));
}));
var request = new TriajeRequest(5, "CONTROL", 120m, "Obs", 10, Now.AddDays(-1));
var result = await controller.Create(request);
var createdResult = Assert.IsType<CreatedAtActionResult>(result);
Assert.Equal(5, captured?.VacunoId);
}
// Especificación del Patrón (Estructura Test Fact)
[Fact]
public async Task ControllerMethod_ReturnsExpectedResult()
{
var controller = CreateController(factory: new MockFactory(req => Task.FromResult(new MockOutput())));
var result = await controller.TargetMethod(new MockRequest());
Assert.IsType<OkObjectResult>(result);
}
4. Pruebas Unitarias para Endpoints de Gráficos e Historial
Ruta: C:\Users\juan9\OneDrive\Desktop\ciclo 7\construncionII\zootech-backend\tests\Unit\ZooTech.InterfaceAdapters.UnitTests\Modules\Module_Sanidad\Controllers\TriajeControllerTests.cs
[Fact]
public async Task GetHistorialGeneral_MapsDatesAndReturnsOk()
{
GetHistorialGeneralCommand? captured = null;
var controller = CreateController(historialGeneralFactory: new HistorialGeneralFactory(command =>
{
captured = command;
return Task.FromResult(new GetHistorialGeneralOutput(new[] { new HistorialTriajeItemOutput(1, Now, "CONTROL", 120m) }));
}));
var result = await controller.GetHistorialGeneral("2026-07-01", "2026-07-31");
Assert.IsType<OkObjectResult>(result);
Assert.Equal("2026-07-01", captured?.FechaDesde);
}
// Especificación del Patrón (Filtro de Consulta)
[Fact]
public async Task GetQuery_PassesFiltersCorrectly()
{
CustomQuery? captured = null;
var controller = CreateController(queryFactory: new QueryFactory(q => { captured = q; return Task.FromResult(new QueryOutput()); }));
var result = await controller.QueryEndpoint("filter");
Assert.Equal("filter", captured?.Filter);
}
5. Pruebas Unitarias para Exportación a PDF y Excel
Ruta: C:\Users\juan9\OneDrive\Desktop\ciclo 7\construncionII\zootech-backend\tests\Unit\ZooTech.InterfaceAdapters.UnitTests\Modules\Module_Sanidad\Controllers\TriajeControllerTests.cs
[Fact]
public async Task GeneratePdf_MapsFiltersAndReturnsFile()
{
GenerateTriajesPdfQuery? captured = null;
var controller = CreateController(pdfFactory: new PdfFactory(query =>
{
captured = query;
return Task.FromResult(new GenerateTriajesPdfOutput(new byte[] { 1 }, "application/pdf", "triajes.pdf"));
}));
var result = await controller.GeneratePdf("2026-07-22", "2026-07-01", "2026-07-31", "TRI", "Luna", "CONTROL", "120", 5, CancellationToken.None);
var fileResult = Assert.IsType<FileContentResult>(result);
Assert.Equal("application/pdf", fileResult.ContentType);
}
// Especificación del Patrón (Test Descarga Archivo)
[Fact]
public async Task ExportFile_ReturnsFileContentResult()
{
var controller = CreateController(fileFactory: new FileFactory(_ => Task.FromResult(new FileOutput(new byte[] { 1 }, "application/pdf", "doc.pdf"))));
var result = await controller.ExportEndpoint();
var fileResult = Assert.IsType<FileContentResult>(result);
Assert.Equal("application/pdf", fileResult.ContentType);
}
6. Mandados Rápidos de Código (Tareas en Vivo)
Mandado A: Filtro por Rango de Pesos (LINQ)
Ruta: C:\Users\juan9\OneDrive\Desktop\ciclo 7\construncionII\zootech-backend\src\ZooTech.Infrastructure\Persistence\Modules\Module_Sanidad\Repositories\TriajeRepository.cs
if (pesoDesde.HasValue)
query = query.Where(t => t.peso_kg >= pesoDesde.Value);
if (pesoHasta.HasValue)
query = query.Where(t => t.peso_kg <= pesoHasta.Value);
Mandado B: Regla de Validación de Peso Máximo (FluentValidation)
Ruta: C:\Users\juan9\OneDrive\Desktop\ciclo 7\construncionII\zootech-backend\src\ZooTech.Application\Modules\Module_Sanidad\Validators\CreateTriajeValidator.cs
RuleFor(x => x.PesoKg)
.InclusiveBetween(1m, 1500m)
.WithMessage("El peso registrado debe estar entre 1 kg y 1500 kg.");
// Especificación del Patrón (Validation Pattern)
RuleFor(x => x.PropertyName)
.InclusiveBetween(min, max)
.WithMessage("Validation Error Message");
7. Ejecución de las Suites por Consola (.NET CLI)
Ruta: Consola PowerShell
# 1. Pruebas de Aplicacion (244 tests)
cd "C:\Users\juan9\OneDrive\Desktop\ciclo 7\construncionII\zootech-backend\tests\Unit\ZooTech.Application.UnitTests"
dotnet test
# 2. Pruebas de InterfaceAdapters (47 tests)
cd "C:\Users\juan9\OneDrive\Desktop\ciclo 7\construncionII\zootech-backend\tests\Unit\ZooTech.InterfaceAdapters.UnitTests"
dotnet test