[CAT-001] Flakiness en integration tests por contención de SIGCM2_Test compartida entre proyectos #29

Closed
opened 2026-04-18 22:54:20 +00:00 by dmolinari · 0 comments
Owner

Contexto

Detectado durante CAT-001 Batch 2→3. No introducido por CAT-001 — deuda pre-existente que se hizo visible ahora que el suite corre más rápido y con más datos compartidos.

Síntoma

Correr dotnet test (sin especificar proyecto) produce falsos negativos intermitentes:

Corrida Application.Tests Api.Tests
Individual (dotnet test tests/<proyecto>/) 787/787 230/230
Ambos en paralelo (dotnet test) 786/787 o 787/787 173/230 a 230/230

Los mismos tests pasan o fallan según el timing, sin cambio de código.

Root cause

Dos proyectos de integration tests comparten la misma base física SIGCM2_Test:

  • tests/SIGCM2.Application.Tests (787 tests)
  • tests/SIGCM2.Api.Tests (230 tests)

Cuando dotnet test ejecuta ambos en paralelo (procesos separados), cada uno inicializa su propio Respawner y hace ciclos ResetAsync → Seed → Insert → Assert. El Reset de un proceso borra el seed del otro → asserts como Assert.Equal(25, permisos.Count) ven 0 permisos.

Además del problema inter-proyecto, dentro del mismo proyecto conviven dos patterns de fixture:

  • SqlTestFixture centralizado (usado por TestWebAppFactory)
  • Respawner inline hardcoded en cada test file (6 archivos: MedioRepositoryTests, SeccionRepositoryTests, V009MigrationTests, UsuarioRepositoryTests, UsuarioRepository_PermisosTests, RefreshTokenRepositoryTests)

Esto genera deuda: cada nueva temporal table (como Rubro_History en V016) requiere actualizar el TablesToIgnore en 7 lugares distintos. Misma regla, 7 copias → una se olvida → los 47 tests que vimos.

Impacto

  • Desarrolladores locales: falsos rojos cuando corren dotnet test sin filtrar → pierden tiempo investigando fallos que no existen.
  • CI (si corre en paralelo): los PRs pueden tener "failing checks" fantasma que pasan al rerun.
  • Nuevos UDTs: cada temporal table nueva multiplica el costo de propagación (checklist en engram sig-cm2/conventions/new-temporal-table-test-propagation).

Propuestas (de menos a más invasivo)

Opción 1 — Ejecución secuencial (quick win, ~1h)

Agregar <VSTestCollect>true</VSTestCollect> + flag -m:1 en scripts/CI, o configurar .runsettings con MaxCpuCount=1. Mata el paralelismo inter-proyecto. Pero: lento (suite total duplicado).

Opción 2 — DB separada por proyecto (recomendada, ~2-3h)

  • SIGCM2_Test_App para Application.Tests
  • SIGCM2_Test_Api para Api.Tests
    Cada proyecto inicializa independiente. Sin contención. Escalable.
    Requiere: crear ambas DBs en el server, actualizar connection strings en 8+ test files, aplicar todas las migraciones V001-V016+ a ambas.

Opción 3 — Consolidar en SqlTestFixture (recomendada, ~4-6h)

Eliminar los 6 Respawner inline → todos los test files usan [Collection("Database")] con SqlTestFixture como shared. Una sola lista TablesToIgnore. Cada migración nueva: 1 archivo a tocar (el Ensure del fixture), no 7.

Se puede combinar con Opción 2 para máximo aislamiento.

Opción 4 — Testcontainers (ideal largo plazo, ~1-2 días)

DB efímera por test run con Testcontainers.MsSql. Cero contención, aislamiento total, CI ejecuta sin DB externa. Cambio grande pero elimina toda la infra compartida.

Recomendación

Opciones 2 + 3 combinadas. Opción 2 es mecánica y destraba el CI inmediatamente; Opción 3 es estructural y previene deuda futura. Juntas es 1 día de trabajo bien invertido.

Referencias

  • Engram: sig-cm2/conventions/new-temporal-table-test-propagation — checklist actual para propagar V016-style changes (se vuelve innecesario si aplicamos Opción 3)
  • Engram: sig-cm2/discoveries/sqlserver-collate-order — gotcha paralela encontrada en el mismo batch
  • Commit b1be4a5 — fix manual aplicado para CAT-001 (parche, no solución)
  • Convención DB testing: sig-cm2/conventions/integration-testing-db

Cuándo retomar

Antes de que más UDTs con temporal tables (CAT-002..006, PRD-, VTA-) multipliquen la deuda. Idealmente resolver como parte del próximo "day-off técnico" o entre CAT-001 y CAT-002.

