mirror of
https://origin.cursor.com/mrdevmx/panels.git
synced 2026-10-09 20:23:17 +00:00
<!-- CURSOR_AGENT_PR_BODY_BEGIN --> ## Validación de endpoints y SPs Se probaron todos los endpoints nuevos (gastos, almacén, control presupuestal, IAM) y sus RPCs asociados. ### Bugs corregidos en migraciones/SQL 1. **IAM grants en schema core** — `iam-004e` intentaba `GRANT` sobre `core` con rol `iam_owner` (sin permiso). Los grants cruzados se movieron a `core-027-iam-rpc-cross-grants.sql`. 2. **`_cost_settings` ambiguo** — columnas `budget_warn_pct` etc. colisionaban con `RETURNS TABLE` en PL/pgSQL, rompiendo `fn_expense_create` y control presupuestal. Corregido en `core-028-fix-cost-settings-ambiguous.sql`. ### Tests añadidos - `api/cost_modules_test.ts` — CRUD RPC gastos, flujo almacén completo, control presupuestal, IAM permisos - `scripts/crud-smoke-test.sh` — smoke HTTP de todos los endpoints nuevos ### Resultados - `deno test cost_modules_test.ts` — 5/5 OK - `./scripts/crud-smoke-test.sh` — todos los checks OK (gastos CRUD, IVA, cost-settings, almacén, transferencias, cost-control, IAM) <!-- CURSOR_AGENT_PR_BODY_END --> <div><a href="https://cursor.com/agents/bc-eeec3b5c-f789-43e8-a353-0755e4706377?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a> <a href="https://cursor.com/background-agent?bcId=bc-eeec3b5c-f789-43e8-a353-0755e4706377&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a> </div>
307 lines
13 KiB
Bash
Executable file
307 lines
13 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Smoke test CRUD via RPC-backed API
|
|
set -euo pipefail
|
|
BASE="http://127.0.0.1:8000"
|
|
JAR="/tmp/panels-cookies.txt"
|
|
PASS="${SEED_PASSWORD:-TestAdmin123!}"
|
|
SUFFIX=$((RANDOM % 90000 + 10000))
|
|
WORKER_TAG=$(printf '%02d' $((10 + SUFFIX % 80)))
|
|
|
|
pass() { echo "✓ $1"; }
|
|
fail() { echo "✗ $1"; exit 1; }
|
|
|
|
check_status() {
|
|
local name="$1" expected="$2" actual="$3" body="$4"
|
|
if [[ "$actual" != "$expected" ]]; then
|
|
echo "FAIL $name: expected HTTP $expected, got $actual"
|
|
echo "$body" | head -c 800
|
|
exit 1
|
|
fi
|
|
pass "$name (HTTP $actual)"
|
|
}
|
|
|
|
echo "=== Health ==="
|
|
H=$(curl -s -w "\n%{http_code}" "$BASE/v1/health")
|
|
BODY=$(echo "$H" | head -n -1)
|
|
CODE=$(echo "$H" | tail -1)
|
|
check_status "health" "200" "$CODE" "$BODY"
|
|
echo "$BODY" | grep -q '"ok":true' || fail "health body not ok"
|
|
|
|
echo "=== Login ==="
|
|
LOGIN=$(curl -s -w "\n%{http_code}" -c "$JAR" -X POST "$BASE/v1/auth/login" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"username\":\"arct2608\",\"password\":\"$PASS\"}")
|
|
BODY=$(echo "$LOGIN" | head -n -1)
|
|
CODE=$(echo "$LOGIN" | tail -1)
|
|
check_status "login" "200" "$CODE" "$BODY"
|
|
|
|
echo "=== Companies LIST ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" "$BASE/v1/companies")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "companies list" "200" "$CODE" "$BODY"
|
|
echo "$BODY" | grep -q '"ok":true' || fail "companies list envelope"
|
|
echo "$BODY" | grep -q 'ARCT2608' || fail "seed company missing"
|
|
|
|
echo "=== Companies CREATE ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" -X POST "$BASE/v1/companies" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"name": "Subempresa Prueba RPC",
|
|
"code": "SUBRPC'"$SUFFIX"'",
|
|
"razon_social": "Subempresa Prueba SA de CV",
|
|
"rfc": "SPR010101ABC",
|
|
"regimen_fiscal": "601",
|
|
"registro_patronal": "A1234567890",
|
|
"domicilio_fiscal": "Calle Test 123",
|
|
"codigo_postal": "01000",
|
|
"ciudad": "Ciudad de México",
|
|
"estado": "CDMX"
|
|
}')
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "company create" "201" "$CODE" "$BODY"
|
|
COMPANY_ID=$(echo "$BODY" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2)
|
|
[[ -n "$COMPANY_ID" ]] || fail "no company id in create response"
|
|
|
|
echo "=== Companies GET ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" "$BASE/v1/companies/$COMPANY_ID")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "company get" "200" "$CODE" "$BODY"
|
|
echo "$BODY" | grep -qi "SUBRPC$SUFFIX" || fail "company code mismatch"
|
|
|
|
echo "=== Companies PATCH ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" -X PATCH "$BASE/v1/companies/$COMPANY_ID" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"nombre_comercial":"Sub RPC Editada","telefono":"5551234567"}')
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "company patch" "200" "$CODE" "$BODY"
|
|
echo "$BODY" | grep -q 'Sub RPC Editada' || fail "company patch not applied"
|
|
|
|
echo "=== Projects LIST ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" "$BASE/v1/projects")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "projects list" "200" "$CODE" "$BODY"
|
|
|
|
echo "=== Projects CREATE ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" -X POST "$BASE/v1/projects" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"name": "Obra Prueba RPC",
|
|
"company_id": 1,
|
|
"address": "Av. Reforma 100",
|
|
"contract_amount": 1500000,
|
|
"start_date": "2026-01-01"
|
|
}')
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "project create" "201" "$CODE" "$BODY"
|
|
PROJECT_ID=$(echo "$BODY" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2)
|
|
[[ -n "$PROJECT_ID" ]] || fail "no project id"
|
|
|
|
echo "=== Projects GET ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" "$BASE/v1/projects/$PROJECT_ID")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "project get" "200" "$CODE" "$BODY"
|
|
|
|
echo "=== Projects PATCH ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" -X PATCH "$BASE/v1/projects/$PROJECT_ID" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"name":"Obra Prueba RPC Editada","resident_name":"Ing. Test"}')
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "project patch" "200" "$CODE" "$BODY"
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" "$BASE/v1/projects/$PROJECT_ID")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "project get after patch" "200" "$CODE" "$BODY"
|
|
echo "$BODY" | grep -qi 'Obra Prueba RPC Editada' || fail "project patch not applied"
|
|
|
|
echo "=== Workers CREATE ==="
|
|
WORKER_JSON=$(cat <<EOF
|
|
{
|
|
"first_name": "Juan",
|
|
"last_name_p": "Perez",
|
|
"last_name_m": "Lopez",
|
|
"curp": "ROCL900215HDFRPM${WORKER_TAG}",
|
|
"rfc": "ROCL900215A${WORKER_TAG}",
|
|
"nss": "12345679000",
|
|
"phone": "5559876543",
|
|
"email": "worker${SUFFIX}@test.local",
|
|
"address": "Calle Worker 1",
|
|
"hire_type": "ARCT2608",
|
|
"position": "Oficial",
|
|
"risk_code": "medio",
|
|
"work_type": "N",
|
|
"daily_wage": 450,
|
|
"project_id": ${PROJECT_ID}
|
|
}
|
|
EOF
|
|
)
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" -X POST "$BASE/v1/workers" \
|
|
-H "Content-Type: application/json" \
|
|
-d "$WORKER_JSON")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "worker create" "201" "$CODE" "$BODY"
|
|
WORKER_ID=$(echo "$BODY" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2)
|
|
[[ -n "$WORKER_ID" ]] || fail "no worker id"
|
|
|
|
echo "=== Workers GET ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" "$BASE/v1/workers/$WORKER_ID")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "worker get" "200" "$CODE" "$BODY"
|
|
|
|
echo "=== Workers LIST ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" "$BASE/v1/workers")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "workers list" "200" "$CODE" "$BODY"
|
|
|
|
echo "=== Workers PATCH ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" -X PATCH "$BASE/v1/workers/$WORKER_ID" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"position":"Residente","daily_wage":500}')
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "worker patch" "200" "$CODE" "$BODY"
|
|
|
|
echo "=== Catalogs ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" "$BASE/v1/catalogs")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "catalogs" "200" "$CODE" "$BODY"
|
|
echo "$BODY" | grep -q '"risks"' || fail "catalogs missing risks"
|
|
|
|
echo "=== Expenses LIST ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" "$BASE/v1/expenses")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "expenses list" "200" "$CODE" "$BODY"
|
|
echo "$BODY" | grep -q '"ok":true' || fail "expenses list envelope"
|
|
|
|
echo "=== Expenses CREATE ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" -X POST "$BASE/v1/expenses" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{
|
|
\"company_id\": 1,
|
|
\"project_id\": ${PROJECT_ID},
|
|
\"description\": \"Gasto smoke test ${SUFFIX}\",
|
|
\"subtotal\": 1000,
|
|
\"total\": 1160,
|
|
\"tax_included\": false,
|
|
\"is_deductible\": true,
|
|
\"counts_toward_budget\": true
|
|
}")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "expense create" "201" "$CODE" "$BODY"
|
|
EXPENSE_ID=$(echo "$BODY" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2)
|
|
[[ -n "$EXPENSE_ID" ]] || fail "no expense id"
|
|
|
|
echo "=== Expenses GET ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" "$BASE/v1/expenses/$EXPENSE_ID")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "expense get" "200" "$CODE" "$BODY"
|
|
|
|
echo "=== Expenses PATCH ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" -X PATCH "$BASE/v1/expenses/$EXPENSE_ID" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"description":"Gasto smoke editado","notes":"ok"}')
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "expense patch" "200" "$CODE" "$BODY"
|
|
|
|
echo "=== IVA summary ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" "$BASE/v1/iva/summary?company_id=1&year=2026&month=1")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "iva summary" "200" "$CODE" "$BODY"
|
|
|
|
echo "=== Cost settings GET/PUT ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" "$BASE/v1/cost-settings")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "cost settings get" "200" "$CODE" "$BODY"
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" -X PUT "$BASE/v1/cost-settings" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"budget_warn_pct":85,"budget_critical_pct":100,"default_iva_rate":0.16}')
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "cost settings put" "200" "$CODE" "$BODY"
|
|
|
|
echo "=== Warehouses LIST ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" "$BASE/v1/warehouses")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "warehouses list" "200" "$CODE" "$BODY"
|
|
|
|
echo "=== Warehouse central ensure ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" -X POST "$BASE/v1/warehouses/central/ensure")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "warehouse central ensure" "200" "$CODE" "$BODY"
|
|
CENTRAL_ID=$(echo "$BODY" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2)
|
|
[[ -n "$CENTRAL_ID" ]] || fail "no central warehouse id"
|
|
|
|
echo "=== Project warehouse open ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" -X POST "$BASE/v1/projects/$PROJECT_ID/warehouse/open")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "project warehouse open" "200" "$CODE" "$BODY"
|
|
PROJECT_WH_ID=$(echo "$BODY" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2)
|
|
[[ -n "$PROJECT_WH_ID" ]] || fail "no project warehouse id"
|
|
|
|
echo "=== Warehouse material ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" -X POST "$BASE/v1/warehouse-materials" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"code\":\"MAT${SUFFIX}\",\"name\":\"Material smoke\",\"unit\":\"PZA\"}")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "warehouse material upsert" "200" "$CODE" "$BODY"
|
|
MATERIAL_ID=$(echo "$BODY" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2)
|
|
[[ -n "$MATERIAL_ID" ]] || fail "no material id"
|
|
|
|
echo "=== Warehouse entry ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" -X POST "$BASE/v1/warehouses/$CENTRAL_ID/entries" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"material_id\":${MATERIAL_ID},\"quantity\":10,\"unit_cost\":50}")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "warehouse entry" "201" "$CODE" "$BODY"
|
|
|
|
echo "=== Warehouse transfer ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" -X POST "$BASE/v1/warehouse-transfers" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"from_warehouse_id\":${CENTRAL_ID},\"to_warehouse_id\":${PROJECT_WH_ID},\"material_id\":${MATERIAL_ID},\"quantity\":3}")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "warehouse transfer" "201" "$CODE" "$BODY"
|
|
|
|
echo "=== Warehouse stock ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" "$BASE/v1/warehouses/$PROJECT_WH_ID/stock")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "warehouse stock" "200" "$CODE" "$BODY"
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" "$BASE/v1/warehouses/stock/global")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "warehouse global stock" "200" "$CODE" "$BODY"
|
|
|
|
echo "=== Warehouse movements ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" "$BASE/v1/warehouses/$PROJECT_WH_ID/movements")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "warehouse movements" "200" "$CODE" "$BODY"
|
|
|
|
echo "=== Cost control (project) ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" "$BASE/v1/projects/$PROJECT_ID/cost-control/summary")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "cost control summary" "200" "$CODE" "$BODY"
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" "$BASE/v1/projects/$PROJECT_ID/cost-control/items")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "cost control items" "200" "$CODE" "$BODY"
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" "$BASE/v1/projects/$PROJECT_ID/cost-control/chapters")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "cost control chapters" "200" "$CODE" "$BODY"
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" "$BASE/v1/projects/$PROJECT_ID/cost-control/deviations")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "cost control deviations" "200" "$CODE" "$BODY"
|
|
|
|
echo "=== IAM endpoints ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" "$BASE/v1/me/permissions")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "me permissions" "200" "$CODE" "$BODY"
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" "$BASE/v1/iam/permissions")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "iam permissions" "200" "$CODE" "$BODY"
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" "$BASE/v1/iam/users")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "iam users" "200" "$CODE" "$BODY"
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" "$BASE/v1/iam/roles/user/permissions")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "iam role permissions get" "200" "$CODE" "$BODY"
|
|
|
|
echo "=== Expenses DELETE (void) ==="
|
|
R=$(curl -s -w "\n%{http_code}" -b "$JAR" -X DELETE "$BASE/v1/expenses/$EXPENSE_ID")
|
|
BODY=$(echo "$R" | head -n -1); CODE=$(echo "$R" | tail -1)
|
|
check_status "expense void" "200" "$CODE" "$BODY"
|
|
|
|
echo ""
|
|
echo "All CRUD smoke tests passed."
|