# Pruebas del 13-oct — backend (tareas, plan, inventario, sitios) y páginas Todo se corrió en el sandbox contra `backend/app.py` arrancado con carpetas temporales (`ACTAS_DIR=/actas`; las demás colecciones se deducen de ahí). Sin nginx: el usuario se inyecta con la cabecera `X-Remote-User` tal como la fija nginx en producción. Nada de lo de abajo toca el servidor real. ## 1 · Backend — `backend/tests-1013.sh` (61 comprobaciones) El script arranca `app.py` en un puerto libre, ejercita cada endpoint nuevo con `curl` como **leo** (admin) y como **alan** (técnico), corre la siembra dos veces y una importación `--extra`. Se puede repetir en cualquier máquina con Python 3 + Flask: ```bash bash backend/tests-1013.sh /ruta/a/hbox-docs 8098 ``` Resultado de la corrida (13-oct-2026): ``` ## salud y endpoints viejos ✓ salud ✓ whoami leo admin ✓ actas vacías ## tareas ✓ sin usuario → 403 ✓ POST (alan) ✓ POST id duplicado → 409 ✓ POST pista inválida → 400 ✓ POST fecha inválida → 400 ✓ GET filtro pista ✓ GET filtro responsable ✓ vencida calculada ✓ PATCH mover (alan) → historial con quién ✓ PATCH sin cambios ✓ PATCH estado inválido → 400 ✓ PATCH inexistente → 404 ✓ POST orden (renumera columna: t-b cambia de columna, t-a de posición) ✓ resumen ✓ resumen vencidas ✓ DELETE alan → 403 ✓ DELETE leo → ok ✓ índice CSV escrito ## plan ✓ GET sin archivo → por omisión ✓ PUT alan → 403 ✓ PUT leo ✓ PUT conserva etapas ✓ PUT sin versiones → 400 ## inventario ✓ POST ítem (alan) ✓ POST código duplicado → 409 ✓ POST categoría inválida → 400 ✓ POST con stock_inicial → entrada ✓ salida sin stock → 409 ✓ entrada ✓ salida ✓ ajuste (conteo 4 → delta −3) ✓ sin motivo → 400 ✓ alerta = stock < mínimo ✓ PATCH stock a mano → 400 ✓ PATCH ubicación → historial ✓ movimientos del ítem ✓ export CSV ✓ export CSV cabecera ## sitios ✓ POST alan → 403 ✓ POST leo ✓ POST duplicado → 409 ✓ PATCH alan → 403 ✓ PATCH leo → historial ✓ GET sitios ✓ unidades del sitio ✓ coincidencia por slug ✓ coincidencia por texto (terminal/destino) ✓ instalada con fecha ✓ marca de daño en la unidad ✓ sitio inexistente → 404 ✓ /api/unidad trae sitio ✓ /api/actas trae sitio ## siembra (idempotente) ✓ primera corrida crea ✓ sitios: 3 nuevos (icave ya existía) ✓ segunda corrida no duplica ✓ inventario no duplica ✓ --extra importa ✓ resumen tras siembra (1 + 12 sembrados + 1 extra) RESULTADO: 61 correctas · 0 fallidas · datos en ``` Qué cubre, por colección: | Colección | Probado | |---|---| | `tareas` | 403 sin usuario; POST con y sin `id`; 409 por id duplicado; 400 por pista/estado/fecha inválidos; filtros `pista`, `responsable`; `vencida`; PATCH que mueve de columna y deja `historial` con **quién**; PATCH sin cambios; 404; `POST /api/tareas/orden`; `resumen` (conteo y vencidas); DELETE 403 para técnico y ok para admin; `_indice.csv` | | `plan` | GET devuelve el plan por omisión (`_default`) sin archivo; PUT 403 para técnico; PUT admin guarda `actualizado_por` y conserva las `etapas`; 400 sin versiones | | `inventario` | POST ítem; 409 código duplicado (insensible a mayúsculas); 400 categoría inválida; `stock_inicial` → entrada; salida mayor al stock → 409; entrada/salida/ajuste con `delta` y `stock_despues`; 400 sin motivo; `alerta` = stock < mínimo; PATCH `stock` rechazado; PATCH con historial; listado de movimientos; CSV con cabecera `text/csv` | | `sitios` | POST/PATCH 403 para técnico; POST admin; 409 duplicado; PATCH con historial; GET; `/api/sitio/unidades` con coincidencia por slug (`sitio`) y por texto (`terminal`, destino de envío), fecha de instalación, marca de daño; 404 | | actas existentes | `/api/actas` y `/api/unidad` ahora traen `sitio` (y `fecha`, `componentes` en las propias) sin romper lo demás | | `seed_1013.py` | primera corrida crea plan, 4 sitios, 18 ítems, 12 pendientes; la segunda no duplica nada; `--extra archivo.json` importa pendientes adicionales (Notion) de forma idempotente | Además, antes del script: `python3 -m py_compile backend/app.py` sin errores; los endpoints viejos (`/api/salud`, `/api/whoami`, `/api/actas`, `/api/acta`, `/api/adjuntos`, `/api/unidad`, `/api/series`, `/api/estado`, `/api/danos`) se usaron desde las páginas durante las pruebas de navegador sin cambios de comportamiento. ## 2 · Páginas — navegador real (Chromium headless con Playwright) Se sirvió `docs/` tal cual con un proxy que reenvía `/api/` al backend y pone `X-Remote-User` (un servidor como **leo** y otro como **alan**). Pruebas en teléfono (390×844, táctil) y escritorio. | Página | Probado | Resultado | |---|---|---| | `tableros/kanban.html` | carga 12 pendientes sembrados; en teléfono se ve «Mover a…» y mueve de columna; el historial del detalle dice `alan · estado: por-hacer → en-curso`; alan no ve «Borrar»; crear pendiente con fecha pasada → tarjeta roja (vencida); filtro por pista; en escritorio **arrastrar y soltar** a Hecho; leo borra con confirmación | ✓ sin errores de consola | | `tableros/ingenieria.html` | 4 tarjetas de versión con enlaces; pendientes filtrados a Ingeniería; contador por versión | ✓ | | `tableros/produccion.html` | KPIs, plan vs real, embudo, bloqueos, alertas, daños; leo ve «Editar plan» y guarda una fecha objetivo; alan no ve el botón | ✓ | | `inventario/inventario.html` | 18 ítems; ajuste a 15 desde el diálogo; salida de 99 → error visible «No hay stock suficiente»; historial del ítem; nuevo ítem con stock inicial 3; filtro «solo alertas»; export CSV con `Content-Disposition` | ✓ (el único mensaje de consola es el 409 esperado) | | `formularios/instalacion.html` | lista de sitios desde `/api/sitios`; técnico precargado; unidad «otra»; exige las 3 fotos obligatorias; con fotos subidas envía: «Registro enviado … con 3 foto(s)»; segunda visita muestra el historial | ✓ | | `formularios/envio.html` · `dano.html` | el select de sitio existe; en daño solo aparece al elegir «En campo / transporte / no se sabe» | ✓ | | `sitios/sitio.html` | LZC muestra la unidad recién instalada; ICAVE muestra RTK-014 «por texto» (acta vieja sin sitio) y DAÑADA; alan no ve «Editar sitio»; leo edita ubicación + contacto y crea un sitio nuevo desde el selector | ✓ | | `unidad.html` | bloque «Trazabilidad»: cadena de producción (módulos deducidos del número de unidad cuando no hay ensamble) y cadena de campo con sitio y fecha | ✓ | Artefactos de Playwright (no van al repo): capturas `kanban-leo.png`, `produccion.png`, `inventario-movil.png`, `instalacion-movil.png`, `sitio-icave.png`, `unidad.png`. Dos falsos negativos que salieron en el camino y son del arnés de prueba, no de las páginas: (1) Playwright hace scroll entre `mousedown` y `dragstart`, así que con una ventana corta arrastraba la tarjeta equivocada — se resolvió con una ventana alta; (2) disparar `change` sintético en el campo «otra unidad» y luego el `change` nativo al perder el foco hacía dos `loadSerie()` — se resolvió haciendo blur normal (Tab), como lo haría un usuario. ## 3 · Sitio estático - `mkdocs build --strict` sobre una copia: **55 advertencias, las mismas 55 de antes** (PDF y binarios que no existen en esta copia). Ninguna menciona `tableros/`, `inventario/`, `sitios/`, `instalacion.html` ni `unidad.html`. Las tres páginas `.md` nuevas se construyen (quedan fuera del `nav` hasta que se aplique la nueva navegación; ver `NUEVOS-1013.md`). - Comprobación de referencias en los HTML nuevos y modificados (todo `src`/`href` relativo y los enlaces `../hardware/…/` dentro del JS): **0 rotas**. - `node --check` sobre `tableros/kanban.js` y `formularios/acta.js`: sin errores de sintaxis. - El `