Files
dh-inspeccion-v2/scripts/phase-d4-acceptance.sh

178 lines
8.0 KiB
Bash
Executable File

#!/usr/bin/env bash
set -Eeuo pipefail
dhv2_base_url="${DHV2_BASE_URL:-https://dhv2.korexlabs.com}"
dhv2_base_url="${dhv2_base_url%/}"
dhv2_nil_uuid="00000000-0000-4000-8000-000000000000"
fail() {
echo "ERROR: $*" >&2
exit 1
}
echo "========== D4 · MIGRACIONES =========="
dhv2_migration_status="$(docker compose --profile tools run --rm migrate npm run migration:show 2>&1)"
grep -q 'Pending migrations: no' <<<"$dhv2_migration_status" \
|| fail "Quedaron migraciones pendientes"
echo "Migraciones pendientes: no"
echo
echo "========== D4 · HEALTH =========="
dhv2_internal="$(curl -fsS --connect-timeout 10 --max-time 30 http://127.0.0.1:3101/api/v3/health)"
dhv2_external="$(curl -fsS --connect-timeout 10 --max-time 30 "$dhv2_base_url/api/v3/health")"
grep -q '"version":"0.18.0"' <<<"$dhv2_internal" || fail "Health interno no informa v0.18.0"
grep -q '"phase":"D4"' <<<"$dhv2_internal" || fail "Health interno no informa D4"
grep -q '"version":"0.18.0"' <<<"$dhv2_external" || fail "Health externo no informa v0.18.0"
grep -q '"database":"ok"' <<<"$dhv2_external" || fail "La base no está saludable"
echo "$dhv2_external"
echo
echo "========== D4 · RUTAS PROTEGIDAS =========="
for dhv2_path in \
"inspection-findings/$dhv2_nil_uuid/evidence" \
"inspection-findings/$dhv2_nil_uuid/communications" \
"inspection-finding-evidence/$dhv2_nil_uuid/content"; do
dhv2_code="$(curl -sS --connect-timeout 10 --max-time 30 \
-o /tmp/dhv2-d4-response.json -w '%{http_code}' \
"$dhv2_base_url/api/v3/$dhv2_path")"
test "$dhv2_code" = "401" \
|| fail "/api/v3/$dhv2_path devolvió HTTP $dhv2_code; se esperaba 401"
echo "/api/v3/$dhv2_path sin sesión: 401 OK"
done
rm -f /tmp/dhv2-d4-response.json
echo
echo "========== D4 · MODELO INMUTABLE =========="
dhv2_db_check="$(docker compose exec -T db sh -lc \
'psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -v ON_ERROR_STOP=1 -At' <<'SQL'
SELECT 'tables', COUNT(*)
FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name IN (
'inspection_finding_evidence',
'inspection_finding_communications'
);
SELECT 'communication_checks', COUNT(*)
FROM pg_constraint
WHERE conname IN (
'chk_inspection_finding_communications_direction',
'chk_inspection_finding_communications_channel',
'chk_inspection_finding_communications_type',
'chk_inspection_finding_communications_subject',
'chk_inspection_finding_company_response_direction'
);
SELECT 'evidence_checks', COUNT(*)
FROM pg_constraint
WHERE conname IN (
'chk_inspection_finding_evidence_kind',
'chk_inspection_finding_evidence_purpose',
'chk_inspection_finding_evidence_source',
'chk_inspection_finding_evidence_mime',
'chk_inspection_finding_evidence_size',
'chk_inspection_finding_evidence_sha256',
'chk_inspection_finding_evidence_coordinates',
'chk_inspection_finding_evidence_accuracy',
'chk_inspection_finding_evidence_kind_mime',
'chk_inspection_finding_company_response_pdf',
'chk_inspection_finding_attachment_link'
);
SELECT 'protected_links', COUNT(*)
FROM pg_constraint
WHERE conname IN (
'uq_inspection_finding_communications_id_finding',
'fk_inspection_finding_evidence_communication',
'uq_inspection_finding_evidence_stored_name'
);
SELECT 'permissions', COUNT(*)
FROM permissions
WHERE code IN (
'inspection_evidence.read',
'inspection_evidence.create',
'inspection_communications.read',
'inspection_communications.create'
);
SELECT 'evidence_read_roles', COUNT(*)
FROM role_permissions rp
INNER JOIN roles role ON role.id = rp.role_id
INNER JOIN permissions permission ON permission.id = rp.permission_id
WHERE role.code IN ('admin', 'director', 'supervisor', 'inspector', 'auditor')
AND permission.code = 'inspection_evidence.read';
SELECT 'evidence_create_roles', COUNT(*)
FROM role_permissions rp
INNER JOIN roles role ON role.id = rp.role_id
INNER JOIN permissions permission ON permission.id = rp.permission_id
WHERE role.code IN ('admin', 'director', 'supervisor', 'inspector')
AND permission.code = 'inspection_evidence.create';
SELECT 'communication_read_roles', COUNT(*)
FROM role_permissions rp
INNER JOIN roles role ON role.id = rp.role_id
INNER JOIN permissions permission ON permission.id = rp.permission_id
WHERE role.code IN ('admin', 'director', 'supervisor', 'inspector', 'auditor')
AND permission.code = 'inspection_communications.read';
SELECT 'communication_create_roles', COUNT(*)
FROM role_permissions rp
INNER JOIN roles role ON role.id = rp.role_id
INNER JOIN permissions permission ON permission.id = rp.permission_id
WHERE role.code IN ('admin', 'director', 'supervisor', 'inspector')
AND permission.code = 'inspection_communications.create';
SQL
)"
grep -q 'tables|2' <<<"$dhv2_db_check" || fail "Faltan tablas D4"
grep -q 'communication_checks|5' <<<"$dhv2_db_check" || fail "Faltan controles de comunicaciones"
grep -q 'evidence_checks|11' <<<"$dhv2_db_check" || fail "Faltan controles de evidencias"
grep -q 'protected_links|3' <<<"$dhv2_db_check" || fail "Faltan vínculos protegidos"
grep -q 'permissions|4' <<<"$dhv2_db_check" || fail "Faltan permisos D4"
grep -q 'evidence_read_roles|5' <<<"$dhv2_db_check" || fail "Faltan roles de lectura de evidencias"
grep -q 'evidence_create_roles|4' <<<"$dhv2_db_check" || fail "Faltan roles de carga de evidencias"
grep -q 'communication_read_roles|5' <<<"$dhv2_db_check" || fail "Faltan roles de lectura de comunicaciones"
grep -q 'communication_create_roles|4' <<<"$dhv2_db_check" || fail "Faltan roles de carga de comunicaciones"
echo "$dhv2_db_check"
echo
echo "========== D4 · PRIVILEGIOS RUNTIME =========="
dhv2_privileges="$(docker compose exec -T db sh -lc \
'psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -v ON_ERROR_STOP=1 -v app_user="$APP_DB_USER" -At' <<'SQL'
SELECT 'evidence_select', has_table_privilege(:'app_user', 'inspection_finding_evidence', 'SELECT');
SELECT 'evidence_insert', has_table_privilege(:'app_user', 'inspection_finding_evidence', 'INSERT');
SELECT 'evidence_update', has_table_privilege(:'app_user', 'inspection_finding_evidence', 'UPDATE');
SELECT 'evidence_delete', has_table_privilege(:'app_user', 'inspection_finding_evidence', 'DELETE');
SELECT 'communications_select', has_table_privilege(:'app_user', 'inspection_finding_communications', 'SELECT');
SELECT 'communications_insert', has_table_privilege(:'app_user', 'inspection_finding_communications', 'INSERT');
SELECT 'communications_update', has_table_privilege(:'app_user', 'inspection_finding_communications', 'UPDATE');
SELECT 'communications_delete', has_table_privilege(:'app_user', 'inspection_finding_communications', 'DELETE');
SQL
)"
grep -q 'evidence_select|t' <<<"$dhv2_privileges" || fail "La API no puede leer evidencias"
grep -q 'evidence_insert|t' <<<"$dhv2_privileges" || fail "La API no puede crear evidencias"
grep -q 'evidence_update|f' <<<"$dhv2_privileges" || fail "La API no debe modificar evidencias"
grep -q 'evidence_delete|f' <<<"$dhv2_privileges" || fail "La API no debe borrar evidencias"
grep -q 'communications_select|t' <<<"$dhv2_privileges" || fail "La API no puede leer comunicaciones"
grep -q 'communications_insert|t' <<<"$dhv2_privileges" || fail "La API no puede crear comunicaciones"
grep -q 'communications_update|f' <<<"$dhv2_privileges" || fail "La API no debe modificar comunicaciones"
grep -q 'communications_delete|f' <<<"$dhv2_privileges" || fail "La API no debe borrar comunicaciones"
echo "$dhv2_privileges"
echo
echo "========== D4 · ALMACENAMIENTO =========="
docker compose exec -T api sh -lc \
'test -d "$ASSET_MEDIA_ROOT" && test -w "$ASSET_MEDIA_ROOT"'
echo "Volumen persistente protegido y escribible: OK"
echo
echo "========== D4 · FRONTEND =========="
docker compose exec -T web sh -lc \
'grep -R "0.18.0" /usr/share/nginx/html/assets >/dev/null && grep -R "Evidencias y comunicaciones" /usr/share/nginx/html/assets >/dev/null && grep -R "PDF de respuesta de la empresa" /usr/share/nginx/html/assets >/dev/null && grep -R "Historial de comunicaciones" /usr/share/nginx/html/assets >/dev/null && grep -R "SHA-256" /usr/share/nginx/html/assets >/dev/null'
echo "Evidencias, comunicaciones y footer v0.18.0: OK"
echo
echo "ACEPTACIÓN DE FASE D4: OK"