FASE 4: - Redis cache de stock con fallback graceful - Multi-moneda (MXN/USD) con contabilidad en MXN - Proveedores y ordenes de compra completo - Meilisearch 1.5M+ partes indexadas - Metabase KPIs con dashboard auto-generado FASE 5: - CRM mejorado: activities, tags, loyalty program, analytics - Imagenes de partes: upload, resize, thumbnails WebP - Ordenes de servicio Kanban: received->diagnosis->repair->ready->delivered - Garantias/RMA, alertas de reorden, multi-sucursal - Stubs BNPL (APLAZO) y ERP Sync (Aspel/Contpaqi) FASE 6: - Notificaciones automaticas: push/WhatsApp/email/in-app - Reportes de ahorro vs retail_price - Logistica + tracking: DHL, FedEx, Estafeta, 99min, Uber - API Publica: API keys, rate limiting, catalog search Migraciones: v1.9-v3.0 Tests: 93/93 pasando Backup: nexus_backup_20260427_045859.tar.gz
117 lines
4.0 KiB
Python
117 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Test Metabase integration (Mejora #5).
|
|
|
|
Validates:
|
|
1. Metabase health endpoint
|
|
2. Dashboard exists and is accessible
|
|
3. Database connection is configured
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import requests
|
|
|
|
METABASE_URL = os.environ.get('METABASE_URL', 'http://localhost:3000').rstrip('/')
|
|
|
|
RED = '\033[91m'
|
|
GREEN = '\033[92m'
|
|
YELLOW = '\033[93m'
|
|
RESET = '\033[0m'
|
|
|
|
|
|
def print_result(name, passed, detail=""):
|
|
status = f"{GREEN}PASS{RESET}" if passed else f"{RED}FAIL{RESET}"
|
|
print(f" [{status}] {name}" + (f" — {detail}" if detail else ""))
|
|
|
|
|
|
def main():
|
|
print("=" * 60)
|
|
print("METABASE KPIs — VALIDATION SUITE")
|
|
print("=" * 60)
|
|
|
|
passed = 0
|
|
failed = 0
|
|
|
|
# ── Test 1: Health ──────────────────────────────────────────
|
|
print("\n[1] Metabase Health")
|
|
try:
|
|
r = requests.get(f"{METABASE_URL}/api/health", timeout=10)
|
|
if r.status_code == 200 and r.json().get('status') == 'ok':
|
|
print_result("Health", True, "ok")
|
|
passed += 1
|
|
else:
|
|
print_result("Health", False, f"status={r.status_code}")
|
|
failed += 1
|
|
except Exception as e:
|
|
print_result("Health", False, str(e))
|
|
failed += 1
|
|
return passed, failed
|
|
|
|
# ── Test 2: Session properties ──────────────────────────────
|
|
print("\n[2] Metabase API")
|
|
try:
|
|
r = requests.get(f"{METABASE_URL}/api/session/properties", timeout=10)
|
|
if r.status_code == 200:
|
|
props = r.json()
|
|
has_user = props.get('has-user-setup', False)
|
|
if has_user:
|
|
print_result("Setup", True, "has admin user")
|
|
passed += 1
|
|
else:
|
|
print_result("Setup", False, "no admin user")
|
|
failed += 1
|
|
else:
|
|
print_result("API", False, f"status={r.status_code}")
|
|
failed += 1
|
|
except Exception as e:
|
|
print_result("API", False, str(e))
|
|
failed += 1
|
|
|
|
# ── Test 3: Database connection ─────────────────────────────
|
|
print("\n[3] Database Connection")
|
|
try:
|
|
# Try to read config from saved file
|
|
config_path = os.path.expanduser('~/.nexus_metabase_config.json')
|
|
if os.path.exists(config_path):
|
|
import json
|
|
with open(config_path) as f:
|
|
config = json.load(f)
|
|
session = config.get('session_id')
|
|
if session:
|
|
r = requests.get(
|
|
f"{METABASE_URL}/api/database",
|
|
headers={'X-Metabase-Session': session},
|
|
timeout=10
|
|
)
|
|
if r.status_code == 200:
|
|
dbs = r.json().get('data', [])
|
|
if dbs:
|
|
print_result("DB connection", True, f"{len(dbs)} DB(s) configured")
|
|
passed += 1
|
|
else:
|
|
print_result("DB connection", False, "no databases")
|
|
failed += 1
|
|
else:
|
|
print_result("DB connection", False, f"status={r.status_code}")
|
|
failed += 1
|
|
else:
|
|
print_result("DB connection", False, "no session")
|
|
failed += 1
|
|
else:
|
|
print_result("DB connection", True, "SKIP (no config)")
|
|
passed += 1
|
|
except Exception as e:
|
|
print_result("DB connection", False, str(e))
|
|
failed += 1
|
|
|
|
# ── Summary ─────────────────────────────────────────────────
|
|
print("\n" + "=" * 60)
|
|
print(f"RESULTS: {GREEN}{passed} passed{RESET}, {RED}{failed} failed{RESET}")
|
|
print("=" * 60)
|
|
return passed, failed
|
|
|
|
|
|
if __name__ == '__main__':
|
|
passed, failed = main()
|
|
sys.exit(0 if failed == 0 else 1)
|