hbox-docs/backend/tests-1013.sh

101 lines
10 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# Pruebas del backend 13-oct: arranca app.py con carpetas temporales y ejercita cada endpoint nuevo.
# Uso: bash tests-1013.sh <ruta-a-hbox-docs> [puerto]
set -u
REPO=${1:?ruta a hbox-docs}; PORT=${2:-8098}
T=$(mktemp -d); export ACTAS_DIR=$T/actas PORT
mkdir -p $ACTAS_DIR
python3 $REPO/backend/app.py > $T/app.log 2>&1 &
APP=$!; trap "kill $APP 2>/dev/null" EXIT; sleep 1.5
U="http://127.0.0.1:$PORT"
LEO=(-s -H "X-Remote-User: leo" -H "Content-Type: application/json")
ALAN=(-s -H "X-Remote-User: alan" -H "Content-Type: application/json")
ok=0; fail=0
check() { # check "nombre" "esperado" "obtenido"
if [[ "$3" == *"$2"* ]]; then ok=$((ok+1)); echo " ✓ $1"; else fail=$((fail+1)); echo " ✕ $1 — esperaba «$2», obtuve: ${3:0:160}"; fi; }
echo "## salud y endpoints viejos"
check "salud" '"servicio":"actas-hbox"' "$(curl -s $U/api/salud)"
check "whoami leo admin" '"admin":true' "$(curl "${LEO[@]}" $U/api/whoami)"
check "actas vacías" '"total":0' "$(curl -s $U/api/actas)"
echo "## tareas"
check "sin usuario → 403" "No puedo identificarte" "$(curl -s -H 'Content-Type: application/json' -X POST $U/api/tareas -d '{"titulo":"x"}')"
check "POST (alan)" '"id":"t-a"' "$(curl "${ALAN[@]}" -X POST $U/api/tareas -d '{"id":"t-a","titulo":"Pendiente A","pista":"ingenieria","responsable":"michelle","fecha_limite":"2026-10-01","version_tarjeta":"V5.6","enlaces":[{"titulo":"ECO","url":"/hardware/v56-eco/"}]}')"
check "POST id duplicado → 409" '"existe":true' "$(curl "${ALAN[@]}" -X POST $U/api/tareas -d '{"id":"t-a","titulo":"dup","responsable":"leo"}')"
check "POST pista inválida → 400" "Pista inv" "$(curl "${ALAN[@]}" -X POST $U/api/tareas -d '{"titulo":"x","pista":"ventas","responsable":"leo"}')"
check "POST fecha inválida → 400" "AAAA-MM-DD" "$(curl "${ALAN[@]}" -X POST $U/api/tareas -d '{"titulo":"x","fecha_limite":"13/10/2026","responsable":"leo"}')"
curl "${LEO[@]}" -X POST $U/api/tareas -d '{"id":"t-b","titulo":"Pendiente B","pista":"compras","responsable":"leo","bloqueada_por":["t-a"]}' > /dev/null
check "GET filtro pista" '"total":1' "$(curl -s "$U/api/tareas?pista=compras")"
check "GET filtro responsable" '"total":1' "$(curl -s "$U/api/tareas?responsable=mich")"
check "vencida calculada" '"vencida":true' "$(curl -s "$U/api/tareas?pista=ingenieria")"
check "PATCH mover (alan) → historial con quién" '"quien":"alan"' "$(curl "${ALAN[@]}" -X PATCH "$U/api/tarea?id=t-a" -d '{"estado":"en-curso","orden":0}')"
check "PATCH sin cambios" '"sin_cambios":true' "$(curl "${ALAN[@]}" -X PATCH "$U/api/tarea?id=t-a" -d '{"estado":"en-curso"}')"
check "PATCH estado inválido → 400" "Estado inv" "$(curl "${ALAN[@]}" -X PATCH "$U/api/tarea?id=t-a" -d '{"estado":"listo"}')"
check "PATCH inexistente → 404" "No existe" "$(curl "${ALAN[@]}" -X PATCH "$U/api/tarea?id=nada" -d '{"titulo":"x"}')"
check "POST orden (renumera columna: t-b cambia de columna, t-a de posición)" '"actualizadas":2' "$(curl "${ALAN[@]}" -X POST $U/api/tareas/orden -d '{"estado":"en-curso","ids":["t-b","t-a"]}')"
check "resumen" '"en-curso": 1' "$(curl -s $U/api/tareas/resumen | python3 -c 'import sys,json; print(json.dumps(json.load(sys.stdin)["por_pista"]["ingenieria"]))')"
check "resumen vencidas" '"id":"t-a"' "$(curl -s $U/api/tareas/resumen)"
check "DELETE alan → 403" "Solo un administrador" "$(curl "${ALAN[@]}" -X DELETE "$U/api/tarea?id=t-b")"
check "DELETE leo → ok" '"borrado":"t-b"' "$(curl "${LEO[@]}" -X DELETE "$U/api/tarea?id=t-b")"
check "índice CSV escrito" "t-a" "$(cat $T/tareas/_indice.csv)"
echo "## plan"
check "GET sin archivo → por omisión" '"_default":true' "$(curl -s $U/api/plan)"
check "PUT alan → 403" "Solo un administrador" "$(curl "${ALAN[@]}" -X PUT $U/api/plan -d '{"pedido_total":570}')"
check "PUT leo" '"actualizado_por":"leo"' "$(curl "${LEO[@]}" -X PUT $U/api/plan -d '{"pedido_total":570,"versiones":[{"version":"V5.6","objetivo":113,"serie_prefijo":"V56","fecha":"2026-11-30"},{"version":"V5.7","objetivo":327},{"version":"v6","objetivo":130}]}')"
check "PUT conserva etapas" '"clave":"instalacion"' "$(curl -s $U/api/plan)"
check "PUT sin versiones → 400" "al menos una versi" "$(curl "${LEO[@]}" -X PUT $U/api/plan -d '{"versiones":[]}')"
echo "## inventario"
check "POST ítem (alan)" '"id":"EB-V56"' "$(curl "${ALAN[@]}" -X POST $U/api/inventario -d '{"codigo":"EB-V56","descripcion":"EinarBoard V5.6","categoria":"tarjeta","aplica_a":["V5.6"],"minimo":5}')"
check "POST código duplicado → 409" '"existe":true' "$(curl "${ALAN[@]}" -X POST $U/api/inventario -d '{"codigo":"eb-v56","descripcion":"dup"}')"
check "POST categoría inválida → 400" "inv\\u00e1lida: tornillo" "$(curl "${ALAN[@]}" -X POST $U/api/inventario -d '{"codigo":"X1","descripcion":"x","categoria":"tornillo"}')"
check "POST con stock_inicial → entrada" '"stock": 7' "$(curl "${ALAN[@]}" -X POST $U/api/inventario -d '{"codigo":"SD-15B-05","descripcion":"Mean Well","categoria":"fuente","minimo":113,"stock_inicial":7}' | python3 -c 'import sys,json; print(json.dumps(json.load(sys.stdin)["item"]))')"
check "salida sin stock → 409" "No hay stock suficiente" "$(curl "${ALAN[@]}" -X POST $U/api/inventario/movimiento -d '{"item_id":"EB-V56","tipo":"salida","cantidad":3,"motivo":"ensamble"}')"
check "entrada" '"stock_despues":10' "$(curl "${ALAN[@]}" -X POST $U/api/inventario/movimiento -d '{"item_id":"EB-V56","tipo":"entrada","cantidad":10,"motivo":"llegó pedido","referencia":"OC-1"}')"
check "salida" '"stock_despues":7' "$(curl "${ALAN[@]}" -X POST $U/api/inventario/movimiento -d '{"item_id":"EB-V56","tipo":"salida","cantidad":3,"motivo":"ensamble","referencia":"HBOX5-E01-A001-R0001"}')"
check "ajuste (conteo 4 → delta −3)" '"delta":-3' "$(curl "${ALAN[@]}" -X POST $U/api/inventario/movimiento -d '{"item_id":"EB-V56","tipo":"ajuste","cantidad":4,"motivo":"conteo físico"}')"
check "sin motivo → 400" "Escribe el motivo" "$(curl "${ALAN[@]}" -X POST $U/api/inventario/movimiento -d '{"item_id":"EB-V56","tipo":"entrada","cantidad":1}')"
check "alerta = stock < mínimo" '"alerta": true' "$(curl -s "$U/api/inventario?alertas=1" | python3 -c 'import sys,json; j=json.load(sys.stdin); print(json.dumps([i for i in j["items"] if i["id"]=="EB-V56"]))')"
check "PATCH stock a mano → 400" "no se edita a mano" "$(curl "${ALAN[@]}" -X PATCH "$U/api/inventario/item?id=EB-V56" -d '{"stock":99}')"
check "PATCH ubicación → historial" "ubicacion:" "$(curl "${ALAN[@]}" -X PATCH "$U/api/inventario/item?id=EB-V56" -d '{"ubicacion":"Anaquel A2","minimo":3}')"
check "movimientos del ítem" '"total":3' "$(curl -s "$U/api/inventario/movimientos?item_id=EB-V56")"
check "export CSV" 'codigo,descripcion' "$(curl -s $U/api/inventario/export.csv)"
check "export CSV cabecera" 'text/csv' "$(curl -s -D - -o /dev/null $U/api/inventario/export.csv)"
echo "## sitios"
check "POST alan → 403" "Solo un administrador" "$(curl "${ALAN[@]}" -X POST $U/api/sitios -d '{"id":"icave","nombre":"ICAVE"}')"
check "POST leo" '"id":"icave"' "$(curl "${LEO[@]}" -X POST $U/api/sitios -d '{"id":"icave","nombre":"ICAVE","cliente":"Hutchison Ports","ubicacion":"Veracruz","alias":["ICAVE (Veracruz)"],"contactos":[{"nombre":"Abraham","rol":"supervisor"}]}')"
check "POST duplicado → 409" '"existe":true' "$(curl "${LEO[@]}" -X POST $U/api/sitios -d '{"id":"icave","nombre":"ICAVE"}')"
check "PATCH alan → 403" "Solo un administrador" "$(curl "${ALAN[@]}" -X PATCH "$U/api/sitio?id=icave" -d '{"ubicacion":"x"}')"
check "PATCH leo → historial" "ubicacion: Veracruz" "$(curl "${LEO[@]}" -X PATCH "$U/api/sitio?id=icave" -d '{"ubicacion":"Veracruz, Ver."}')"
check "GET sitios" '"nombre":"ICAVE"' "$(curl -s $U/api/sitios)"
curl "${ALAN[@]}" -X POST $U/api/actas -d '{"tipo":"instalacion-campo","serie":"HBOX5-E01-A001-R0001","tecnico":"Alan","conforme":true,"resultado":"OK","fecha":"2026-10-10","sitio":"icave","maquina_id":"FL-JUN-060","accion":"Instalada y reportando"}' > /dev/null
curl "${ALAN[@]}" -X POST $U/api/actas -d '{"tipo":"diagnostico-campo","serie":"RTK-014","tecnico":"Alan","conforme":true,"resultado":"FALLA","fecha":"2026-10-11","terminal":"ICAVE","maquina_id":"FL-JUN-060"}' > /dev/null
curl "${ALAN[@]}" -X POST $U/api/actas -d '{"tipo":"dano","serie":"RTK-014","tecnico":"Alan","conforme":true,"resultado":"REGISTRADO","fecha":"2026-10-11","sitio":"icave","clasificacion":"Agua","accion":"Marcada como dañada","resumen":"agua adentro"}' > /dev/null
curl "${ALAN[@]}" -X POST $U/api/actas -d '{"tipo":"envio","serie":"HBOX5-E02-A002-R0002","tecnico":"Alan","conforme":true,"resultado":"ENVIADA","fecha":"2026-10-12","clasificacion":"ICAVE (Veracruz)","accion":"Enviada"}' > /dev/null
R=$(curl -s "$U/api/sitio/unidades?id=icave")
check "unidades del sitio" '"total_unidades":3' "$R"
check "coincidencia por slug" '"coincidencia":"sitio"' "$R"
check "coincidencia por texto (terminal/destino)" '"coincidencia":"texto"' "$R"
check "instalada con fecha" '"instalada":"2026-10-10"' "$R"
check "marca de daño en la unidad" '"marca":"DANADA"' "$R"
check "sitio inexistente → 404" "No existe el sitio" "$(curl -s "$U/api/sitio/unidades?id=nada")"
check "/api/unidad trae sitio" '"sitio":"icave"' "$(curl -s "$U/api/unidad?serie=HBOX5-E01-A001-R0001")"
check "/api/actas trae sitio" '"sitio":"icave"' "$(curl -s "$U/api/actas")"
echo "## siembra (idempotente)"
S1=$(python3 $REPO/backend/seed_1013.py --usuario leo 2>&1)
check "primera corrida crea" "pendientes (seed_1013): 13 creados" "$S1"
check "sitios: 3 nuevos (icave ya existía)" "sitios: 3 creados, 1 ya existían" "$S1"
S2=$(python3 $REPO/backend/seed_1013.py --usuario leo 2>&1)
check "segunda corrida no duplica" "13 ya existían" "$S2"
check "inventario no duplica" "0 ítems creados" "$S2"
echo '[{"id":"t-notion-1","titulo":"Importado de Notion","pista":"ingenieria","estado":"hecho"}]' > $T/extra.json # sin responsable: el seed pone el de omisión (leo)
check "--extra importa" "extra.json): 1 creados" "$(python3 $REPO/backend/seed_1013.py --solo nada --extra $T/extra.json 2>&1)"
check "resumen tras siembra (1 + 13 sembrados + 1 extra)" '"total":15' "$(curl -s $U/api/tareas/resumen)"
echo; echo "RESULTADO: $ok correctas · $fail fallidas · datos en $T"
[ $fail -eq 0 ]