## Contexto Detectado durante CAT-001 Batch 2→3. No introducido por CAT-001 — deuda pre-existente que se hizo visible ahora que el suite corre más rápido y con más datos compartidos. ## Síntoma Correr `dotnet test` (sin especificar proyecto) produce falsos negativos intermitentes: | Corrida | Application.Tests | Api.Tests | |---------|-------------------|-----------| | Individual (`dotnet test tests/<proyecto>/`) | 787/787 ✅ | 230/230 ✅ | | Ambos en paralelo (`dotnet test`) | 786/787 o 787/787 | 173/230 a 230/230 | Los mismos tests pasan o fallan según el timing, sin cambio de código. ## Root cause Dos proyectos de integration tests comparten la **misma base física** `SIGCM2_Test`: - `tests/SIGCM2.Application.Tests` (787 tests) - `tests/SIGCM2.Api.Tests` (230 tests) Cuando `dotnet test` ejecuta ambos en paralelo (procesos separados), cada uno inicializa su propio `Respawner` y hace ciclos `ResetAsync → Seed → Insert → Assert`. El `Reset` de un proceso borra el seed del otro → asserts como `Assert.Equal(25, permisos.Count)` ven 0 permisos. Además del problema inter-proyecto, dentro del mismo proyecto conviven **dos patterns** de fixture: - `SqlTestFixture` centralizado (usado por `TestWebAppFactory`) - `Respawner` inline hardcoded en cada test file (6 archivos: `MedioRepositoryTests`, `SeccionRepositoryTests`, `V009MigrationTests`, `UsuarioRepositoryTests`, `UsuarioRepository_PermisosTests`, `RefreshTokenRepositoryTests`) Esto genera deuda: cada nueva temporal table (como `Rubro_History` en V016) requiere actualizar el `TablesToIgnore` en **7 lugares distintos**. Misma regla, 7 copias → una se olvida → los 47 tests que vimos. ## Impacto - **Desarrolladores locales**: falsos rojos cuando corren `dotnet test` sin filtrar → pierden tiempo investigando fallos que no existen. - **CI (si corre en paralelo)**: los PRs pueden tener "failing checks" fantasma que pasan al rerun. - **Nuevos UDTs**: cada temporal table nueva multiplica el costo de propagación (checklist en engram `sig-cm2/conventions/new-temporal-table-test-propagation`). ## Propuestas (de menos a más invasivo) ### Opción 1 — Ejecución secuencial (quick win, ~1h) Agregar `<VSTestCollect>true</VSTestCollect>` + flag `-m:1` en scripts/CI, o configurar `.runsettings` con `MaxCpuCount=1`. Mata el paralelismo inter-proyecto. **Pero**: lento (suite total duplicado). ### Opción 2 — DB separada por proyecto (recomendada, ~2-3h) - `SIGCM2_Test_App` para Application.Tests - `SIGCM2_Test_Api` para Api.Tests Cada proyecto inicializa independiente. Sin contención. Escalable. Requiere: crear ambas DBs en el server, actualizar connection strings en 8+ test files, aplicar todas las migraciones V001-V016+ a ambas. ### Opción 3 — Consolidar en SqlTestFixture (recomendada, ~4-6h) Eliminar los 6 `Respawner` inline → todos los test files usan `[Collection("Database")]` con `SqlTestFixture` como shared. Una sola lista `TablesToIgnore`. Cada migración nueva: 1 archivo a tocar (el Ensure del fixture), no 7. Se puede combinar con Opción 2 para máximo aislamiento. ### Opción 4 — Testcontainers (ideal largo plazo, ~1-2 días) DB efímera por test run con `Testcontainers.MsSql`. Cero contención, aislamiento total, CI ejecuta sin DB externa. Cambio grande pero elimina toda la infra compartida. ## Recomendación **Opciones 2 + 3 combinadas**. Opción 2 es mecánica y destraba el CI inmediatamente; Opción 3 es estructural y previene deuda futura. Juntas es 1 día de trabajo bien invertido. ## Referencias - Engram: `sig-cm2/conventions/new-temporal-table-test-propagation` — checklist actual para propagar V016-style changes (se vuelve innecesario si aplicamos Opción 3) - Engram: `sig-cm2/discoveries/sqlserver-collate-order` — gotcha paralela encontrada en el mismo batch - Commit `b1be4a5` — fix manual aplicado para CAT-001 (parche, no solución) - Convención DB testing: `sig-cm2/conventions/integration-testing-db` ## Cuándo retomar Antes de que más UDTs con temporal tables (CAT-002..006, PRD-*, VTA-*) multipliquen la deuda. Idealmente resolver como parte del próximo "day-off técnico" o entre CAT-001 y CAT-002.
dmolinari added the followup label 2026-04-18 22:54:20 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dmolinari/SIG-CM2.0#29