167 lines
7.6 KiB
Bash
Executable File
167 lines
7.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%/}"
|
||
|
||
fail() {
|
||
echo "ERROR: $*" >&2
|
||
exit 1
|
||
}
|
||
|
||
echo "========== D5.3 · 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 "========== D5.3 · 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.19.3"' <<<"$dhv2_internal" || fail "Health interno no informa v0.19.3"
|
||
grep -q '"phase":"D5.3"' <<<"$dhv2_internal" || fail "Health interno no informa D5.3"
|
||
grep -q '"version":"0.19.3"' <<<"$dhv2_external" || fail "Health externo no informa v0.19.3"
|
||
grep -q '"database":"ok"' <<<"$dhv2_external" || fail "La base no está saludable"
|
||
echo "$dhv2_external"
|
||
|
||
echo
|
||
echo "========== D5.3 · RUTA OPERATIVA PROTEGIDA =========="
|
||
dhv2_code="$(curl -sS --connect-timeout 10 --max-time 30 \
|
||
-o /tmp/dhv2-d5-3-response.json -w '%{http_code}' \
|
||
"$dhv2_base_url/api/v3/asset-operational-relations/areas")"
|
||
test "$dhv2_code" = "401" || fail "/api/v3/asset-operational-relations/areas devolvió HTTP $dhv2_code; se esperaba 401"
|
||
rm -f /tmp/dhv2-d5-3-response.json
|
||
echo "/api/v3/asset-operational-relations/areas sin sesión: 401 OK"
|
||
|
||
echo
|
||
echo "========== D5.3 · MODELO OPERATIVO =========="
|
||
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 'operational_role_enum', COUNT(*)
|
||
FROM pg_enum enum_value
|
||
INNER JOIN pg_type enum_type ON enum_type.oid = enum_value.enumtypid
|
||
WHERE enum_type.typname = 'asset_type_operational_role';
|
||
|
||
SELECT 'asset_type_role_column', COUNT(*)
|
||
FROM information_schema.columns
|
||
WHERE table_schema = 'public'
|
||
AND table_name = 'asset_types'
|
||
AND column_name = 'operational_role';
|
||
|
||
SELECT 'asset_operational_columns', COUNT(*)
|
||
FROM information_schema.columns
|
||
WHERE table_schema = 'public'
|
||
AND table_name = 'assets'
|
||
AND column_name IN ('operational_area_id', 'operator_company_id');
|
||
|
||
SELECT 'relations_table', COUNT(*)
|
||
FROM information_schema.tables
|
||
WHERE table_schema = 'public'
|
||
AND table_name = 'area_company_relations';
|
||
|
||
SELECT 'relations_constraints', COUNT(*)
|
||
FROM information_schema.table_constraints
|
||
WHERE table_schema = 'public'
|
||
AND table_name = 'area_company_relations'
|
||
AND constraint_name IN (
|
||
'chk_area_company_relation_dates',
|
||
'chk_area_company_relation_start_reason',
|
||
'chk_area_company_relation_end_reason',
|
||
'fk_area_company_relation_area',
|
||
'fk_area_company_relation_company',
|
||
'fk_area_company_relation_created_by',
|
||
'fk_area_company_relation_ended_by'
|
||
);
|
||
|
||
SELECT 'active_pair_unique_index', COUNT(*)
|
||
FROM pg_indexes
|
||
WHERE schemaname = 'public'
|
||
AND tablename = 'area_company_relations'
|
||
AND indexname = 'uq_area_company_relations_active_pair';
|
||
|
||
SELECT 'operational_triggers', COUNT(DISTINCT trigger_name)
|
||
FROM information_schema.triggers
|
||
WHERE event_object_schema = 'public'
|
||
AND trigger_name IN (
|
||
'trg_asset_types_protect_operational_changes',
|
||
'trg_area_company_relation_roles',
|
||
'trg_assets_operational_context',
|
||
'trg_assets_operational_descendants',
|
||
'trg_assets_protect_operational_anchor_inactivation'
|
||
);
|
||
|
||
SELECT 'relation_read_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 = 'asset_relations.read';
|
||
|
||
SELECT 'relation_manage_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')
|
||
AND permission.code = 'asset_relations.manage';
|
||
|
||
SELECT 'relation_select', has_table_privilege(:'app_user', 'area_company_relations', 'SELECT');
|
||
SELECT 'relation_insert', has_table_privilege(:'app_user', 'area_company_relations', 'INSERT');
|
||
SELECT 'relation_update', has_table_privilege(:'app_user', 'area_company_relations', 'UPDATE');
|
||
SELECT 'relation_delete', has_table_privilege(:'app_user', 'area_company_relations', 'DELETE');
|
||
|
||
SELECT 'asset_versions_backfilled', NOT EXISTS (
|
||
SELECT 1
|
||
FROM asset_versions
|
||
WHERE NOT (snapshot->'type' ? 'operationalRole')
|
||
OR NOT (snapshot ? 'operationalArea')
|
||
OR NOT (snapshot ? 'operatorCompany')
|
||
);
|
||
|
||
SELECT 'operational_pair_integrity', NOT EXISTS (
|
||
SELECT 1 FROM assets
|
||
WHERE (operational_area_id IS NULL) <> (operator_company_id IS NULL)
|
||
);
|
||
|
||
SELECT 'migration_applied', COUNT(*)
|
||
FROM typeorm_migrations
|
||
WHERE name = 'PhaseD53OperationalContext1787421600000';
|
||
SQL
|
||
)"
|
||
grep -q 'operational_role_enum|3' <<<"$dhv2_db_check" || fail "El enum operativo no tiene los tres roles esperados"
|
||
grep -q 'asset_type_role_column|1' <<<"$dhv2_db_check" || fail "Falta operational_role en asset_types"
|
||
grep -q 'asset_operational_columns|2' <<<"$dhv2_db_check" || fail "Faltan columnas de contexto operativo en assets"
|
||
grep -q 'relations_table|1' <<<"$dhv2_db_check" || fail "No se creó area_company_relations"
|
||
grep -q 'relations_constraints|7' <<<"$dhv2_db_check" || fail "Faltan restricciones de integridad en relaciones Área–Empresa"
|
||
grep -q 'active_pair_unique_index|1' <<<"$dhv2_db_check" || fail "Falta unicidad del vínculo activo Área–Empresa"
|
||
grep -q 'operational_triggers|5' <<<"$dhv2_db_check" || fail "Faltan triggers de integridad operativa"
|
||
grep -q 'relation_read_roles|5' <<<"$dhv2_db_check" || fail "Los roles de consulta no recibieron asset_relations.read"
|
||
grep -q 'relation_manage_roles|2' <<<"$dhv2_db_check" || fail "Admin y supervisor no recibieron asset_relations.manage"
|
||
grep -q 'relation_select|t' <<<"$dhv2_db_check" || fail "La API no puede leer relaciones operativas"
|
||
grep -q 'relation_insert|t' <<<"$dhv2_db_check" || fail "La API no puede crear relaciones operativas"
|
||
grep -q 'relation_update|t' <<<"$dhv2_db_check" || fail "La API no puede finalizar relaciones operativas"
|
||
grep -q 'relation_delete|f' <<<"$dhv2_db_check" || fail "La API no debe borrar relaciones operativas"
|
||
grep -q 'asset_versions_backfilled|t' <<<"$dhv2_db_check" || fail "Hay snapshots históricos sin los campos de D5.3"
|
||
grep -q 'operational_pair_integrity|t' <<<"$dhv2_db_check" || fail "Hay activos con contexto operativo incompleto"
|
||
grep -q 'migration_applied|1' <<<"$dhv2_db_check" || fail "La migración D5.3 no figura aplicada"
|
||
echo "$dhv2_db_check"
|
||
|
||
echo
|
||
echo "========== D5.3 · API COMPILADA =========="
|
||
docker compose exec -T api sh -lc \
|
||
'grep -R "AssetOperationalRelationsController" /app/dist >/dev/null && grep -R "ASSET_AREA_COMPANY_RELATION_CREATED" /app/dist >/dev/null && grep -R "ASSET_HIERARCHY_MOVE_BREAKS_OPERATIONAL_CONTEXT" /app/dist >/dev/null'
|
||
echo "Relaciones, auditoría y validaciones operativas cargadas: OK"
|
||
|
||
echo
|
||
echo "========== D5.3 · DASHBOARD =========="
|
||
docker compose exec -T web sh -lc \
|
||
'grep -R "0.19.3" /usr/share/nginx/html/assets >/dev/null && grep -R "Contexto operativo" /usr/share/nginx/html/assets >/dev/null && grep -R "Relaciones operativas" /usr/share/nginx/html/assets >/dev/null && grep -R "Empresa explotadora" /usr/share/nginx/html/assets >/dev/null'
|
||
echo "Contexto Área–Empresa y footer v0.19.3: OK"
|
||
|
||
echo
|
||
echo "========== D5.3 · CONTENEDORES =========="
|
||
docker compose ps
|
||
|
||
echo
|
||
echo "ACEPTACIÓN DE FASE D5.3: OK"
|