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

145 lines
6.6 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"
dhv2_now="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
fail() {
echo "ERROR: $*" >&2
exit 1
}
echo "========== B5 · 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 "========== B5 · 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.12.0"' <<<"$dhv2_internal" || fail "Health interno no informa v0.12.0"
grep -q '"phase":"B5"' <<<"$dhv2_internal" || fail "Health interno no informa B5"
grep -q '"version":"0.12.0"' <<<"$dhv2_external" || fail "Health externo no informa v0.12.0"
grep -q '"database":"ok"' <<<"$dhv2_external" || fail "La base no está saludable"
echo "$dhv2_external"
echo
echo "========== B5 · RUTAS PROTEGIDAS =========="
for dhv2_path in \
"assets/$dhv2_nil_uuid/provenance" \
"temporal-assets?at=$dhv2_now"; do
dhv2_code="$(curl -sS --connect-timeout 10 --max-time 30 \
-o /tmp/dhv2-b5-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-b5-response.json
echo
echo "========== B5 · PROCEDENCIA Y TEMPORALIDAD =========="
dhv2_db_check="$(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 'provenance_columns', COUNT(*)
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'assets'
AND column_name IN (
'data_origin', 'source_name', 'source_reference', 'source_observed_at',
'source_notes', 'provenance_verified_at', 'provenance_verified_by',
'provenance_updated_at', 'provenance_updated_by'
);
SELECT 'provenance_indexes', COUNT(*)
FROM pg_indexes
WHERE schemaname = 'public'
AND tablename = 'assets'
AND indexname IN (
'idx_assets_data_origin', 'idx_assets_provenance_verified_at'
);
SELECT 'history_provenance_changes', COUNT(*)
FROM pg_constraint
WHERE conname = 'chk_asset_versions_change_type'
AND pg_get_constraintdef(oid) LIKE '%PROVENANCE_BASELINE%'
AND pg_get_constraintdef(oid) LIKE '%PROVENANCE_UPDATED%'
AND pg_get_constraintdef(oid) LIKE '%PROVENANCE_VERIFIED%';
SELECT 'current_snapshot_mismatches', COUNT(*)
FROM assets asset
WHERE NOT EXISTS (
SELECT 1
FROM asset_versions version
WHERE version.asset_id = asset.id
AND version.version_number = asset.current_version
AND version.snapshot ? 'provenance'
);
SELECT 'permissions', COUNT(*)
FROM permissions
WHERE code IN (
'assets.read_provenance', 'assets.manage_provenance',
'assets.verify_provenance', 'assets.read_temporal'
);
SELECT 'read_provenance_roles', COUNT(*)
FROM role_permissions role_permission
INNER JOIN roles role ON role.id = role_permission.role_id
INNER JOIN permissions permission ON permission.id = role_permission.permission_id
WHERE role.code IN ('admin', 'director', 'supervisor', 'inspector', 'auditor')
AND permission.code = 'assets.read_provenance';
SELECT 'read_temporal_roles', COUNT(*)
FROM role_permissions role_permission
INNER JOIN roles role ON role.id = role_permission.role_id
INNER JOIN permissions permission ON permission.id = role_permission.permission_id
WHERE role.code IN ('admin', 'director', 'supervisor', 'inspector', 'auditor')
AND permission.code = 'assets.read_temporal';
SELECT 'manage_provenance_roles', COUNT(*)
FROM role_permissions role_permission
INNER JOIN roles role ON role.id = role_permission.role_id
INNER JOIN permissions permission ON permission.id = role_permission.permission_id
WHERE role.code IN ('admin', 'supervisor', 'inspector')
AND permission.code = 'assets.manage_provenance';
SELECT 'verify_provenance_roles', COUNT(*)
FROM role_permissions role_permission
INNER JOIN roles role ON role.id = role_permission.role_id
INNER JOIN permissions permission ON permission.id = role_permission.permission_id
WHERE role.code IN ('admin', 'director', 'supervisor')
AND permission.code = 'assets.verify_provenance';
SELECT 'app_assets_update', has_table_privilege(:'app_user', 'assets', 'UPDATE');
SELECT 'app_versions_update', has_table_privilege(:'app_user', 'asset_versions', 'UPDATE');
SELECT 'app_versions_delete', has_table_privilege(:'app_user', 'asset_versions', 'DELETE');
SQL
)"
grep -q 'provenance_columns|9' <<<"$dhv2_db_check" || fail "Faltan columnas de procedencia"
grep -q 'provenance_indexes|2' <<<"$dhv2_db_check" || fail "Faltan índices de procedencia"
grep -q 'history_provenance_changes|1' <<<"$dhv2_db_check" || fail "El historial no reconoce cambios de procedencia"
grep -q 'current_snapshot_mismatches|0' <<<"$dhv2_db_check" || fail "Hay activos cuya versión vigente no contiene procedencia"
grep -q 'permissions|4' <<<"$dhv2_db_check" || fail "No se crearon los cuatro permisos B5"
grep -q 'read_provenance_roles|5' <<<"$dhv2_db_check" || fail "Los cinco roles base no recibieron lectura de procedencia"
grep -q 'read_temporal_roles|5' <<<"$dhv2_db_check" || fail "Los cinco roles base no recibieron consulta temporal"
grep -q 'manage_provenance_roles|3' <<<"$dhv2_db_check" || fail "Los tres roles operativos no recibieron gestión de procedencia"
grep -q 'verify_provenance_roles|3' <<<"$dhv2_db_check" || fail "Los tres roles de control no recibieron verificación"
grep -q 'app_assets_update|t' <<<"$dhv2_db_check" || fail "La API no puede actualizar procedencia"
grep -q 'app_versions_update|f' <<<"$dhv2_db_check" || fail "La API no debe modificar versiones históricas"
grep -q 'app_versions_delete|f' <<<"$dhv2_db_check" || fail "La API no debe borrar versiones históricas"
echo "$dhv2_db_check"
echo
echo "========== B5 · FRONTEND =========="
docker compose exec -T web sh -lc \
'grep -R "0.12.0" /usr/share/nginx/html/assets >/dev/null && grep -R "Procedencia y consulta temporal" /usr/share/nginx/html/assets >/dev/null && grep -R "Procedencia de datos" /usr/share/nginx/html/assets >/dev/null && grep -R "Consulta temporal" /usr/share/nginx/html/assets >/dev/null && grep -R "Reconstruir" /usr/share/nginx/html/assets >/dev/null'
echo "Procedencia, consulta temporal y footer v0.12.0: OK"
echo
echo "ACEPTACIÓN DE FASE B5: OK"