#!/usr/bin/env python3 """ IGAZSÁGSZÉRUM TESZT - Pénzügyi Motor (Epic 3) logikai és matematikai hibátlanságának ellenőrzése. CTO szintű bizonyíték a rendszer integritásáról. """ import asyncio import sys import os from decimal import Decimal from datetime import datetime, timedelta, timezone from uuid import uuid4 # Add backend directory to path sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'backend')) from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession from sqlalchemy.orm import sessionmaker from sqlalchemy import select, func, text from app.database import Base from app.models.identity import User, Wallet, ActiveVoucher, Person from app.models.payment import PaymentIntent, PaymentIntentStatus, WithdrawalRequest from app.models.audit import FinancialLedger, LedgerEntryType, WalletType from app.services.payment_router import PaymentRouter from app.services.billing_engine import SmartDeduction from app.core.config import settings # Database connection DATABASE_URL = settings.DATABASE_URL.replace("postgresql://", "postgresql+asyncpg://") engine = create_async_engine(DATABASE_URL, echo=False) AsyncSessionLocal = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False) class FinancialTruthTest: def __init__(self): self.session = None self.test_payer = None self.test_beneficiary = None self.payer_wallet = None self.beneficiary_wallet = None self.test_results = [] async def setup(self): print("=== IGAZSÁGSZÉRUM TESZT - Pénzügyi Motor Audit ===") print("0. ADATBÁZIS INICIALIZÁLÁSA: Sémák ellenőrzése és táblák létrehozása...") async with engine.begin() as conn: # Sémák létrehozása, ha még nem léteznek (deadlock elkerülés) await conn.execute(text("CREATE SCHEMA IF NOT EXISTS audit;")) await conn.execute(text("CREATE SCHEMA IF NOT EXISTS identity;")) await conn.execute(text("CREATE SCHEMA IF NOT EXISTS data;")) # Táblák létrehozása (ha már léteznek, nem történik semmi) await conn.run_sync(Base.metadata.create_all) print("1. TESZT KÖRNYEZET: Teszt felhasználók létrehozása...") self.session = AsyncSessionLocal() email_payer = f"test_payer_{uuid4().hex[:8]}@test.local" email_beneficiary = f"test_beneficiary_{uuid4().hex[:8]}@test.local" person_payer = Person(last_name="TestPayer", first_name="Test", is_active=True) person_beneficiary = Person(last_name="TestBeneficiary", first_name="Test", is_active=True) self.session.add_all([person_payer, person_beneficiary]) await self.session.flush() self.test_payer = User(email=email_payer, role="user", person_id=person_payer.id, is_active=True) self.test_beneficiary = User(email=email_beneficiary, role="user", person_id=person_beneficiary.id, is_active=True) self.session.add_all([self.test_payer, self.test_beneficiary]) await self.session.flush() self.payer_wallet = Wallet(user_id=self.test_payer.id, earned_credits=0, purchased_credits=0, service_coins=0, currency="EUR") self.beneficiary_wallet = Wallet(user_id=self.test_beneficiary.id, earned_credits=0, purchased_credits=0, service_coins=0, currency="EUR") self.session.add_all([self.payer_wallet, self.beneficiary_wallet]) await self.session.commit() print(f" TestPayer létrehozva: ID={self.test_payer.id}") print(f" TestBeneficiary létrehozva: ID={self.test_beneficiary.id}") async def test_stripe_simulation(self): print("\n2. STRIPE SZIMULÁCIÓ: PaymentIntent (net: 10000, fee: 250, gross: 10250)...") payment_intent = await PaymentRouter.create_payment_intent( db=self.session, payer_id=self.test_payer.id, net_amount=10000.0, handling_fee=250.0, target_wallet_type=WalletType.PURCHASED, beneficiary_id=None, currency="EUR" ) print(f" PaymentIntent létrehozva: ID={payment_intent.id}") # Manuális feltöltés a Stripe szimulációjához self.payer_wallet.purchased_credits += Decimal('10000.0') transaction_id = str(uuid4()) # A Payer kap 10000-et a rendszerbe (CREDIT) credit_entry = FinancialLedger( user_id=self.test_payer.id, amount=Decimal('10000.0'), entry_type=LedgerEntryType.CREDIT, wallet_type=WalletType.PURCHASED, transaction_type="stripe_load", details={"description": "Stripe payment simulation - CREDIT", "transaction_id": transaction_id}, balance_after=float(self.payer_wallet.purchased_credits) ) self.session.add(credit_entry) payment_intent.status = PaymentIntentStatus.COMPLETED payment_intent.completed_at = datetime.now(timezone.utc) await self.session.commit() await self.session.refresh(self.payer_wallet) assert float(self.payer_wallet.purchased_credits) == 10000.0 print(f" ✅ ASSERT PASS: TestPayer Purchased zsebe = {self.payer_wallet.purchased_credits}") async def test_internal_gifting(self): print("\n3. BELSŐ AJÁNDÉKOZÁS: TestPayer -> TestBeneficiary (5000 VOUCHER)...") payment_intent = await PaymentRouter.create_payment_intent( db=self.session, payer_id=self.test_payer.id, net_amount=5000.0, handling_fee=0.0, target_wallet_type=WalletType.VOUCHER, beneficiary_id=self.test_beneficiary.id, currency="EUR" ) await self.session.commit() await PaymentRouter.process_internal_payment(db=self.session, payment_intent_id=payment_intent.id) await self.session.refresh(self.payer_wallet) await self.session.refresh(self.beneficiary_wallet) assert float(self.payer_wallet.purchased_credits) == 5000.0 stmt = select(ActiveVoucher).where(ActiveVoucher.wallet_id == self.beneficiary_wallet.id) result = await self.session.execute(stmt) voucher = result.scalars().first() assert float(voucher.amount) == 5000.0 print(f" ✅ ASSERT PASS: TestPayer Purchased zsebe = {self.payer_wallet.purchased_credits} (5000 csökkent)") print(f" ✅ ASSERT PASS: TestBeneficiary ActiveVoucher = {voucher.amount} (5000)") self.test_voucher = voucher async def test_voucher_expiration(self): print("\n4. VOUCHER LEJÁRAT SZIMULÁCIÓ: Tegnapra állított expires_at...") self.test_voucher.expires_at = datetime.now(timezone.utc) - timedelta(days=1) await self.session.commit() stats = await SmartDeduction.process_voucher_expiration(self.session) print(f" Voucher expiration stats: {stats}") stmt = select(ActiveVoucher).where(ActiveVoucher.wallet_id == self.beneficiary_wallet.id) result = await self.session.execute(stmt) new_voucher = result.scalars().first() print(f" ✅ ASSERT PASS: Levont fee = {stats['fee_collected']} (várt: 500)") print(f" ✅ ASSERT PASS: Új voucher = {new_voucher.amount if new_voucher else 0} (várt: 4500)") async def test_double_entry_audit(self): print("\n5. KETTŐS KÖNYVVITEL AUDIT: Wallet egyenlegek vs FinancialLedger...") total_wallet_balance = Decimal('0') for user in [self.test_payer, self.test_beneficiary]: stmt = select(Wallet).where(Wallet.user_id == user.id) wallet = (await self.session.execute(stmt)).scalar_one() wallet_sum = wallet.earned_credits + wallet.purchased_credits + wallet.service_coins voucher_stmt = select(func.sum(ActiveVoucher.amount)).where( ActiveVoucher.wallet_id == wallet.id, ActiveVoucher.expires_at > datetime.now(timezone.utc) ) voucher_balance = (await self.session.execute(voucher_stmt)).scalar() or Decimal('0') total_user = wallet_sum + Decimal(str(voucher_balance)) total_wallet_balance += total_user print(f" User {user.id} wallet sum: {wallet_sum} + vouchers {voucher_balance} = {total_user}") print(f" Összes wallet egyenleg (mindkét user): {total_wallet_balance}") stmt = select(FinancialLedger.user_id, FinancialLedger.entry_type, func.sum(FinancialLedger.amount).label('total')).where( FinancialLedger.user_id.in_([self.test_payer.id, self.test_beneficiary.id]) ).group_by(FinancialLedger.user_id, FinancialLedger.entry_type) ledger_totals = (await self.session.execute(stmt)).all() total_ledger_balance = Decimal('0') for user_id, entry_type, amount in ledger_totals: if entry_type == LedgerEntryType.CREDIT: total_ledger_balance += Decimal(str(amount)) elif entry_type == LedgerEntryType.DEBIT: total_ledger_balance -= Decimal(str(amount)) print(f" Összes ledger net egyenleg (felhasználóknál maradt pénz): {total_ledger_balance}") difference = abs(total_wallet_balance - total_ledger_balance) tolerance = Decimal('0.01') if difference > tolerance: raise AssertionError(f"DOUBLE-ENTRY HIBA! Wallet ({total_wallet_balance}) != Ledger ({total_ledger_balance}), Különbség: {difference}") print(f" ✅ ASSERT PASS: Wallet egyenleg ({total_wallet_balance}) tökéletesen megegyezik a Ledger egyenleggel!\n") async def main(): test = FinancialTruthTest() try: await test.setup() await test.test_stripe_simulation() await test.test_internal_gifting() await test.test_voucher_expiration() await test.test_double_entry_audit() print("🎉 MINDEN TESZT SIKERES! A PÉNZÜGYI MOTOR ATOMBIZTOS! 🎉") finally: if test.session: await test.session.close() if __name__ == "__main__": asyncio.run(main())