Skip to content

Promover staging → master: v0.9.0 + tests + restore GA4#67

Merged
pablotis merged 14 commits intomasterfrom
chore/promote-staging-to-master
May 8, 2026
Merged

Promover staging → master: v0.9.0 + tests + restore GA4#67
pablotis merged 14 commits intomasterfrom
chore/promote-staging-to-master

Conversation

@pablotis
Copy link
Copy Markdown
Collaborator

@pablotis pablotis commented May 8, 2026

Summary

Promoción de staging a producción tras ~4 días de iteración sobre tests + features.

Cambios productivos (v0.9.0 ya documentado en CHANGELOG):

Infra de testing nueva:

  • 185 tests unitarios (Sprint test-1 + test-2): funciones puras, server logic con testServer, fixtures sintéticos.
  • 7 tests E2E con shinytest2 (Sprint test-3 lite): smoke, toggle tipo_duo, render KPI Calidad. Guard RUN_E2E=true para no inflar el ciclo dev.
  • 2 workflows CI: tests-unit.yml (cada PR) + tests-e2e.yml (manual + cron semanal).

Fix crítico:

Test plan

🤖 Generated with Claude Code

pablotis and others added 14 commits May 4, 2026 10:40
…e 3)

Cierra Sprint A (#44 Tipo de dúo end-to-end). El toggle ahora cubre
toda la app: Foto, Película, Tasas, Calidad de la muestra y la
sección Datos descargables.

Calidad de la muestra
  - regenerar_calidad_panel() acepta parámetro window. En anual los
    dúos van T_n año X → T_n año X+1, periodo "YYYY_tN".
  - Detección de inconsistencia de edad usa rango [CH06, CH06 + 2]
    en anual (vs +1 en trimestral) para reflejar cumpleaños entre
    los dos años consecutivos.
  - Nuevo CSV data_output/calidad_panel_anual_pct_historico.csv
    (78 filas).
  - mod_calidad_panel_server recibe tipo_duo y usa el dataset
    correspondiente. Selector "duplas" se adapta al modo:
    1→2 / 2→3 / 3→4 / 4→1 ↔ T1 vs T1 / ... / T4 vs T4.
  - duo_label() acepta window: en anual devuelve "tN" en lugar
    de "tN-tM".

Sección Datos
  - Nueva tarjeta "Panel longitudinal · interanual" al lado de la
    intertrimestral. Mismo estilo card + dropdown.
  - Botones de descarga: Parquet (16 MB) y CSV gzip (18 MB).
  - Tracking GA4 con dataset = "panel_runtime_anual".
  - Handlers en app.R servirán los archivos generados por
    ETL/09b-build_paneles_runtime_anual.R (ya existentes desde v0.7.0).

ETL/11-build_historicos_anuales.R
  - Sumado paso para regenerar_calidad_panel(window = "anual").

Bump v0.9.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sumar la primera capa de tests al proyecto. Stack confirmado por
research técnico: testthat 3.x + shiny::testServer() + shinytest2.
Este commit cierra Sprint test-1 parcial: setup + fixture + 4
archivos de tests con 42 tests pasando.

Setup
  tests/testthat.R: runner principal que sourcea ETL y llama
  testthat::test_dir(). NO sourcea 01-extract.R (datasets reales)
  para que los tests sean rápidos.
  tests/testthat/helper-fixtures.R: helper load_panel_mock() para
  cargar el fixture sintético desde dentro de los tests.
  tests/testthat/fixtures/_generar_fixtures.R: generador del
  fixture (100 individuos × 3 ondas trimestrales con CODUSU
  controlado, distribuciones EPH realistas, semilla fija).
  tests/testthat/fixtures/panel_mock.rds: el fixture en sí
  (versionado para reproducibilidad de tests sin tener que
  re-generar localmente).

Tests
  test-agrega_vars_derivadas.R (6 tests):
    - Schema correcto post-función
    - Reglas de formalidad clásica (asalariados con/sin PP07H)
    - Reglas de formalidad ampliada (cuenta propia + monotributo,
      patrones, TFSR siempre informal)
    - Defensive: completa cols faltantes con NA, no rompe
    - Preserva columnas originales
    - Invariante de rango: solo 1L, 2L o NA
  test-duos_disponibles_por_anio.R (6 tests):
    - Trimestral con periodos completos
    - Trimestral filtra cuando falta extremo
    - Anual labels T1/T2/T3/T4
    - Anual sin t+1 disponible → vacío
    - Anual parcial (solo algunos trim de t+1)
    - Default window = trimestral
  test-armo_tabla_sankey.R (4 tests):
    - Defensive con tabla vacía (regresión hotfix v0.7.0)
    - Schema esperado en output
    - Filtro por categoría con periodo_base = t_anterior
    - Idem con t_posterior
  test-duo_label.R (2 tests):
    - Construye "tN-tM" formato intertrim
    - Vectorizado

Resultado
  [ FAIL 0 | WARN 0 | SKIP 0 | PASS 42 ]

Pendientes Sprint test-1
  Tests sobre arma_tasas_destacadas, regenerar_panel_historico
  (con fixture controlado para validar tasas a mano), armo_base_panel
  modo legacy. CI GitHub Actions tests-unit.yml.

Sprint test-2 y test-3 quedan registrados en ROADMAP.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
v0.9.0: Calidad + Datos descargables en modo Interanual (#47 Fase 3)
chore: setup base de testing (Sprint test-1, #61)
…-1, #61)

Cierra Sprint test-1 con cobertura amplia de funciones puras y CI.
79 tests pasando.

Tests nuevos
  test-arma_tasas_destacadas.R (6 tests):
    Casos controlados con panel mock pequeño donde las tasas se
    calculan a mano:
      - Persistencia 80% / Salida 20% / Entrada 27.3%
      - Caso 100% persistencia (todos siguen)
      - Caso 0% persistencia (todos transitan)
      - Categoría sin presencia → 0/100/100
      - Variable distinta de ESTADO (CAT_OCUP)
      - Schema del output (list de 3 numéricos)

  test-regenerar_panel_historico.R (6 tests):
    Usa withr::with_tempdir() para no tocar data_output/.
      - Trimestral genera CSV con schema esperado
      - Trimestral usa formato de período "YYYY_tA-tB"
      - Anual usa formato "YYYY_tN" (sin -tM)
      - Idempotencia: 2da corrida no agrega duplicados
      - Tipos de columnas (weight numérico, periodo character)
      - Invariante: weight ∈ [0, 100]

  test-duo_label.R extendido (5 tests, era 2):
    Ahora que v0.9.0 está mergeado y la fn acepta `window`:
      - trimestral: "tN-tM"
      - anual: "tN"
      - default trimestral
      - vectorizado en ambos modos

CI
  .github/workflows/tests-unit.yml:
    Corre Rscript tests/testthat.R en cada push y PR a master/staging.
    Cache de paquetes R via setup-r-actions. ~2-3 min de duración.
    Instala solo lo necesario (sin highcharter/gt/waiter UI-only).

Resultado
  [ FAIL 0 | WARN 0 | SKIP 0 | PASS 79 ]

Sprint test-1 cerrado. Próximo: Sprint test-2 (testServer).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Causa
  El job 'unit-tests' del PR #63 falló con "there is no package
  called 'highcharter'". El runner no tenía highcharter instalado
  porque el workflow lo había excluido (UI-only), pero
  tests/testthat.R sourceaba ETL/00-libraries.R que carga
  library(highcharter) explícitamente.

Fix
  tests/testthat.R ahora carga solo los paquetes mínimos necesarios
  (dplyr, tidyr, tibble, eph, arrow, glue) y NO sourcea 00-libraries.R.
  Las funciones de R/utils_analisis.R que usan highcharter::hchart()
  internamente (arma_line_chart_areaspline) se DEFINEN al sourcear
  el archivo pero no se EJECUTAN, así que no requieren el paquete
  hasta que un test específico las invoque.

  Comentario actualizado en el workflow YAML para documentar la
  decisión.

Validación local
  [ FAIL 0 | WARN 0 | SKIP 0 | PASS 79 ]

Cuando arranque Sprint test-2 con tests de UI/server (testServer +
shinytest2), evaluar si conviene un runner separado con su propia
lista de paquetes (highcharter, gt, etc).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
chore: cerrar Sprint test-1 (#61) con tests de tasas + paneles históricos + CI
Cobertura de funciones puras restantes del Sprint test-1:

- arma_matriz_transicion (R/utils_analisis.R): 6 tests para matriz NxN
  (estructura, suma 100% por fila, orden de etiquetas, manejo de
  categorías ausentes, soporte CAT_OCUP).
- build_tasas_historico (ETL/99-functions.R): 8 tests para output
  trimestral/anual, invariantes (persistencia + salida = 100, tasas
  en [0, 100]), filtro desde_panel, vars_extra.
- regenerar_calidad_panel (ETL/99-functions.R): 6 tests con
  with_tempdir() para schema, formato de periodos, idempotencia,
  % en rango, n_panel <= n_t0.
- formato_delta (R/utils_analisis.R): 4 tests para flecha + signo
  + 'sin comparación' en NA/NULL.
- sankey_label_legible (R/utils_analisis.R): 5 tests para mapeo de
  ESTADO/CAT_OCUP/formalidad, vectorización, fallback.
- sankey_nodes_orden (R/utils_analisis.R): 3 tests para estructura
  de columna y orden no-alfabético.

Quedan fuera del Sprint test-1: armo_base_panel modo legacy (mejor
cubrirlo en Sprint test-2 junto con runtime mode usando testServer).

Suite completa: 149 tests PASS · 0 fail · 1m local.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
test: Sprint test-1 batch 3 — +70 tests (total 149)
Cobertura de la capa server-side:

- mod_calidad_panel_server (testServer): 9 tests para los reactives
  expuestos por el módulo. Switch del dataset según tipo_duo (trim ↔
  anual), filtrado por rango de años y dúos, outputs KPI calculando
  promedios sobre el filtro activo. Mock de globals (df_calidad_panel
  + df_calidad_panel_anual) y stub global de renderHighchart para no
  exigir el paquete en CI.

- armo_base_panel(window = "anual") (sin testServer): 6 tests con un
  parquet sintético en with_tempdir(). Verifica filter pushdown sobre
  (anio_0, trim_0), drop de esas cols del output, error informativo
  si no existe el parquet, validación de window inválido. Cubre el
  hotfix v0.7.3 (open_dataset vs read_parquet) sin tocar el archivo
  de producción.

mod_analisis_*_server diferidos a Sprint test-3 (E2E con shinytest2):
requieren mockear ~6 globals por módulo y el ROI es bajo vs probar
el flujo real desde browser.

Suite completa: 185 tests PASS · 0 fail.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
test: Sprint test-2 — server logic + armo_base_panel anual
Versión recortada del Sprint test-3 (scope inicial 5-7 tests + Codecov)
con foco en cubrir el smoke y la regresión del toggle Tipo de dúo. ROI
suficiente para el costo de mantenimiento de E2E.

Tests cubiertos:
- smoke: app levanta y registra el input tipo_duo con default
  "trimestral".
- toggle Tipo de dúo: cambiar trimestral ↔ anual se refleja en el
  reactive state via input. Cubre regresión #44.
- módulo Calidad: tras navegar al nav_panel, el output kpi_encontrado
  renderiza un valor numérico válido (forma "X.X%" o "—").

Infraestructura:
- Workflow tests-e2e.yml separado con workflow_dispatch + cron
  semanal (domingo 06:00 UTC). NO corre en cada PR. tests-unit.yml
  sigue siendo la barrera obligatoria.
- Guard RUN_E2E=true env var: la suite default (Rscript tests/testthat.R)
  salta los E2E manteniéndose rápida (~30s vs +3min).
- Helper new_app() encapsula AppDriver$new + skips defensivos
  (paquetes ausentes, datasets faltantes, env var).

Diferidos a futuro (documentados en ROADMAP):
- E2E de descarga (quirk de Chromote con downloadHandler que copia
  archivos vía file.copy).
- Codecov.
- E2E de regresión #40 line charts (requiere snapshot testing).

Suite total: 192 tests (185 unit + 7 E2E con RUN_E2E=true).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
test: Sprint test-3 lite — 3 tests E2E con shinytest2
Master HEAD venía con GA4_MEASUREMENT_ID <- "" desde el merge de PR #50
(staging → master, 2026-05-03). Ese merge pisó el ID válido de prod
(commit f25f900) sin que se aplicara el git checkout master -- previsto
en el procedimiento de promoción. Resultado: GA4 está roto en producción
desde 2026-05-03 (4 días sin tracking).

Restauro el ID. Al hacerse en este PR de promoción staging → master,
queda atómico: el merge trae las features + tests Y vuelve el tracking
en el mismo deploy.

NOTA: Este archivo seguirá siendo el conflict point en futuros
staging → master. Considerar git attribute merge=ours en master, o
extraer GA4_MEASUREMENT_ID a un archivo separado que solo cambie en
master (mejora futura, no implementada).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@pablotis pablotis merged commit f391e67 into master May 8, 2026
1 check passed
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