Files
dh-inspeccion-v2/scripts/phase-d5-3-7-2-2-acceptance.sh
T

129 lines
7.3 KiB
Bash
Executable File

#!/usr/bin/env bash
set -Eeuo pipefail
ROOT="${ROOT:-/var/www/dhv2.korexlabs.com}"
API_URL="${API_URL:-http://127.0.0.1:3101/api/v3}"
DUMMY_UUID="00000000-0000-4000-8000-000000000000"
TMP_MIGRATION="/tmp/dhv2-d53722-accept-migration-$$.log"
TMP_ROUTE="/tmp/dhv2-d5372-route-$$.txt"
trap 'rm -f "$TMP_MIGRATION" "$TMP_ROUTE"' EXIT
cd "$ROOT"
echo "========== D5.3.7.2.2 · VERSIONES =========="
[[ "$(node -p "require('./api-v3/package.json').version")" == "0.19.3-7.2.2" ]]
[[ "$(node -p "require('./web-v2/package.json').version")" == "0.19.3-7.2.2" ]]
grep -Fq "API_PHASE = 'D5.3.7.2.2'" api-v3/src/version.ts
echo "API: 0.19.3-7.2.2"
echo "WEB: 0.19.3-7.2.2"
echo
echo "========== D5.3.7.2.2 · DISCO Y DB =========="
FREE_KB="$(df -Pk "$ROOT" | awk 'NR==2 {print $4}')"
(( FREE_KB >= 2 * 1024 * 1024 ))
[[ "$(docker inspect dhv2-db --format '{{.State.Health.Status}}')" == "healthy" ]]
docker compose exec -T db sh -lc 'pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"'
echo "Libre: $(( FREE_KB / 1024 / 1024 )) GB · PostgreSQL healthy"
echo
echo "========== D5.3.7.2.2 · MIGRACIONES =========="
docker compose --profile tools run --rm migrate npm run migration:show 2>&1 | tee "$TMP_MIGRATION"
grep -Fq 'Pending migrations: no' "$TMP_MIGRATION"
echo
echo "========== D5.3.7.2.2 · HEALTH =========="
HEALTH="$(curl -fsS "$API_URL/health")"
echo "$HEALTH"
grep -Fq '"version":"0.19.3-7.2.2"' <<<"$HEALTH"
grep -Fq '"phase":"D5.3.7.2.2"' <<<"$HEALTH"
grep -Fq '"database":"ok"' <<<"$HEALTH"
echo
echo "========== D5.3.7.2.2 · RUTA DE DETALLE PROTEGIDA =========="
CODE="$(curl -sS -o "$TMP_ROUTE" -w '%{http_code}' "$API_URL/asset-imports/$DUMMY_UUID/plan/items")"
[[ "$CODE" == "401" ]]
echo "/asset-imports/:id/plan/items sin sesión: 401 OK"
echo
echo "========== D5.3.7.2.2 · MODELO RELACIONAL =========="
DB_CHECK="$(
cat <<'SQL' | docker compose exec -T db sh -c 'psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -At -v ON_ERROR_STOP=1 -v app_user="$APP_DB_USER"'
SELECT CASE WHEN to_regclass('public.administrative_departments') IS NOT NULL AND to_regclass('public.area_department_relations') IS NOT NULL THEN 'RELATIONAL_TABLES_OK' ELSE 'RELATIONAL_TABLES_BAD' END;
SELECT CASE WHEN
has_table_privilege(:'app_user','administrative_departments','SELECT') AND
has_table_privilege(:'app_user','administrative_departments','INSERT') AND
has_table_privilege(:'app_user','administrative_departments','UPDATE') AND
NOT has_table_privilege(:'app_user','administrative_departments','DELETE') AND
has_table_privilege(:'app_user','area_department_relations','SELECT') AND
has_table_privilege(:'app_user','area_department_relations','INSERT') AND
has_table_privilege(:'app_user','area_department_relations','UPDATE') AND
NOT has_table_privilege(:'app_user','area_department_relations','DELETE')
THEN 'RELATIONAL_PRIVILEGES_OK' ELSE 'RELATIONAL_PRIVILEGES_BAD' END;
SELECT CASE WHEN EXISTS (SELECT 1 FROM typeorm_migrations WHERE name='PhaseD5372RelationalTerritoryPlan1787767200000') THEN 'MIGRATION_OK' ELSE 'MIGRATION_BAD' END;
SELECT CASE WHEN EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname='chk_asset_import_plan_item_kind'
AND pg_get_constraintdef(oid) LIKE '%DEPARTMENT%'
AND pg_get_constraintdef(oid) LIKE '%AREA_DEPARTMENT_RELATION%'
AND pg_get_constraintdef(oid) LIKE '%LEGAL_RIGHT%'
AND pg_get_constraintdef(oid) LIKE '%LEGAL_RIGHT_ORGANIZATION%'
) THEN 'PLAN_KINDS_OK' ELSE 'PLAN_KINDS_BAD' END;
SQL
)"
echo "$DB_CHECK"
grep -Fq 'RELATIONAL_TABLES_OK' <<<"$DB_CHECK"
grep -Fq 'RELATIONAL_PRIVILEGES_OK' <<<"$DB_CHECK"
grep -Fq 'MIGRATION_OK' <<<"$DB_CHECK"
grep -Fq 'PLAN_KINDS_OK' <<<"$DB_CHECK"
echo
echo "========== D5.3.7.2.2 · NORMALIZACIÓN Y DEDUPLICACIÓN =========="
grep -Fq 'organizationImportKey' api-v3/src/asset-imports/asset-import-plan.ts
grep -Fq 'normalizedLegalRightType' api-v3/src/asset-imports/asset-import-plan.ts
grep -Fq "entityKind: 'DEPARTMENT'" api-v3/src/asset-imports/asset-imports.service.ts
grep -Fq "entityKind:'AREA_DEPARTMENT_RELATION'" api-v3/src/asset-imports/asset-imports.service.ts || grep -Fq "entityKind: 'AREA_DEPARTMENT_RELATION'" api-v3/src/asset-imports/asset-imports.service.ts
grep -Fq "entityKind:'LEGAL_RIGHT'" api-v3/src/asset-imports/asset-imports.service.ts || grep -Fq "entityKind: 'LEGAL_RIGHT'" api-v3/src/asset-imports/asset-imports.service.ts
grep -Fq "entityKind:'LEGAL_RIGHT_ORGANIZATION'" api-v3/src/asset-imports/asset-imports.service.ts || grep -Fq "entityKind: 'LEGAL_RIGHT_ORGANIZATION'" api-v3/src/asset-imports/asset-imports.service.ts
grep -Fq "operatorUnassigned ? '' : organizationImportKey(operator)" api-v3/src/asset-imports/asset-imports.service.ts
grep -Fq "organizationImportKey\(operator\)" api-v3/test/unit/asset-imports-d5-3-7.test.ts || grep -Fq "organizationImportKey(operator)" api-v3/test/unit/asset-imports-d5-3-7.test.ts
if grep -Fq "add('departamento'" api-v3/src/asset-imports/asset-imports.service.ts; then
echo "ERROR: Departamento no debe persistirse como atributo textual duplicado"; exit 1
fi
grep -Fq "status:'PENDING'" api-v3/src/asset-imports/asset-imports.service.ts || grep -Fq "status: 'PENDING'" api-v3/src/asset-imports/asset-imports.service.ts
grep -Fq 'no se infiere titularidad' api-v3/src/asset-imports/asset-imports.service.ts
echo "Departamentos únicos, siglas de organizaciones, derechos normalizados y relaciones explícitas: OK"
echo
echo "========== D5.3.7.2.2 · APLICACIÓN Y ROLLBACK =========="
grep -Fq 'INSERT INTO administrative_departments' api-v3/src/asset-imports/asset-imports.service.ts
grep -Fq 'INSERT INTO area_department_relations' api-v3/src/asset-imports/asset-imports.service.ts
grep -Fq 'INSERT INTO area_legal_rights' api-v3/src/asset-imports/asset-imports.service.ts
grep -Fq 'INSERT INTO area_legal_right_organizations' api-v3/src/asset-imports/asset-imports.service.ts
grep -Fq "area_department_relations SET valid_until=CURRENT_DATE" api-v3/src/asset-imports/asset-imports.service.ts
grep -Fq "area_legal_rights SET status='REVOKED'" api-v3/src/asset-imports/asset-imports.service.ts
grep -Fq "administrative_departments SET is_active=false" api-v3/src/asset-imports/asset-imports.service.ts
if grep -Eq 'DELETE FROM (assets|administrative_departments|area_department_relations|area_legal_rights)' api-v3/src/asset-imports/asset-imports.service.ts; then
echo "ERROR: rollback relacional no debe hacer DELETE físico"; exit 1
fi
grep -Fq '::integer,$${base+2}::uuid' api-v3/src/asset-imports/asset-imports.service.ts
grep -Fq 'row.row_number=v.row_number' api-v3/src/asset-imports/asset-imports.service.ts
echo "Aplicación transaccional, casteo row_number y reversión lógica relacional: OK"
echo
echo "========== D5.3.7.2.2 · FRONTEND =========="
grep -Fq 'Plan relacional por entidad' web-v2/src/pages/AssetImportsPage.tsx
grep -Fq 'Ver detalle' web-v2/src/pages/AssetImportsPage.tsx
grep -Fq 'Departamentos' web-v2/src/pages/AssetImportsPage.tsx
grep -Fq 'Marco jurídico' web-v2/src/pages/AssetImportsPage.tsx
grep -Fq 'listAssetImportPlanItems' web-v2/src/lib/api.ts
echo "Desglose por tipo y detalle paginado: OK"
echo
echo "========== D5.3.7.2.2 · CONTENEDORES =========="
docker compose ps -a
[[ "$(docker inspect dhv2-db --format '{{.State.Health.Status}}')" == "healthy" ]]
[[ "$(docker inspect dhv2-api --format '{{.State.Status}}')" == "running" ]]
[[ "$(docker inspect dhv2-web --format '{{.State.Status}}')" == "running" ]]
echo
echo "ACEPTACIÓN DE FASE D5.3.7.2.2: OK"