"""
Tests pour les fonctionnalites immobilieres du serveur MCP Wealthfolio.
Ce module teste toutes les fonctions d'acces aux donnees immobilieres.
IMPORTANT: Ces tests sont en lecture seule et ne modifient pas la base de donnees.
"""
import os
import sys
import sqlite3
import json
# Force UTF-8 encoding for Windows console
if sys.platform == 'win32':
import io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
# Ajouter le repertoire parent au path pour importer le serveur
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from mcp_server import get_real_estate_data
# Utiliser la vraie base de donnees
DB_PATH = os.getenv("DB_PATH", r"C:\Users\miche\AppData\Roaming\com.teymz.wealthfolio\app.db")
def test_get_real_estate_data():
"""Test de récupération des données immobilières depuis app_settings."""
print("\n" + "="*80)
print("TEST: get_real_estate_data()")
print("="*80)
try:
data = get_real_estate_data()
print(f"\n✓ Données récupérées avec succès")
print(f" - Version: {data.get('version', 'N/A')}")
print(f" - Nombre de propriétés: {len(data.get('properties', []))}")
print(f" - Nombre de crédits: {len(data.get('loans', []))}")
print(f" - Nombre de valorisations: {len(data.get('valuations', []))}")
print(f" - Nombre d'événements de crédit: {len(data.get('loanEvents', []))}")
# Afficher un échantillon des données
if data.get('properties'):
print("\n Exemple de propriété:")
prop = data['properties'][0]
print(f" - Nom: {prop.get('name')}")
print(f" - Type: {prop.get('type')}")
print(f" - Valeur: {prop.get('currentValue')} {prop.get('currency')}")
if data.get('loans'):
print("\n Exemple de crédit:")
loan = data['loans'][0]
print(f" - Nom: {loan.get('name')}")
print(f" - Solde: {loan.get('currentBalance')} {loan.get('currency')}")
print(f" - Taux: {loan.get('interestRate')}%")
return True
except Exception as e:
print(f"\n✗ ERREUR: {str(e)}")
import traceback
traceback.print_exc()
return False
def test_properties_structure():
"""Test de la structure des propriétés."""
print("\n" + "="*80)
print("TEST: Structure des propriétés")
print("="*80)
try:
data = get_real_estate_data()
properties = data.get('properties', [])
if not properties:
print("\n⚠ Aucune propriété trouvée (normal si base vide)")
return True
# Vérifier la structure de chaque propriété
required_fields = ['id', 'name', 'type', 'purchasePrice', 'currentValue', 'currency']
for i, prop in enumerate(properties, 1):
print(f"\nPropriété {i}: {prop.get('name', 'Sans nom')}")
# Vérifier les champs requis
missing_fields = [field for field in required_fields if field not in prop]
if missing_fields:
print(f" ✗ Champs manquants: {', '.join(missing_fields)}")
return False
print(f" ✓ Tous les champs requis sont présents")
# Vérifier les types
try:
float(prop['purchasePrice'])
float(prop['currentValue'])
print(f" ✓ Valeurs numériques valides")
except (ValueError, TypeError) as e:
print(f" ✗ Erreur de type: {e}")
return False
print(f"\n✓ Structure validée pour {len(properties)} propriété(s)")
return True
except Exception as e:
print(f"\n✗ ERREUR: {str(e)}")
import traceback
traceback.print_exc()
return False
def test_loans_structure():
"""Test de la structure des crédits."""
print("\n" + "="*80)
print("TEST: Structure des crédits")
print("="*80)
try:
data = get_real_estate_data()
loans = data.get('loans', [])
if not loans:
print("\n⚠ Aucun crédit trouvé (normal si base vide)")
return True
# Vérifier la structure de chaque crédit
required_fields = ['id', 'propertyId', 'name', 'originalAmount', 'currentBalance',
'interestRate', 'monthlyPayment', 'startDate', 'endDate']
for i, loan in enumerate(loans, 1):
print(f"\nCrédit {i}: {loan.get('name', 'Sans nom')}")
# Vérifier les champs requis
missing_fields = [field for field in required_fields if field not in loan]
if missing_fields:
print(f" ✗ Champs manquants: {', '.join(missing_fields)}")
return False
print(f" ✓ Tous les champs requis sont présents")
# Vérifier les types et cohérence
try:
original = float(loan['originalAmount'])
current = float(loan['currentBalance'])
rate = float(loan['interestRate'])
payment = float(loan['monthlyPayment'])
if current > original:
print(f" ⚠ ATTENTION: Solde actuel ({current}) > montant initial ({original})")
if rate < 0 or rate > 20:
print(f" ⚠ ATTENTION: Taux d'intérêt inhabituel: {rate}%")
print(f" ✓ Valeurs numériques valides")
except (ValueError, TypeError) as e:
print(f" ✗ Erreur de type: {e}")
return False
print(f"\n✓ Structure validée pour {len(loans)} crédit(s)")
return True
except Exception as e:
print(f"\n✗ ERREUR: {str(e)}")
import traceback
traceback.print_exc()
return False
def test_real_estate_summary_calculations():
"""Test des calculs de la synthèse immobilière."""
print("\n" + "="*80)
print("TEST: Calculs de la synthèse immobilière")
print("="*80)
try:
data = get_real_estate_data()
properties = data.get('properties', [])
loans = data.get('loans', [])
# Calculs manuels pour vérification
total_property_value = sum(float(p.get('currentValue', 0)) for p in properties)
total_purchase_price = sum(float(p.get('purchasePrice', 0)) for p in properties)
total_capital_gain = total_property_value - total_purchase_price
total_debt = sum(float(l.get('currentBalance', 0)) for l in loans)
total_original_debt = sum(float(l.get('originalAmount', 0)) for l in loans)
total_monthly_payment = sum(float(l.get('monthlyPayment', 0)) for l in loans)
net_real_estate_value = total_property_value - total_debt
print(f"\nCalculs effectués:")
print(f" - Valeur totale des propriétés: {total_property_value:,.2f}")
print(f" - Prix d'achat total: {total_purchase_price:,.2f}")
print(f" - Plus-value totale: {total_capital_gain:,.2f}")
print(f" - Dette totale: {total_debt:,.2f}")
print(f" - Valeur nette immobilière: {net_real_estate_value:,.2f}")
if total_property_value > 0:
ltv = (total_debt / total_property_value) * 100
print(f" - LTV (taux d'endettement): {ltv:.1f}%")
if ltv > 80:
print(f" ⚠ ATTENTION: LTV élevé (>80%)")
print(f"\n✓ Calculs validés")
return True
except Exception as e:
print(f"\n✗ ERREUR: {str(e)}")
import traceback
traceback.print_exc()
return False
def test_total_net_worth_integration():
"""Test de l'intégration patrimoine financier + immobilier."""
print("\n" + "="*80)
print("TEST: Patrimoine net total (financier + immobilier)")
print("="*80)
try:
# 1. Récupérer les actifs financiers
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
c = conn.cursor()
c.execute("""
SELECT CAST(total_value AS REAL) as total_value, base_currency
FROM daily_account_valuation
WHERE account_id = 'TOTAL'
AND valuation_date = (SELECT MAX(valuation_date) FROM daily_account_valuation)
""")
result = c.fetchone()
conn.close()
financial_assets = result['total_value'] if result else 0
currency = result['base_currency'] if result else 'EUR'
print(f"\nActifs financiers: {financial_assets:,.2f} {currency}")
# 2. Récupérer le patrimoine immobilier
data = get_real_estate_data()
properties = data.get('properties', [])
loans = data.get('loans', [])
real_estate_gross_value = sum(float(p.get('currentValue', 0)) for p in properties)
total_debt = sum(float(l.get('currentBalance', 0)) for l in loans)
real_estate_net_value = real_estate_gross_value - total_debt
print(f"Immobilier brut: {real_estate_gross_value:,.2f} {currency}")
print(f"Dettes: {total_debt:,.2f} {currency}")
print(f"Immobilier net: {real_estate_net_value:,.2f} {currency}")
# 3. Calculer le patrimoine net total
net_worth = financial_assets + real_estate_net_value
print(f"\n{'='*50}")
print(f"PATRIMOINE NET TOTAL: {net_worth:,.2f} {currency}")
print(f"{'='*50}")
# Répartition
if net_worth > 0:
fin_pct = (financial_assets / net_worth) * 100
re_pct = (real_estate_net_value / net_worth) * 100
print(f"\nRépartition:")
print(f" - Financier: {fin_pct:.1f}%")
print(f" - Immobilier: {re_pct:.1f}%")
print(f"\n✓ Intégration validée")
return True
except Exception as e:
print(f"\n✗ ERREUR: {str(e)}")
import traceback
traceback.print_exc()
return False
def test_loan_filter_by_property():
"""Test du filtrage des crédits par propriété."""
print("\n" + "="*80)
print("TEST: Filtrage des crédits par propriété")
print("="*80)
try:
data = get_real_estate_data()
properties = data.get('properties', [])
loans = data.get('loans', [])
if not properties or not loans:
print("\n⚠ Données insuffisantes pour ce test")
return True
# Tester le filtrage pour chaque propriété
for prop in properties:
property_id = prop.get('id')
property_name = prop.get('name', 'Sans nom')
# Filtrer les crédits
filtered_loans = [l for l in loans if l.get('propertyId') == property_id]
print(f"\nPropriété: {property_name} (ID: {property_id})")
print(f" - Crédits associés: {len(filtered_loans)}")
for loan in filtered_loans:
print(f" • {loan.get('name')}: {loan.get('currentBalance')} {loan.get('currency')}")
print(f"\n✓ Filtrage validé")
return True
except Exception as e:
print(f"\n✗ ERREUR: {str(e)}")
import traceback
traceback.print_exc()
return False
def run_all_tests():
"""Exécute tous les tests."""
print("\n" + "="*80)
print("TESTS DES FONCTIONNALITÉS IMMOBILIÈRES")
print("="*80)
print(f"Base de données: {DB_PATH}")
if not os.path.exists(DB_PATH):
print(f"\n✗ ERREUR: Base de données non trouvée: {DB_PATH}")
return
tests = [
("Récupération des données", test_get_real_estate_data),
("Structure des propriétés", test_properties_structure),
("Structure des crédits", test_loans_structure),
("Calculs de synthèse", test_real_estate_summary_calculations),
("Patrimoine net total", test_total_net_worth_integration),
("Filtrage par propriété", test_loan_filter_by_property),
]
results = []
for test_name, test_func in tests:
try:
result = test_func()
results.append((test_name, result))
except Exception as e:
print(f"\n✗ ERREUR CRITIQUE dans {test_name}: {str(e)}")
import traceback
traceback.print_exc()
results.append((test_name, False))
# Résumé
print("\n" + "="*80)
print("RÉSUMÉ DES TESTS")
print("="*80)
passed = sum(1 for _, result in results if result)
total = len(results)
for test_name, result in results:
status = "✓ PASS" if result else "✗ FAIL"
print(f"{status}: {test_name}")
print(f"\nRésultat: {passed}/{total} tests réussis")
if passed == total:
print("\n🎉 Tous les tests sont passés!")
else:
print(f"\n⚠ {total - passed} test(s) échoué(s)")
if __name__ == "__main__":
run_all_tests()