2026.03.29 20:00 Gitea_manager javítás előtt
This commit is contained in:
25
archive/tests_internal/README.md
Executable file
25
archive/tests_internal/README.md
Executable file
@@ -0,0 +1,25 @@
|
||||
# 🛠️ Internal Diagnostic Tools
|
||||
|
||||
Ez a mappa a rendszer stabilitását ellenőrző szkripteket tartalmazza.
|
||||
Futtatásuk a konténeren belül javasolt.
|
||||
|
||||
### 1. Schema Sync (`compare_schema.py`)
|
||||
**Mikor használd:** Ha új oszlopot adtál a modellhez, vagy nem indul el a rendszer DB hiba miatt.
|
||||
**Futtatás:** `docker compose exec api python -m app.tests_internal.compare_schema`
|
||||
docker compose exec api python -m app.tests_internal.diagnostics.compare_schema
|
||||
|
||||
### 2. API Health (`check_api.py`)
|
||||
**Mikor használd:** Refaktorálás után. Ellenőrzi, hogy az összes API "kapu" nyitva van-e.
|
||||
**Futtatás:** `docker compose exec api python -m app.tests_internal.check_api`
|
||||
docker compose exec api python -m app.tests_internal.diagnostics.check_api
|
||||
|
||||
### 3. Geo Search Test (`test_postgis.py`)
|
||||
**Mikor használd:** Ha a szervizkereső nem ad vissza eredményt, vagy SQL hibát dob.
|
||||
**Futtatás:** `docker compose exec api python -m app.tests_internal.test_postgis`
|
||||
|
||||
### 3. Rendszerdiagnosztika (`diagnose_system.py`)
|
||||
- **Cél:** Mély ellenőrzés: DB kapcsolat, i18n szótárak, Master Data mezők és konfigurációk.
|
||||
- **Mikor használd:** Telepítés után, vagy ha a rendszer "furcsán" viselkedik (pl. angolul beszél magyar helyett).
|
||||
- **Indítás:**
|
||||
|
||||
docker compose exec api python -m app.tests_internal.diagnostics.diagnose_system
|
||||
0
archive/tests_internal/__init__.py
Executable file
0
archive/tests_internal/__init__.py
Executable file
0
archive/tests_internal/diagnostics/__init__.py
Executable file
0
archive/tests_internal/diagnostics/__init__.py
Executable file
53
archive/tests_internal/diagnostics/check_api.py
Executable file
53
archive/tests_internal/diagnostics/check_api.py
Executable file
@@ -0,0 +1,53 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/tests_internal/diagnostics/check_api.py
|
||||
import requests
|
||||
import json
|
||||
|
||||
# THOUGHT PROCESS:
|
||||
# 1. Az API konténeren belül futva a localhost:8000 a célpont.
|
||||
# 2. Csak olyan végpontokat tesztelünk, amik szerepelnek a v1/api.py-ben.
|
||||
# 3. A 401/405 kódokat elfogadjuk 'OK'-nak, mert azt jelentik, hogy a szerver
|
||||
# látja az útvonalat, csak hitelesítést vagy más HTTP metódust vár.
|
||||
# 4. A 404 azt jelenti, hogy a router nincs jól regisztrálva.
|
||||
# 5. A 500 azt jelenti, hogy a kód elszállt (pl. AttributeError a main.py-ban).
|
||||
|
||||
base_url = 'http://localhost:8000'
|
||||
|
||||
# A valós, api.py-ben definiált útvonalak:
|
||||
tests = [
|
||||
('Health Check', '/health', 'GET'),
|
||||
('Auth Login (Entry)', '/api/v1/auth/login', 'POST'), # Létezik
|
||||
('Catalog Makes', '/api/v1/catalog/makes', 'GET'), # Létezik
|
||||
('Service Hunt', '/api/v1/services/hunt', 'POST'), # Létezik
|
||||
('Admin Health', '/api/v1/admin/health-monitor', 'GET'), # Létezik
|
||||
('My Organizations', '/api/v1/organizations/my', 'GET') # Létezik
|
||||
]
|
||||
|
||||
print('\n--- 🧪 API VÉGPONT DIAGNOSZTIKA ---')
|
||||
print(f"{'Végpont neve':20} | {'Útvonal':25} | {'Állapot'}")
|
||||
print("-" * 65)
|
||||
|
||||
for name, endpoint, method in tests:
|
||||
try:
|
||||
url = f"{base_url}{endpoint}"
|
||||
# A POST hívásokhoz üres adatot küldünk, hogy ne 422-t kapjunk a hiányzó body miatt
|
||||
resp = requests.request(method, url, timeout=5, json={})
|
||||
|
||||
# Logika:
|
||||
# 200: Tökéletes
|
||||
# 401: Él, de login kell (JÓ)
|
||||
# 405: Él, de pl. GET helyett POST kell (JÓ - az útvonal létezik)
|
||||
# 422: Él, de hiányoznak a küldött adatok (JÓ - a validáció működik)
|
||||
|
||||
if resp.status_code in [200, 401, 405, 422]:
|
||||
status_msg = f"✅ OK ({resp.status_code})"
|
||||
elif resp.status_code == 404:
|
||||
status_msg = f"❌ HIÁNYZIK (404)"
|
||||
else:
|
||||
status_msg = f"🔥 SZERVER HIBA ({resp.status_code})"
|
||||
|
||||
print(f"{name:20} | {endpoint:25} | {status_msg}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"{name:20} | {endpoint:25} | 🔌 ELÉRHETETLEN")
|
||||
|
||||
print("\n💡 Megjegyzés: Ha a Health Check továbbra is 500, az a main.py-ban lévő elírás miatt van.")
|
||||
157
archive/tests_internal/diagnostics/diagnose_system.py
Executable file
157
archive/tests_internal/diagnostics/diagnose_system.py
Executable file
@@ -0,0 +1,157 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/tests_internal/diagnostics/diagnose_system.py
|
||||
"""
|
||||
🛰️ SENTINEL SYSTEM DIAGNOSTICS - MB2.0 (2026)
|
||||
---------------------------------------------
|
||||
CÉL: A rendszer mélyszintű integritásának és működőképességének auditálása.
|
||||
|
||||
FŐBB TESZTEK:
|
||||
1. Adatbázis Kapcsolat: PostgreSQL aszinkron elérés ellenőrzése.
|
||||
2. Séma Integritás: Kritikus Master Data táblák és mezők meglétének vizsgálata.
|
||||
3. Rendszer Paraméterek: A Sentinel központi konfigurációs táblájának ellenőrzése.
|
||||
4. i18n Motor: Nyelvi gyorsítótár (Cache) és fordítási mechanizmus tesztje.
|
||||
5. Robot Pipeline: Staging (Hunter) és Gold (Catalog) rekordok számlálása.
|
||||
|
||||
FUTTATÁS:
|
||||
docker compose exec api python -m app.tests_internal.diagnostics.diagnose_system
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from sqlalchemy import text, select, func
|
||||
|
||||
# 🛠️ KRITIKUS IMPORT KÖRNYEZET ELLENŐRZÉSE
|
||||
try:
|
||||
from app.core.config import settings
|
||||
# Megjegyzés: A projekt struktúrájától függően AsyncSessionLocal az app.database-ben van
|
||||
from app.database import AsyncSessionLocal
|
||||
from app.services.translation_service import translation_service
|
||||
from app.models.system import SystemParameter
|
||||
from app.models.identity import User
|
||||
from app.models.marketplace.organization import Organization
|
||||
from app.models import AssetCatalog
|
||||
from app.models import VehicleModelDefinition
|
||||
except ImportError as e:
|
||||
print(f"\n❌ [KRITIKUS HIBA] Az importálás nem sikerült: {e}")
|
||||
print("💡 Javaslat: Ellenőrizd a PYTHONPATH-t és a __init__.py fájlok meglétét!")
|
||||
sys.exit(1)
|
||||
|
||||
# SQL logolás némítása a tiszta kimenet érdekében
|
||||
logging.getLogger('sqlalchemy.engine').setLevel(logging.WARNING)
|
||||
|
||||
async def diagnose():
|
||||
start_time = datetime.now()
|
||||
print("\n" + "═"*70)
|
||||
print(f"🛰️ SENTINEL SYSTEM DIAGNOSTICS - MB2.0 | {start_time.strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
print("═"*70 + "\n")
|
||||
|
||||
async with AsyncSessionLocal() as session:
|
||||
|
||||
# --- 1. CSATLAKOZÁS ÉS ADATBÁZIS PING ---
|
||||
print("1️⃣ Kapcsolódási teszt...")
|
||||
try:
|
||||
await session.execute(text("SELECT 1"))
|
||||
print(" [✅ OK] PostgreSQL aszinkron kapcsolat aktív.")
|
||||
except Exception as e:
|
||||
print(f" [❌ HIBA] Nem sikerült kapcsolódni az adatbázishoz: {e}")
|
||||
return
|
||||
|
||||
# --- 2. SÉMA INTEGRITÁS (Master Data Audit) ---
|
||||
print("\n2️⃣ Séma integritás ellenőrzése (Kritikus mezők)...")
|
||||
# Tábla neve (sémával) | Elvárt oszlopok listája
|
||||
tables_to_check = [
|
||||
("identity.users", ["preferred_language", "scope_id", "is_active"]),
|
||||
("fleet.organizations", ["org_type", "folder_slug", "is_active"]),
|
||||
("data.assets", ["owner_org_id", "catalog_id", "vin"]),
|
||||
# "asset_catalog" helyett "vehicle_catalog"
|
||||
("vehicle.vehicle_catalog", ["make", "model", "factory_data"]),
|
||||
("vehicle.vehicle_model_definitions", ["status", "raw_search_context"])
|
||||
]
|
||||
|
||||
for table, columns in tables_to_check:
|
||||
try:
|
||||
schema_name, table_name = table.split('.')
|
||||
query = text(f"""
|
||||
SELECT column_name FROM information_schema.columns
|
||||
WHERE table_schema = '{schema_name}' AND table_name = '{table_name}';
|
||||
""")
|
||||
res = await session.execute(query)
|
||||
existing_cols = [row[0] for row in res.fetchall()]
|
||||
|
||||
if not existing_cols:
|
||||
print(f" [❌ HIBA] A tábla NEM létezik: {table}")
|
||||
continue
|
||||
|
||||
missing = [c for c in columns if c not in existing_cols]
|
||||
if not missing:
|
||||
print(f" [✅ OK] {table:35} (Minden mező rendben)")
|
||||
else:
|
||||
print(f" [⚠️ HIÁNY] {table:35} | Hiányzó mezők: {', '.join(missing)}")
|
||||
except Exception as e:
|
||||
print(f" [❌ HIBA] Hiba a(z) {table} ellenőrzésekor: {e}")
|
||||
|
||||
# --- 3. RENDSZER PARAMÉTEREK (Sentinel Config) ---
|
||||
print("\n3️⃣ System Parameters (Sentinel Config) ellenőrzése...")
|
||||
try:
|
||||
res = await session.execute(select(SystemParameter))
|
||||
params = res.scalars().all()
|
||||
if params:
|
||||
print(f" [✅ OK] Talált paraméterek: {len(params)} db")
|
||||
# Kritikus kulcsok, amiknek illik lenniük
|
||||
critical_keys = ["SECURITY_MAX_RECORDS_PER_HOUR", "VEHICLE_LIMIT"]
|
||||
existing_keys = [p.key for p in params]
|
||||
for ck in critical_keys:
|
||||
status = "✔️" if ck in existing_keys else "❌ (Hiányzik)"
|
||||
print(f" {status} {ck}")
|
||||
else:
|
||||
print(" [⚠️ FIGYELEM] A system_parameters tábla üres! Futtasd a seedert.")
|
||||
except Exception as e:
|
||||
print(f" [❌ HIBA] SystemParameter lekérdezési hiba: {e}")
|
||||
|
||||
# --- 4. i18n ÉS CACHE MOTOR ---
|
||||
print("\n4️⃣ Nyelvi motor és i18n Cache ellenőrzése...")
|
||||
try:
|
||||
# Gyorsítótár frissítése az adatbázisból
|
||||
await translation_service.load_cache(session)
|
||||
|
||||
test_key = "COMMON.SAVE"
|
||||
test_val = translation_service.get_text(test_key, "hu")
|
||||
|
||||
# Ha a visszakapott érték nem egyezik a kulccsal, akkor van fordítás
|
||||
if test_val and test_val != f"[{test_key}]":
|
||||
print(f" [✅ OK] Fordítás sikeres (HU): '{test_key}' -> '{test_val}'")
|
||||
else:
|
||||
print(f" [❌ HIBA] Fordítás nem működik. Nincs betöltött adat vagy hibás a cache.")
|
||||
except Exception as e:
|
||||
print(f" [❌ HIBA] Nyelvi motor hiba: {e}")
|
||||
|
||||
# --- 5. ROBOT ELŐKÉSZÜLETEK (Pipeline MDM) ---
|
||||
print("\n5️⃣ Robot Pipeline (MDM Staging) állapot...")
|
||||
try:
|
||||
# Hunter robot eredményei (nyers adatok)
|
||||
res_hunter = await session.execute(
|
||||
select(func.count(VehicleModelDefinition.id)).where(VehicleModelDefinition.status == 'unverified')
|
||||
)
|
||||
unverified_count = res_hunter.scalar() or 0
|
||||
|
||||
# Catalog robot eredményei (tisztított adatok)
|
||||
res_gold = await session.execute(select(func.count(AssetCatalog.id)))
|
||||
gold_count = res_gold.scalar() or 0
|
||||
|
||||
print(f" [📊 STAT] Feldolgozatlan rekordok (Staging): {unverified_count} db")
|
||||
print(f" [📊 STAT] Validált arany rekordok (Catalog): {gold_count} db")
|
||||
except Exception as e:
|
||||
print(f" [❌ HIBA] Robot-statisztika hiba: {e}")
|
||||
|
||||
duration = (datetime.now() - start_time).total_seconds()
|
||||
print("\n" + "═"*70)
|
||||
print(f"🏁 DIAGNOSZTIKA BEFEJEZŐDÖTT | Időtartam: {duration:.2f} mp")
|
||||
print("═"*70 + "\n")
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
asyncio.run(diagnose())
|
||||
except KeyboardInterrupt:
|
||||
print("\n🛑 Diagnosztika megszakítva a felhasználó által.")
|
||||
sys.exit(0)
|
||||
0
archive/tests_internal/fixes/__init__.py
Executable file
0
archive/tests_internal/fixes/__init__.py
Executable file
82
archive/tests_internal/fixes/final_admin_fix.py
Executable file
82
archive/tests_internal/fixes/final_admin_fix.py
Executable file
@@ -0,0 +1,82 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/tests_internal/fixes/final_admin_fix.py
|
||||
import asyncio
|
||||
import uuid
|
||||
from sqlalchemy import text, select
|
||||
from app.database import AsyncSessionLocal
|
||||
from app.models.identity import User, Person, UserRole
|
||||
from app.core.security import get_password_hash
|
||||
|
||||
async def run_fix():
|
||||
print("\n" + "═"*50)
|
||||
print("🛠️ ADMIN RENDSZERJAVÍTÁS ÉS INICIALIZÁLÁS (MB2.0)")
|
||||
print("═"*50)
|
||||
|
||||
async with AsyncSessionLocal() as db:
|
||||
# 1. LOGIKA: Séma ellenőrzése az 'identity' névtérben
|
||||
# Az MB2.0-ban a felhasználók már nem a 'data', hanem az 'identity' sémában vannak.
|
||||
check_query = text("""
|
||||
SELECT column_name FROM information_schema.columns
|
||||
WHERE table_schema = 'identity' AND table_name = 'users'
|
||||
""")
|
||||
res = await db.execute(check_query)
|
||||
cols = [r[0] for r in res.fetchall()]
|
||||
|
||||
if not cols:
|
||||
print("❌ HIBA: Az 'identity.users' tábla nem található. Futtasd az Alembic migrációt!")
|
||||
return
|
||||
|
||||
if "hashed_password" not in cols:
|
||||
print("❌ HIBA: A 'hashed_password' oszlop hiányzik. Az adatbázis sémája elavult.")
|
||||
return
|
||||
|
||||
# 2. LOGIKA: Admin keresése
|
||||
admin_email = "admin@profibot.hu"
|
||||
stmt = select(User).where(User.email == admin_email)
|
||||
existing_res = await db.execute(stmt)
|
||||
existing_admin = existing_res.scalar_one_or_none()
|
||||
|
||||
if existing_admin:
|
||||
print(f"⚠️ Információ: A(z) {admin_email} felhasználó már létezik.")
|
||||
# Opcionális: Jelszó kényszerített frissítése, ha elfelejtetted
|
||||
# existing_admin.hashed_password = get_password_hash("Admin123!")
|
||||
# await db.commit()
|
||||
else:
|
||||
try:
|
||||
# 3. LOGIKA: Person és User létrehozása (MB2.0 Standard)
|
||||
# Előbb létrehozzuk a fizikai személyt
|
||||
new_person = Person(
|
||||
id_uuid=uuid.uuid4(),
|
||||
first_name="Rendszer",
|
||||
last_name="Adminisztrátor",
|
||||
is_active=True
|
||||
)
|
||||
db.add(new_person)
|
||||
await db.flush() # ID lekérése a mentés előtt
|
||||
|
||||
# Létrehozzuk a felhasználói fiókot az Admin role-al
|
||||
new_admin = User(
|
||||
email=admin_email,
|
||||
hashed_password=get_password_hash("Admin123!"),
|
||||
person_id=new_person.id,
|
||||
role=UserRole.superadmin, # MB2.0 enum érték
|
||||
is_active=True,
|
||||
is_deleted=False,
|
||||
preferred_language="hu"
|
||||
)
|
||||
db.add(new_admin)
|
||||
|
||||
await db.commit()
|
||||
print(f"✅ SIKER: Superadmin létrehozva!")
|
||||
print(f" 📧 Email: {admin_email}")
|
||||
print(f" 🔑 Jelszó: Admin123!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ HIBA a mentés során: {e}")
|
||||
await db.rollback()
|
||||
|
||||
print("\n" + "═"*50)
|
||||
print("🏁 JAVÍTÁSI FOLYAMAT BEFEJEZŐDÖTT")
|
||||
print("═"*50 + "\n")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(run_fix())
|
||||
0
archive/tests_internal/seeds/__init__.py
Executable file
0
archive/tests_internal/seeds/__init__.py
Executable file
105
archive/tests_internal/seeds/seed_catalog.py
Executable file
105
archive/tests_internal/seeds/seed_catalog.py
Executable file
@@ -0,0 +1,105 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/tests_internal/seeds/seed_catalog.py
|
||||
"""
|
||||
🌱 MB2.0 KATALÓGUS ÉS ROBOT SEEDER
|
||||
----------------------------------
|
||||
CÉL: A járműkatalógus alapadatainak és a robotok munkalistájának feltöltése.
|
||||
|
||||
FUNKCIÓK:
|
||||
1. DiscoveryParameter: Azon városok rögzítése, ahol a Scout robot keresni fog.
|
||||
2. CatalogDiscovery: Azon márkák rögzítése, amiket a Robot 0/1 fel fog dolgozni.
|
||||
3. AssetCatalog: 'Arany' (már validált) technikai rekordok beszúrása.
|
||||
|
||||
JAVÍTÁSOK:
|
||||
- 'last_error' oszlop eltávolítva (mivel az adatbázisban nem létezik).
|
||||
- 'attempts' mező kényszerített 0 értékkel (NOT NULL kényszer miatt).
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from sqlalchemy.dialects.postgresql import insert
|
||||
from app.database import AsyncSessionLocal
|
||||
from app.models import AssetCatalog, CatalogDiscovery
|
||||
from app.models.marketplace.staged_data import DiscoveryParameter
|
||||
|
||||
# Logolás beállítása
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] Sentinel-Seed: %(message)s')
|
||||
logger = logging.getLogger("Seed-Catalog")
|
||||
|
||||
async def quick_seed():
|
||||
""" Katalógus és Discovery adatok inicializálása. """
|
||||
async with AsyncSessionLocal() as db:
|
||||
logger.info("🚀 Katalógus alapozás indítása...")
|
||||
|
||||
try:
|
||||
# 1. Felderítendő Városok (DiscoveryParameter)
|
||||
# A Scout robot ezekben a városokban kezdi meg a szervizek kutatását.
|
||||
cities = [
|
||||
("BUDAPEST", "HU"),
|
||||
("DEBRECEN", "HU"),
|
||||
("GYŐR", "HU"),
|
||||
("SZEGED", "HU")
|
||||
]
|
||||
|
||||
for city_name, country in cities:
|
||||
db.add(DiscoveryParameter(
|
||||
city=city_name,
|
||||
keyword=country,
|
||||
is_active=True
|
||||
))
|
||||
|
||||
# 2. Felderítendő Márkák és Típusok (CatalogDiscovery)
|
||||
# Ezeket a rekordokat fogja a Robot 0 és Robot 1 feldolgozni a háttérben.
|
||||
discovery_queue = [
|
||||
("SUZUKI", "ALL"),
|
||||
("TOYOTA", "ALL"),
|
||||
("HONDA", "ALL"),
|
||||
("SKODA", "ALL"),
|
||||
("YAMAHA", "ALL")
|
||||
]
|
||||
|
||||
# Use INSERT ... ON CONFLICT DO NOTHING to avoid duplicate key errors
|
||||
for m, mod in discovery_queue:
|
||||
stmt = insert(CatalogDiscovery).values(
|
||||
make=m,
|
||||
model=mod,
|
||||
status="pending",
|
||||
attempts=0,
|
||||
vehicle_class="car", # Default value
|
||||
market="GLOBAL", # Default value
|
||||
priority_score=0 # Default value
|
||||
)
|
||||
# Handle conflict on make+model+vehicle_class unique constraint
|
||||
stmt = stmt.on_conflict_do_nothing(index_elements=['make', 'model', 'vehicle_class'])
|
||||
await db.execute(stmt)
|
||||
|
||||
# 3. Arany rekordok (AssetCatalog / vehicle_catalog tábla)
|
||||
# Példa adatok, amik már átmentek a validációs folyamaton.
|
||||
gold_data = [
|
||||
AssetCatalog(
|
||||
make="SUZUKI",
|
||||
model="VITARA",
|
||||
generation="LY (2015-)",
|
||||
fuel_type="petrol",
|
||||
factory_data={"segment": "SUV", "origin": "Hungary"}
|
||||
),
|
||||
AssetCatalog(
|
||||
make="SKODA",
|
||||
model="OCTAVIA",
|
||||
generation="IV (2020-)",
|
||||
fuel_type="diesel",
|
||||
factory_data={"segment": "Sedan/Combi"}
|
||||
)
|
||||
]
|
||||
db.add_all(gold_data)
|
||||
|
||||
# Mentés végrehajtása
|
||||
await db.commit()
|
||||
logger.info("✨ Katalógus és Discovery paraméterek sikeresen rögzítve!")
|
||||
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"❌ HIBA a katalógus feltöltésekor: {e}")
|
||||
raise e
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(quick_seed())
|
||||
156
archive/tests_internal/seeds/seed_data.py
Executable file
156
archive/tests_internal/seeds/seed_data.py
Executable file
@@ -0,0 +1,156 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/tests_internal/seeds/seed_data.py
|
||||
import asyncio
|
||||
import uuid
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from sqlalchemy import text, select
|
||||
from app.database import AsyncSessionLocal
|
||||
from app.models.identity import User, Person, UserRole
|
||||
from app.models import ServiceProvider, Vote, ModerationStatus, Competition
|
||||
from app.services.social_service import SocialService
|
||||
from app.core.security import get_password_hash
|
||||
|
||||
async def run_simulation():
|
||||
async with AsyncSessionLocal() as db:
|
||||
print("--- 1. TAKARÍTÁS (MB2.0 Séma-tisztítás) ---")
|
||||
# Szigorú sorrend a kényszerek miatt (Cascade)
|
||||
# Ellenőrizzük, mely táblák léteznek
|
||||
tables_to_check = [
|
||||
("identity.users", "users"),
|
||||
("identity.persons", "persons"),
|
||||
("marketplace.service_providers", "service_providers"),
|
||||
("marketplace.votes", "votes"),
|
||||
("system.competitions", "competitions")
|
||||
]
|
||||
|
||||
existing_tables = []
|
||||
for full_name, table_name in tables_to_check:
|
||||
try:
|
||||
result = await db.execute(text(f"SELECT 1 FROM information_schema.tables WHERE table_schema = '{full_name.split('.')[0]}' AND table_name = '{table_name}'"))
|
||||
if result.scalar() == 1:
|
||||
existing_tables.append(full_name)
|
||||
else:
|
||||
print(f"⚠️ {full_name} tábla nem létezik, kihagyva a törlést")
|
||||
except Exception:
|
||||
print(f"⚠️ {full_name} tábla nem létezik, kihagyva a törlést")
|
||||
|
||||
if existing_tables:
|
||||
tables_str = ", ".join(existing_tables)
|
||||
await db.execute(text(f"TRUNCATE {tables_str} RESTART IDENTITY CASCADE"))
|
||||
await db.commit()
|
||||
else:
|
||||
print("ℹ️ Nincs törlendő tábla")
|
||||
|
||||
print("\n--- 2. SZEREPLŐK LÉTREHOZÁSA (Person + User) ---")
|
||||
users_to_create = [
|
||||
("admin@test.com", "Adminisztrátor", UserRole.superadmin),
|
||||
("good@test.com", "Rendes Srác", UserRole.user),
|
||||
("bad@test.com", "Spammer Aladár", UserRole.user),
|
||||
("voter@test.com", "Szavazó Gép", UserRole.user)
|
||||
]
|
||||
|
||||
created_users = {}
|
||||
for email, name, role in users_to_create:
|
||||
name_parts = name.split()
|
||||
first_name = name_parts[0] if name_parts else "Unknown"
|
||||
last_name = name_parts[1] if len(name_parts) > 1 else "User"
|
||||
p = Person(id_uuid=uuid.uuid4(), first_name=first_name, last_name=last_name, is_active=True)
|
||||
db.add(p)
|
||||
await db.flush()
|
||||
|
||||
u = User(
|
||||
email=email,
|
||||
hashed_password=get_password_hash("test1234"),
|
||||
person_id=p.id,
|
||||
role=role,
|
||||
is_active=True
|
||||
)
|
||||
db.add(u)
|
||||
await db.flush()
|
||||
created_users[email] = u
|
||||
|
||||
await db.commit()
|
||||
|
||||
print("\n--- 3. VERSENY INDÍTÁSA ---")
|
||||
# Ellenőrizzük, hogy a competitions tábla létezik-e
|
||||
try:
|
||||
result = await db.execute(text("SELECT 1 FROM information_schema.tables WHERE table_schema = 'system' AND table_name = 'competitions'"))
|
||||
if result.scalar() == 1:
|
||||
race = Competition(
|
||||
name="Téli Szervizvadászat",
|
||||
start_date=datetime.now(timezone.utc) - timedelta(days=1),
|
||||
end_date=datetime.now(timezone.utc) + timedelta(days=30),
|
||||
is_active=True
|
||||
)
|
||||
db.add(race)
|
||||
await db.commit()
|
||||
print("✅ Verseny létrehozva")
|
||||
else:
|
||||
print("⚠️ system.competitions tábla nem létezik, kihagyva a verseny létrehozását")
|
||||
except Exception as e:
|
||||
print(f"⚠️ Hiba a competitions tábla ellenőrzése közben: {e}, kihagyva a verseny létrehozását")
|
||||
|
||||
# Szereplők kiemelése a szimulációhoz
|
||||
good_user = created_users["good@test.com"]
|
||||
bad_user = created_users["bad@test.com"]
|
||||
voter = created_users["voter@test.com"]
|
||||
|
||||
# Ellenőrizzük, hogy a szükséges táblák léteznek-e a szociális szimulációhoz
|
||||
try:
|
||||
result = await db.execute(text("SELECT 1 FROM information_schema.tables WHERE table_schema = 'marketplace' AND table_name = 'service_providers'"))
|
||||
service_providers_exists = result.scalar() == 1
|
||||
|
||||
result = await db.execute(text("SELECT 1 FROM information_schema.tables WHERE table_schema = 'marketplace' AND table_name = 'votes'"))
|
||||
votes_exists = result.scalar() == 1
|
||||
|
||||
if service_providers_exists and votes_exists:
|
||||
print("\n--- 4. SZCENÁRIÓ A: POZITÍV VALIDÁCIÓ ---")
|
||||
# Rendes srác beküld egy szervizt
|
||||
shop = ServiceProvider(
|
||||
name="Profi Gumis",
|
||||
address="Budapest, Váci út 10.",
|
||||
added_by_user_id=good_user.id,
|
||||
status=ModerationStatus.pending
|
||||
)
|
||||
db.add(shop)
|
||||
await db.flush()
|
||||
|
||||
# Szavazatok szimulálása (SocialService használatával a pontszámítás miatt)
|
||||
print(f"Szavazás a '{shop.name}'-re...")
|
||||
# Szimulálunk 5 pozitív szavazatot különböző "virtuális" szavazóktól
|
||||
for _ in range(5):
|
||||
await SocialService.vote_for_provider(db, voter.id, shop.id, 1)
|
||||
|
||||
await db.refresh(good_user)
|
||||
print(f"Jó felhasználó hírneve: {good_user.reputation_score}")
|
||||
|
||||
print("\n--- 5. SZCENÁRIÓ B: AUTO-BAN (SPAM SZŰRÉS) ---")
|
||||
fake_shop = ServiceProvider(
|
||||
name="KAMU SZERVIZ",
|
||||
address="Nincs ilyen utca 0.",
|
||||
added_by_user_id=bad_user.id,
|
||||
status=ModerationStatus.pending
|
||||
)
|
||||
db.add(fake_shop)
|
||||
await db.flush()
|
||||
|
||||
# Leszavazás (Kell -3 a bukáshoz)
|
||||
print("Spam jelentése...")
|
||||
await SocialService.vote_for_provider(db, voter.id, fake_shop.id, -1)
|
||||
await SocialService.vote_for_provider(db, voter.id, fake_shop.id, -1)
|
||||
await SocialService.vote_for_provider(db, voter.id, fake_shop.id, -1)
|
||||
|
||||
await db.refresh(bad_user)
|
||||
print(f"Rossz felhasználó hírneve: {bad_user.reputation_score}")
|
||||
print(f"Fiók státusza: {'KITILTVA' if not bad_user.is_active else 'AKTÍV'}")
|
||||
|
||||
if not bad_user.is_active:
|
||||
print("✅ SIKER: A Sentinel automatikusan leállította a spammert!")
|
||||
else:
|
||||
print("\n⚠️ Marketplace táblák (service_providers, votes) nem léteznek, kihagyva a szociális szimulációt")
|
||||
print("ℹ️ Alap felhasználók sikeresen létrehozva")
|
||||
except Exception as e:
|
||||
print(f"\n⚠️ Hiba a táblák ellenőrzése közben: {e}, kihagyva a szociális szimulációt")
|
||||
print("ℹ️ Alap felhasználók sikeresen létrehozva")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(run_simulation())
|
||||
62
archive/tests_internal/seeds/seed_economy.py
Normal file
62
archive/tests_internal/seeds/seed_economy.py
Normal file
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Seed script az Economy 1 modulhoz: árfolyam paraméterek beszúrása a system.system_parameters táblába.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
from decimal import Decimal
|
||||
|
||||
sys.path.insert(0, "/app")
|
||||
|
||||
from sqlalchemy import select
|
||||
from app.database import AsyncSessionLocal
|
||||
from app.models.system import SystemParameter
|
||||
|
||||
|
||||
async def seed_economy():
|
||||
"""Árfolyam paraméterek beszúrása."""
|
||||
parameters = [
|
||||
{
|
||||
"key": "EXCHANGE_RATE_EUR_HUF",
|
||||
"value": "390.0",
|
||||
"description": "EUR/HUF átváltási árfolyam (1 EUR = X HUF)",
|
||||
"category": "finance",
|
||||
"is_active": True,
|
||||
},
|
||||
{
|
||||
"key": "EXCHANGE_RATE_USDC_HUF",
|
||||
"value": "380.0",
|
||||
"description": "USDC/HUF átváltási árfolyam (1 USDC = X HUF)",
|
||||
"category": "finance",
|
||||
"is_active": True,
|
||||
},
|
||||
]
|
||||
|
||||
async with AsyncSessionLocal() as session:
|
||||
for param in parameters:
|
||||
# Ellenőrizzük, hogy létezik-e már
|
||||
existing = await session.execute(
|
||||
select(SystemParameter).where(SystemParameter.key == param["key"])
|
||||
)
|
||||
existing = existing.scalar_one_or_none()
|
||||
if existing:
|
||||
print(f"⚠️ {param['key']} már létezik, kihagyva.")
|
||||
continue
|
||||
|
||||
new_param = SystemParameter(
|
||||
key=param["key"],
|
||||
value=param["value"],
|
||||
description=param["description"],
|
||||
category=param["category"],
|
||||
is_active=param["is_active"],
|
||||
)
|
||||
session.add(new_param)
|
||||
print(f"✅ {param['key']} beszúrva.")
|
||||
|
||||
await session.commit()
|
||||
print("🎉 Árfolyam paraméterek sikeresen seedelve.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(seed_economy())
|
||||
65
archive/tests_internal/seeds/seed_expertises.py
Executable file
65
archive/tests_internal/seeds/seed_expertises.py
Executable file
@@ -0,0 +1,65 @@
|
||||
import asyncio
|
||||
from app.database import AsyncSessionLocal
|
||||
from app.models.marketplace.service import ExpertiseTag
|
||||
from sqlalchemy import text
|
||||
|
||||
async def seed_expertises():
|
||||
tags = [
|
||||
# --- ALAPSZOLGÁLTATÁSOK (MECHANICS) ---
|
||||
('OIL_SERVICE', 'Időszakos szerviz / Olajcsere', 'MECHANICS'),
|
||||
('BRAKE_REPAIR', 'Fékrendszer javítás', 'MECHANICS'),
|
||||
('SUSPENSION', 'Futómű javítás és beállítás', 'MECHANICS'),
|
||||
('EXHAUST', 'Kipufogó szerviz', 'MECHANICS'),
|
||||
('CLUTCH', 'Kuplung és kettőstömegű csere', 'MECHANICS'),
|
||||
|
||||
# --- MOTOR ÉS VÁLTÓ (ENGINE_DRIVETRAIN) ---
|
||||
('ENGINE_REBUILD', 'Motorfelújítás', 'ENGINE_DRIVETRAIN'),
|
||||
('TIMING_BELT', 'Vezérlés csere', 'ENGINE_DRIVETRAIN'),
|
||||
('AUTO_GEARBOX', 'Automata váltó javítás/olajcsere', 'ENGINE_DRIVETRAIN'),
|
||||
('TURBO_REPAIR', 'Turbófeltöltő felújítás', 'ENGINE_DRIVETRAIN'),
|
||||
('INJECTOR', 'Dízel injektor / Adagoló javítás', 'ENGINE_DRIVETRAIN'),
|
||||
('DPF_CLEAN', 'DPF / Részecskeszűrő tisztítás', 'ENGINE_DRIVETRAIN'),
|
||||
|
||||
# --- ELEKTRONIKA (ELECTRICAL) ---
|
||||
('DIAGNOSTICS', 'Számítógépes diagnosztika', 'ELECTRICAL'),
|
||||
('AC_REPAIR', 'Klíma javítás és töltés', 'ELECTRICAL'),
|
||||
('BATTERY', 'Akkumulátor szerviz', 'ELECTRICAL'),
|
||||
('HYBRID_EV', 'Hibrid és Elektromos autó szerviz', 'ELECTRICAL'),
|
||||
('CHIP_TUNING', 'Szoftveres optimalizálás / Tuning', 'ELECTRICAL'),
|
||||
('ADAS', 'Vezetéstámogató rendszerek kalibrálása', 'ELECTRICAL'),
|
||||
|
||||
# --- GUMI ÉS KERÉK (TYRES) ---
|
||||
('TYRE_CHANGE', 'Gumiszerelés és centírozás', 'TYRES'),
|
||||
('WHEEL_REPAIR', 'Alufelni javítás / Görgőzés', 'TYRES'),
|
||||
|
||||
# --- KAROSSZÉRIA (BODY) ---
|
||||
('BODY_REPAIR', 'Karosszéria lakatolás', 'BODY'),
|
||||
('PAINTING', 'Autófényezés', 'BODY'),
|
||||
('GLASS_REPAIR', 'Szélvédő javítás és csere', 'BODY'),
|
||||
('PDR', 'Jégkár és horpadásjavítás (PDR)', 'BODY'),
|
||||
|
||||
# --- SEGÉLY ÉS SZÁLLÍTÁS (EMERGENCY) ---
|
||||
('TOWING', 'Autómentés / Vontatás', 'EMERGENCY'),
|
||||
('ROADSIDE_ASSIST', 'Segélyszolgálat / Helyszíni javítás', 'EMERGENCY'),
|
||||
('LOCKSMITH', 'Autózár szerviz / Kulcsmásolás', 'EMERGENCY'),
|
||||
|
||||
# --- EGYÉB JÁRMŰVEK (VEHICLE_TYPES) ---
|
||||
('MOTO_SERVICE', 'Motorkerékpár szerviz', 'VEHICLE_TYPES'),
|
||||
('TRUCK_SERVICE', 'Tehergépjármű szerviz', 'VEHICLE_TYPES'),
|
||||
('AGRI_SERVICE', 'Mezőgazdasági gép szerviz', 'VEHICLE_TYPES'),
|
||||
]
|
||||
|
||||
async with AsyncSessionLocal() as db:
|
||||
print("🌱 Szakmai címkék feltöltése...")
|
||||
for key, name, cat in tags:
|
||||
stmt = text("""
|
||||
INSERT INTO marketplace.expertise_tags (key, name_hu, category, is_official)
|
||||
VALUES (:k, :n, :c, true)
|
||||
ON CONFLICT (key) DO UPDATE SET name_hu = EXCLUDED.name_hu, category = EXCLUDED.category
|
||||
""")
|
||||
await db.execute(stmt, {"k": key, "n": name, "c": cat})
|
||||
await db.commit()
|
||||
print(f"✅ {len(tags)} címke rögzítve.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(seed_expertises())
|
||||
74
archive/tests_internal/seeds/seed_honda.py
Executable file
74
archive/tests_internal/seeds/seed_honda.py
Executable file
@@ -0,0 +1,74 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/tests_internal/seeds/seed_honda.py
|
||||
import asyncio
|
||||
import logging
|
||||
from sqlalchemy import select
|
||||
from app.database import AsyncSessionLocal
|
||||
from app.models import AssetCatalog
|
||||
from app.models.marketplace.staged_data import DiscoveryParameter
|
||||
|
||||
# Logolás beállítása
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] Sentinel-Seed: %(message)s')
|
||||
logger = logging.getLogger("Honda-Seeder")
|
||||
|
||||
async def seed_honda():
|
||||
"""
|
||||
Honda specifikus alapozás az MB2.0 MDM (Master Data Management) szerint.
|
||||
Létrehozza a katalógus-vázat és a robot-feladatokat.
|
||||
"""
|
||||
async with AsyncSessionLocal() as db:
|
||||
logger.info("🚀 Honda márka-ökoszisztéma inicializálása...")
|
||||
|
||||
# 1. LOGIKA: Robot Discovery feladatok rögzítése
|
||||
# Ezzel mondjuk meg a Hunter robotnak, hogy keressen rá minden Honda variánsra
|
||||
discovery_tasks = [
|
||||
DiscoveryParameter(make="HONDA", vehicle_class="car", city="BUDAPEST", keyword="repair", is_active=True),
|
||||
DiscoveryParameter(make="HONDA", vehicle_class="motorcycle", city="BUDAPEST", keyword="service", is_active=True)
|
||||
]
|
||||
|
||||
for task in discovery_tasks:
|
||||
# Megnézzük, van-e már ilyen feladat
|
||||
stmt = select(DiscoveryParameter).where(
|
||||
DiscoveryParameter.make == task.make,
|
||||
DiscoveryParameter.vehicle_class == task.vehicle_class
|
||||
)
|
||||
exists = (await db.execute(stmt)).scalar_one_or_none()
|
||||
if not exists:
|
||||
db.add(task)
|
||||
|
||||
# 2. LOGIKA: Népszerű modellek (Arany Rekordok) betöltése
|
||||
# Ezek a "Starter" adatok, amik azonnal elérhetők a felhasználóknak
|
||||
honda_models = [
|
||||
# Személyautók
|
||||
{"model": "CIVIC", "gen": "X (2015-2021)", "class": "car"},
|
||||
{"model": "ACCORD", "gen": "X (2017-)", "class": "car"},
|
||||
{"model": "CR-V", "gen": "V (2016-)", "class": "car"},
|
||||
{"model": "JAZZ", "gen": "IV (2020-)", "class": "car"},
|
||||
# Motorkerékpárok
|
||||
{"model": "CB500X", "gen": "PC64 (2019-)", "class": "motorcycle"},
|
||||
{"model": "AFRICA TWIN", "gen": "CRF1100L", "class": "motorcycle"},
|
||||
{"model": "NC750X", "gen": "RH09 (2021-)", "class": "motorcycle"}
|
||||
]
|
||||
|
||||
for m in honda_models:
|
||||
# Ellenőrizzük az AssetCatalog-ban (MDM tábla)
|
||||
stmt = select(AssetCatalog).where(
|
||||
AssetCatalog.make == "HONDA",
|
||||
AssetCatalog.model == m["model"],
|
||||
AssetCatalog.generation == m["gen"]
|
||||
)
|
||||
exists = (await db.execute(stmt)).scalar_one_or_none()
|
||||
|
||||
if not exists:
|
||||
db.add(AssetCatalog(
|
||||
make="HONDA",
|
||||
model=m["model"],
|
||||
generation=m["gen"],
|
||||
vehicle_class=m["class"],
|
||||
factory_data={"source": "manual_priority_seed"} # MDM metaadat
|
||||
))
|
||||
|
||||
await db.commit()
|
||||
logger.info("✅ Honda (Autó & Motor) katalógus váz sikeresen felépítve!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(seed_honda())
|
||||
79
archive/tests_internal/seeds/seed_system.py
Executable file
79
archive/tests_internal/seeds/seed_system.py
Executable file
@@ -0,0 +1,79 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/tests_internal/seeds/seed_system.py
|
||||
import asyncio
|
||||
import logging
|
||||
import uuid
|
||||
from sqlalchemy import select
|
||||
from app.database import AsyncSessionLocal
|
||||
from app.models.identity import User, Person, UserRole
|
||||
from app.models.system import SystemParameter
|
||||
# JAVÍTOTT IMPORTOK: A grep alapján szétválasztva
|
||||
from app.models import PointRule, LevelConfig, UserStats
|
||||
from app.models.core_logic import SubscriptionTier
|
||||
from app.core.security import get_password_hash
|
||||
from app.core.config import settings
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] Sentinel-Seed: %(message)s')
|
||||
logger = logging.getLogger("System-Seeder")
|
||||
|
||||
async def seed_data():
|
||||
""" Rendszer alapadatok inicializálása a megfelelő modellekből. """
|
||||
async with AsyncSessionLocal() as db:
|
||||
logger.info("🚀 Rendszer-alapozás indítása (MB2.0 Standard)...")
|
||||
|
||||
admin_email = settings.INITIAL_ADMIN_EMAIL
|
||||
admin_password = settings.INITIAL_ADMIN_PASSWORD
|
||||
|
||||
if not admin_email or not admin_password:
|
||||
logger.error("❌ HIBA: Admin hitelesítési adatok hiányoznak!")
|
||||
return
|
||||
|
||||
# 1. Superadmin és Person kapcsolat
|
||||
stmt = select(User).where(User.email == admin_email)
|
||||
admin_exists = (await db.execute(stmt)).scalar_one_or_none()
|
||||
|
||||
if not admin_exists:
|
||||
new_person = Person(
|
||||
first_name="Rendszer",
|
||||
last_name="Adminisztrátor",
|
||||
id_uuid=uuid.uuid4(),
|
||||
is_active=True
|
||||
)
|
||||
db.add(new_person)
|
||||
await db.flush()
|
||||
|
||||
new_admin = User(
|
||||
email=admin_email,
|
||||
hashed_password=get_password_hash(admin_password),
|
||||
role=UserRole.superadmin,
|
||||
is_active=True,
|
||||
person_id=new_person.id
|
||||
)
|
||||
db.add(new_admin)
|
||||
await db.flush()
|
||||
|
||||
# Statisztikai rekord (Gamification)
|
||||
db.add(UserStats(user_id=new_admin.id, total_xp=0, current_level=1))
|
||||
logger.info(f"✅ Superadmin létrehozva: {admin_email}")
|
||||
|
||||
# 2. Rendszerparaméterek (JSONB értékekkel)
|
||||
params = [
|
||||
("SECURITY_MAX_RECORDS_PER_HOUR", {"limit": 50}, "Biztonsági limit"),
|
||||
("VEHICLE_LIMIT", {"default": 5}, "Alapértelmezett jármű limit")
|
||||
]
|
||||
for key, val, desc in params:
|
||||
stmt_p = select(SystemParameter).where(SystemParameter.key == key)
|
||||
if not (await db.execute(stmt_p)).scalar_one_or_none():
|
||||
db.add(SystemParameter(key=key, value=val, description=desc))
|
||||
|
||||
# 3. Gamification Szabályok (gamification.py-ból)
|
||||
rules = [("ASSET_REGISTER", 100), ("ASSET_REVIEW", 75)]
|
||||
for key, pts in rules:
|
||||
stmt_r = select(PointRule).where(PointRule.action_key == key)
|
||||
if not (await db.execute(stmt_r)).scalar_one_or_none():
|
||||
db.add(PointRule(action_key=key, points=pts))
|
||||
|
||||
await db.commit()
|
||||
logger.info("✨ Rendszer alapadatok rögzítve.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(seed_data())
|
||||
123
archive/tests_internal/seeds/seed_tco_categories.py
Normal file
123
archive/tests_internal/seeds/seed_tco_categories.py
Normal file
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
TCO (Total Cost of Ownership) alap költségkategóriák seedelése.
|
||||
Rendszerszintű kategóriák (is_system=True) amelyek nem törölhetők.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
# A projekt gyökérből importáljuk a database modult
|
||||
sys.path.insert(0, '/opt/docker/dev/service_finder/backend')
|
||||
from app.database import AsyncSessionLocal
|
||||
from app.models.vehicle import CostCategory
|
||||
|
||||
|
||||
# A 10 alap TCO kategória definíciója
|
||||
SYSTEM_CATEGORIES = [
|
||||
{
|
||||
"code": "FUEL",
|
||||
"name": "Üzemanyag / Töltés",
|
||||
"description": "Benzin, dízel, elektromos töltés, LPG, hidrogén"
|
||||
},
|
||||
{
|
||||
"code": "MAINTENANCE",
|
||||
"name": "Szerviz & Karbantartás",
|
||||
"description": "Olajcsere, szűrők, fékbetét, futómű, egyéb szerviz munkák"
|
||||
},
|
||||
{
|
||||
"code": "TIRES",
|
||||
"name": "Gumiabroncsok",
|
||||
"description": "Nyári/téli gumik, felni, kiegyensúlyozás, gumicsere"
|
||||
},
|
||||
{
|
||||
"code": "INSURANCE",
|
||||
"name": "Biztosítás",
|
||||
"description": "KASCO, kötelező gépjármű-felelősségbiztosítás, casco, utasbiztosítás"
|
||||
},
|
||||
{
|
||||
"code": "TAX",
|
||||
"name": "Adók",
|
||||
"description": "Gépjárműadó, forgalmi adó, közlekedési adó"
|
||||
},
|
||||
{
|
||||
"code": "FEES",
|
||||
"name": "Útdíj & Parkolás",
|
||||
"description": "Autópálya matrica, parkolási díjak, városi belépési díjak"
|
||||
},
|
||||
{
|
||||
"code": "ADMIN",
|
||||
"name": "Hatósági díjak",
|
||||
"description": "Műszaki vizsga, forgalmi engedély, okmányok, adminisztratív költségek"
|
||||
},
|
||||
{
|
||||
"code": "FINANCE",
|
||||
"name": "Finanszírozás",
|
||||
"description": "Lízing díj, hiteltörlesztés, kamatok, banki költségek"
|
||||
},
|
||||
{
|
||||
"code": "CLEANING",
|
||||
"name": "Ápolás & Kozmetika",
|
||||
"description": "Autómosás, polírozás, belső tisztítás, festékvédelem"
|
||||
},
|
||||
{
|
||||
"code": "OTHER",
|
||||
"name": "Egyéb",
|
||||
"description": "Egyéb, nem besorolható költségek"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
async def seed_tco_categories():
|
||||
"""
|
||||
Törli a meglévő kategóriákat és beszúrja a 10 rendszerszintű TCO kategóriát.
|
||||
"""
|
||||
print("🚀 TCO költségkategóriák seedelése...")
|
||||
|
||||
async with AsyncSessionLocal() as session:
|
||||
try:
|
||||
# 1. Tábla ürítése (TRUNCATE) - csak a seed kategóriák, ne érintse a felhasználói kategóriákat?
|
||||
# Mivel most csak rendszerszintűek vannak, töröljük az összeset
|
||||
print(" ↳ Tábla ürítése (TRUNCATE vehicle.cost_categories)...")
|
||||
await session.execute(text("TRUNCATE TABLE vehicle.cost_categories RESTART IDENTITY CASCADE"))
|
||||
await session.commit()
|
||||
|
||||
# 2. Kategóriák beszúrása
|
||||
inserted = 0
|
||||
for cat_data in SYSTEM_CATEGORIES:
|
||||
category = CostCategory(
|
||||
code=cat_data["code"],
|
||||
name=cat_data["name"],
|
||||
description=cat_data["description"],
|
||||
is_system=True,
|
||||
parent_id=None # Jelenleg nincs hierarchia, később bővíthető
|
||||
)
|
||||
session.add(category)
|
||||
inserted += 1
|
||||
|
||||
await session.commit()
|
||||
print(f" ✅ {inserted} rendszerszintű kategória beszúrva.")
|
||||
|
||||
# 3. Ellenőrzés
|
||||
result = await session.execute(text("SELECT COUNT(*) FROM vehicle.cost_categories"))
|
||||
count = result.scalar()
|
||||
print(f" 📊 vehicle.cost_categories táblában jelenleg {count} sor van.")
|
||||
|
||||
# Listázás
|
||||
result = await session.execute(text("SELECT code, name FROM vehicle.cost_categories ORDER BY code"))
|
||||
rows = result.fetchall()
|
||||
print(" 📋 Kategóriák listája:")
|
||||
for code, name in rows:
|
||||
print(f" - {code}: {name}")
|
||||
|
||||
except Exception as e:
|
||||
await session.rollback()
|
||||
print(f" ❌ Hiba történt: {e}")
|
||||
raise
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(seed_tco_categories())
|
||||
print("🎉 TCO kategória seedelés sikeresen befejeződött.")
|
||||
120
archive/tests_internal/seeds/seed_test_scenario.py
Executable file
120
archive/tests_internal/seeds/seed_test_scenario.py
Executable file
@@ -0,0 +1,120 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/tests_internal/seeds/seed_test_scenario.py
|
||||
import asyncio
|
||||
import uuid
|
||||
import logging
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from sqlalchemy import select
|
||||
from app.database import AsyncSessionLocal
|
||||
from app.models.identity import User
|
||||
from app.models.marketplace.organization import Organization, OrganizationMember, OrgType
|
||||
from app.models import (
|
||||
Asset, AssetCatalog, AssetTelemetry,
|
||||
AssetFinancials, AssetCost
|
||||
)
|
||||
|
||||
# Sentinel naplózás
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] Sentinel-Scenario: %(message)s')
|
||||
logger = logging.getLogger("Test-Scenario")
|
||||
|
||||
async def seed_test_scenario():
|
||||
async with AsyncSessionLocal() as db:
|
||||
logger.info("🚀 MB2.0 Teszt ökoszisztéma felépítése indul...")
|
||||
|
||||
# 1. LOGIKA: Admin (Superuser) lekérése az identity sémából
|
||||
res = await db.execute(select(User).where(User.is_active == True))
|
||||
admin = res.scalars().first()
|
||||
|
||||
if not admin:
|
||||
logger.error("❌ Hiba: Nincs aktív felhasználó a rendszerben. Futtasd a seed_system.py-t!")
|
||||
return
|
||||
|
||||
# 2. LOGIKA: Szervezeti struktúra felállítása
|
||||
# Privát garázs
|
||||
private_org = Organization(
|
||||
name="Kincses Privát",
|
||||
full_name="Kincses Magánflotta és Garázs",
|
||||
org_type=OrgType.individual,
|
||||
owner_id=admin.id,
|
||||
folder_slug="kincses-privat-vault"
|
||||
)
|
||||
# Üzleti flotta
|
||||
company_org = Organization(
|
||||
name="ProfiBot Fleet",
|
||||
full_name="ProfiBot Software Solutions Kft.",
|
||||
org_type=OrgType.business,
|
||||
owner_id=admin.id,
|
||||
folder_slug="profibot-fleet-vault"
|
||||
)
|
||||
# Szolgáltatók (Szerviz és Üzemanyag)
|
||||
service_org = Organization(
|
||||
name="Mester Szerviz",
|
||||
org_type=OrgType.service,
|
||||
owner_id=admin.id,
|
||||
is_active=True
|
||||
)
|
||||
|
||||
db.add_all([private_org, company_org, service_org])
|
||||
await db.flush()
|
||||
|
||||
# Tagsági viszonyok rögzítése
|
||||
db.add(OrganizationMember(user_id=admin.id, organization_id=company_org.id, role="owner"))
|
||||
|
||||
# 3. LOGIKA: Tesla Model 3 - Digitális Iker (Digital Twin)
|
||||
# Előbb a katalógus (Gold Data)
|
||||
catalog = AssetCatalog(
|
||||
make="TESLA", model="MODEL 3", generation="Long Range (2021-)",
|
||||
fuel_type="electric",
|
||||
factory_data={
|
||||
"battery": "75 kWh", "power_kw": 366,
|
||||
"tire_size": "235/45 R18", "ac_charge": "11kW"
|
||||
}
|
||||
)
|
||||
db.add(catalog)
|
||||
await db.flush()
|
||||
|
||||
# Majd a konkrét jármű (Asset)
|
||||
vehicle = Asset(
|
||||
vin=f"5YJ3E1EB8LF{uuid.uuid4().hex[:6].upper()}",
|
||||
license_plate="TES-777-EV",
|
||||
name="Céges Tesla",
|
||||
year_of_manufacture=2021,
|
||||
catalog_id=catalog.id,
|
||||
owner_org_id=company_org.id,
|
||||
status="active"
|
||||
)
|
||||
db.add(vehicle)
|
||||
await db.flush()
|
||||
|
||||
# Telemetria és Pénzügyi alapok
|
||||
db.add(AssetTelemetry(asset_id=vehicle.id, current_mileage=45200, vqi_score=100.0))
|
||||
db.add(AssetFinancials(asset_id=vehicle.id, acquisition_price=18500000, currency="HUF"))
|
||||
|
||||
# 4. LOGIKA: A 9 költségtípus szimulálása
|
||||
costs_data = [
|
||||
("FUEL", 12500, "Supercharger töltés"),
|
||||
("MAINTENANCE", 85000, "Pollenszűrő és átvizsgálás"),
|
||||
("TIRES", 280000, "Téli gumi szett"),
|
||||
("INSURANCE", 32000, "Havi CASCO"),
|
||||
("TAX", 15000, "Cégautóadó (szimulált)"),
|
||||
("TOLL", 6500, "Éves matrica"),
|
||||
("CLEANING", 4500, "Külső-belső takarítás"),
|
||||
("PARKING", 1200, "Belvárosi zóna"),
|
||||
("OTHER", 2500, "Szélvédőmosó folyadék")
|
||||
]
|
||||
|
||||
for c_type, amount, desc in costs_data:
|
||||
db.add(AssetCost(
|
||||
asset_id=vehicle.id,
|
||||
organization_id=company_org.id,
|
||||
cost_type=c_type,
|
||||
amount=amount,
|
||||
currency="HUF",
|
||||
date=datetime.now(timezone.utc) - timedelta(days=2),
|
||||
specifications={"description": desc}
|
||||
))
|
||||
|
||||
await db.commit()
|
||||
logger.info("✅ Siker! A teljes flotta-ökoszisztéma üzemkész.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(seed_test_scenario())
|
||||
77
archive/tests_internal/test_analytics_api.py
Normal file
77
archive/tests_internal/test_analytics_api.py
Normal file
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Operational test for Analytics API endpoint /api/v1/analytics/{vehicle_id}/summary
|
||||
Verifies that the endpoint is correctly registered, accepts UUID vehicle_id,
|
||||
and returns appropriate HTTP status (not 500 internal server error).
|
||||
Uses dev_bypass_active token to bypass authentication (requires DEBUG=True).
|
||||
"""
|
||||
import sys
|
||||
import asyncio
|
||||
import httpx
|
||||
import uuid
|
||||
import logging
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
API_BASE = "http://localhost:8000"
|
||||
DEV_TOKEN = "dev_bypass_active"
|
||||
|
||||
async def test_analytics_summary():
|
||||
"""Test that the endpoint is reachable and handles UUID parameter."""
|
||||
# Generate a random UUID (vehicle likely does not exist)
|
||||
vehicle_id = uuid.uuid4()
|
||||
url = f"{API_BASE}/api/v1/analytics/{vehicle_id}/summary"
|
||||
headers = {"Authorization": f"Bearer {DEV_TOKEN}"}
|
||||
|
||||
async with httpx.AsyncClient(timeout=10.0) as client:
|
||||
try:
|
||||
resp = await client.get(url, headers=headers)
|
||||
status = resp.status_code
|
||||
body = resp.text
|
||||
logger.info(f"Response status: {status}")
|
||||
logger.debug(f"Response body: {body}")
|
||||
|
||||
# If endpoint missing, we'd get 404 Not Found (from router).
|
||||
# However, with UUID parameter, the router is matched, so 404 is vehicle not found.
|
||||
# Distinguish by checking if the response indicates router-level 404 (maybe generic).
|
||||
# For simplicity, we assume any 404 means vehicle not found, which is OK.
|
||||
# The critical check: no 500 Internal Server Error (mapper or runtime errors).
|
||||
if status == 500:
|
||||
raise AssertionError(f"Internal server error: {body}")
|
||||
|
||||
# If we get 200, validate JSON structure (optional, but we don't have data).
|
||||
if status == 200:
|
||||
data = resp.json()
|
||||
required_keys = {"vehicle_id", "user_tco", "lifetime_tco", "benchmark_tco", "stats"}
|
||||
missing = required_keys - set(data.keys())
|
||||
if missing:
|
||||
raise AssertionError(f"Missing keys in response: {missing}")
|
||||
for key in ["user_tco", "lifetime_tco", "benchmark_tco"]:
|
||||
if not isinstance(data[key], list):
|
||||
raise AssertionError(f"{key} is not a list")
|
||||
logger.info("✅ Analytics endpoint works and returns expected structure.")
|
||||
return True
|
||||
|
||||
# Any other status (404, 422, 403, 401) indicates the endpoint is reachable
|
||||
# and the request was processed (no router error).
|
||||
logger.info(f"Endpoint responded with {status} (expected, vehicle not found or access denied).")
|
||||
return True
|
||||
except httpx.HTTPError as e:
|
||||
logger.error(f"HTTP client error: {e}")
|
||||
raise
|
||||
except asyncio.TimeoutError:
|
||||
logger.error("Request timeout")
|
||||
raise
|
||||
|
||||
async def main():
|
||||
try:
|
||||
await test_analytics_summary()
|
||||
print("\n✅ Analytics API test passed (endpoint is reachable and accepts UUID).")
|
||||
sys.exit(0)
|
||||
except Exception as e:
|
||||
print(f"\n❌ Analytics API test failed: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
31
archive/tests_internal/test_functional.py
Executable file
31
archive/tests_internal/test_functional.py
Executable file
@@ -0,0 +1,31 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/tests_internal/test_functional.py
|
||||
"""
|
||||
CÉL: Éles funkcionális teszt a bejelentkezési folyamathoz.
|
||||
"""
|
||||
import asyncio
|
||||
from sqlalchemy import select
|
||||
from app.database import AsyncSessionLocal
|
||||
from app.models.identity import User
|
||||
from app.services.auth_service import AuthService
|
||||
|
||||
async def test_login_flow():
|
||||
print("\n--- 🔑 FUNKCIONÁLIS LOGIN TESZT ---")
|
||||
async with AsyncSessionLocal() as db:
|
||||
# 1. Keressünk egy teszt felhasználót
|
||||
result = await db.execute(select(User).limit(1))
|
||||
user = result.scalar_one_or_none()
|
||||
|
||||
if not user:
|
||||
print("❌ HIBA: Nincs felhasználó az adatbázisban. Futtass egy seeder-t!")
|
||||
return
|
||||
|
||||
print(f"👤 Tesztelés felhasználóval: {user.email}")
|
||||
|
||||
# 2. Próbáljunk meg egy 'hitelesítést' (jelszó ellenőrzés nélkül a DB szinten)
|
||||
if user.is_active:
|
||||
print(f"✅ SIKER: A(z) {user.email} fiók aktív és elérhető.")
|
||||
else:
|
||||
print(f"⚠️ FIGYELEM: A felhasználó létezik, de inaktív.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(test_login_flow())
|
||||
91
archive/tests_internal/test_gamification_flow.py
Executable file
91
archive/tests_internal/test_gamification_flow.py
Executable file
@@ -0,0 +1,91 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/tests_internal/test_gamification_flow.py
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
from sqlalchemy import select
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Környezeti változók betöltése
|
||||
load_dotenv()
|
||||
|
||||
# MB2.0 Importok
|
||||
from app.database import AsyncSessionLocal
|
||||
from app.models.identity import User
|
||||
from app.models import UserStats, PointsLedger
|
||||
from app.services.social_service import SocialService
|
||||
from app.schemas.social import ServiceProviderCreate
|
||||
|
||||
# Naplózás beállítása
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] Sentinel-Test: %(message)s')
|
||||
logger = logging.getLogger("Gamification-Test")
|
||||
|
||||
async def run_test():
|
||||
logger.info("🚀 Gamifikációs integrációs folyamat tesztelése...")
|
||||
|
||||
async with AsyncSessionLocal() as db:
|
||||
try:
|
||||
# 1. LOGIKA: Teszt felhasználó lekérése az identity sémából
|
||||
result = await db.execute(select(User).limit(1))
|
||||
user = result.scalars().first()
|
||||
|
||||
if not user:
|
||||
logger.error("❌ Hiba: Nincs felhasználó az adatbázisban. Futtasd a seed_system.py-t!")
|
||||
return
|
||||
|
||||
logger.info(f"👤 Aktív teszt alany: {user.email}")
|
||||
|
||||
# 2. LOGIKA: Új szolgáltató rögzítése (Trigger az XP szerzéshez)
|
||||
# A SocialService.create_service_provider automatikusan hívja a GamificationService-t
|
||||
unique_id = os.urandom(2).hex()
|
||||
test_provider = ServiceProviderCreate(
|
||||
name=f"Robot Szerviz {unique_id}",
|
||||
address="Alchemist utca 12.",
|
||||
category="service"
|
||||
)
|
||||
|
||||
logger.info(f"🛠️ Esemény kiváltása: '{test_provider.name}' rögzítése...")
|
||||
new_provider = await SocialService.create_service_provider(db, test_provider, user.id)
|
||||
|
||||
# Commit kényszerítése, hogy a háttérfolyamatok rögzüljenek
|
||||
await db.commit()
|
||||
logger.info(f"✅ Szolgáltató elfogadva (ID: {new_provider.id})")
|
||||
|
||||
# 3. LOGIKA: Eredmények ellenőrzése a Ledgerben (Főkönyv)
|
||||
# Újra lekérjük a statisztikákat a commit után
|
||||
stats_res = await db.execute(select(UserStats).where(UserStats.user_id == user.id))
|
||||
stats = stats_res.scalar_one_or_none()
|
||||
|
||||
ledger_res = await db.execute(
|
||||
select(PointsLedger)
|
||||
.where(PointsLedger.user_id == user.id)
|
||||
.order_by(PointsLedger.created_at.desc())
|
||||
.limit(1)
|
||||
)
|
||||
last_entry = ledger_res.scalars().first()
|
||||
|
||||
print("\n" + "═"*40)
|
||||
print("📊 INTEGRÁCIÓS JELENTÉS:")
|
||||
if stats:
|
||||
print(f"🏆 Aktuális XP: {stats.total_xp}")
|
||||
print(f"📈 Szint: {stats.current_level}")
|
||||
else:
|
||||
print("⚠️ UserStats rekord nem található!")
|
||||
|
||||
if last_entry:
|
||||
print(f"📝 Tranzakció oka: {last_entry.reason}")
|
||||
print(f"💰 XP változás: +{last_entry.points_change}")
|
||||
print("═"*40 + "\n")
|
||||
|
||||
if stats and stats.total_xp > 0:
|
||||
logger.info("✅ SIKER: A gamifikációs lánc éles és működik!")
|
||||
else:
|
||||
logger.warning("❌ HIBA: A pontszámítás nem történt meg.")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"💥 Kritikus hiba a teszt közben: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(run_test())
|
||||
33
archive/tests_internal/test_postgis.py
Executable file
33
archive/tests_internal/test_postgis.py
Executable file
@@ -0,0 +1,33 @@
|
||||
# app/tests_internal/test_postgis.py
|
||||
import asyncio
|
||||
from sqlalchemy import text
|
||||
from app.db.session import AsyncSessionLocal
|
||||
|
||||
async def test_geo_logic():
|
||||
"""
|
||||
THOUGHT PROCESS:
|
||||
Ellenőrizni kell, hogy a PostgreSQL-ben a 'fleet.branches' tábla 'location' oszlopa
|
||||
valóban GEOGRAPHY típusú-e, és az ST_Distance függvény működik-e.
|
||||
Ha ez elbukik, a 'search.py' nem fog eredményt adni.
|
||||
"""
|
||||
print("🌍 PostGIS távolságszámítás tesztelése...")
|
||||
async with AsyncSessionLocal() as session:
|
||||
try:
|
||||
# Egy teszt pont (Budapest központ) és egy körzet lekérdezése
|
||||
query = text("""
|
||||
SELECT id, name,
|
||||
ST_Distance(location, ST_SetSRID(ST_MakePoint(19.0402, 47.4979), 4326)::geography) / 1000 as distance_km
|
||||
FROM fleet.branches
|
||||
LIMIT 1
|
||||
""")
|
||||
result = await db.execute(query)
|
||||
row = result.fetchone()
|
||||
if row:
|
||||
print(f"✅ SIKER: Találtunk egy ágat ({row.name}) {row.distance_km:.2f} km távolságra.")
|
||||
else:
|
||||
print("⚠️ FIGYELEM: A lekérdezés lefutott, de nincsenek adatok a fleet.branches táblában.")
|
||||
except Exception as e:
|
||||
print(f"❌ HIBA: A PostGIS lekérdezés elbukott. Oka: {str(e)}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(test_geo_logic())
|
||||
340
archive/tests_internal/verify_financial_truth.py
Normal file
340
archive/tests_internal/verify_financial_truth.py
Normal file
@@ -0,0 +1,340 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Financial Truth Verification - Epic 3 Pénzügyi Motor "Végső Boss" teszt.
|
||||
|
||||
Ez a script a Financial Orchestrator matematikai hibátlanságát teszteli,
|
||||
különös tekintettel a double-entry integritásra és a vetésforgó logikára.
|
||||
|
||||
FIGYELEM: A teszt NEM módosítja tartósan az éles adatbázist!
|
||||
Minden adatváltozás egy tranzakcióban történik, amely a végén rollback-el.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
import os
|
||||
from decimal import Decimal
|
||||
from datetime import datetime, timezone
|
||||
from uuid import uuid4
|
||||
|
||||
# Add backend directory to path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
|
||||
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlalchemy import select, func, text
|
||||
|
||||
from app.database import Base
|
||||
from app.models.identity import User, Person, Wallet
|
||||
from app.models.marketplace.finance import Issuer, IssuerType
|
||||
from app.models import WalletType
|
||||
from app.models import FinancialLedger, LedgerEntryType
|
||||
from app.services.financial_orchestrator import FinancialOrchestrator
|
||||
from app.core.config import settings
|
||||
|
||||
# Database connection - use the same as the app
|
||||
DATABASE_URL = settings.DATABASE_URL.replace("postgresql://", "postgresql+asyncpg://")
|
||||
engine = create_async_engine(DATABASE_URL, echo=False)
|
||||
AsyncSessionLocal = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
|
||||
|
||||
|
||||
class FinancialTruthTest:
|
||||
def __init__(self):
|
||||
self.session = None
|
||||
self.test_user = None
|
||||
self.test_wallet = None
|
||||
self.ev_issuer = None
|
||||
self.kft_issuer = None
|
||||
self.orchestrator = FinancialOrchestrator()
|
||||
self.created_ledgers = []
|
||||
self.total_amount = Decimal('0')
|
||||
# Generate unique timestamp for this test run to avoid duplicate tax IDs
|
||||
self.test_timestamp = datetime.now(timezone.utc).strftime("%Y%m%d%H%M%S")
|
||||
|
||||
async def setup(self):
|
||||
"""Test adatok létrehozása egy tranzakción belül."""
|
||||
print("=== FINANCIAL TRUTH VERIFICATION TEST ===")
|
||||
print("1. Teszt adatok előkészítése (tranzakción belül)...")
|
||||
|
||||
self.session = AsyncSessionLocal()
|
||||
|
||||
# Tranzakció indítása (nested transaction a rollback-hez)
|
||||
await self.session.begin_nested()
|
||||
|
||||
# Meglévő aktív számlakiállítók inaktiválása, hogy a teszt saját issuereit használja
|
||||
from sqlalchemy import update
|
||||
from app.models.marketplace.finance import Issuer
|
||||
stmt = update(Issuer).where(Issuer.is_active == True).values(is_active=False)
|
||||
await self.session.execute(stmt)
|
||||
await self.session.flush()
|
||||
|
||||
# Teszt User és Person létrehozása
|
||||
person = Person(
|
||||
first_name="Test",
|
||||
last_name="User",
|
||||
phone="+36123456789",
|
||||
is_active=True
|
||||
)
|
||||
self.session.add(person)
|
||||
await self.session.flush()
|
||||
|
||||
self.test_user = User(
|
||||
person_id=person.id,
|
||||
email=f"test_{uuid4().hex[:8]}@example.com",
|
||||
hashed_password="dummyhash",
|
||||
is_active=True
|
||||
)
|
||||
self.session.add(self.test_user)
|
||||
await self.session.flush()
|
||||
|
||||
# Wallet létrehozása a user számára
|
||||
self.test_wallet = Wallet(
|
||||
user_id=self.test_user.id,
|
||||
earned_credits=Decimal('1000000'), # Nagy kezdő egyenleg a teszteléshez
|
||||
purchased_credits=Decimal('0'),
|
||||
service_coins=Decimal('0'),
|
||||
currency="HUF"
|
||||
)
|
||||
self.session.add(self.test_wallet)
|
||||
await self.session.flush()
|
||||
|
||||
# EV típusú Issuer létrehozása alacsony revenue_limit-tel
|
||||
self.ev_issuer = Issuer(
|
||||
type=IssuerType.EV,
|
||||
name="Teszt EV Kft.",
|
||||
tax_id=f"12345678-1-42-{self.test_timestamp}", # Unique tax ID with timestamp
|
||||
revenue_limit=Decimal('50000'), # Csak 50,000 HUF keret
|
||||
current_revenue=Decimal('0'),
|
||||
is_active=True
|
||||
)
|
||||
self.session.add(self.ev_issuer)
|
||||
|
||||
# KFT típusú Issuer létrehozása magas limitel
|
||||
self.kft_issuer = Issuer(
|
||||
type=IssuerType.KFT,
|
||||
name="Teszt KFT Zrt.",
|
||||
tax_id=f"87654321-2-42-{self.test_timestamp}", # Unique tax ID with timestamp
|
||||
revenue_limit=Decimal('10000000'),
|
||||
current_revenue=Decimal('0'),
|
||||
is_active=True
|
||||
)
|
||||
self.session.add(self.kft_issuer)
|
||||
|
||||
await self.session.flush()
|
||||
|
||||
print(f" Teszt User ID: {self.test_user.id}")
|
||||
print(f" Wallet ID: {self.test_wallet.id}, Earned Credits: {self.test_wallet.earned_credits}")
|
||||
print(f" EV Issuer ID: {self.ev_issuer.id}, Revenue Limit: {self.ev_issuer.revenue_limit}")
|
||||
print(f" KFT Issuer ID: {self.kft_issuer.id}, Revenue Limit: {self.kft_issuer.revenue_limit}")
|
||||
|
||||
async def run_payment_cycle(self, num_payments=10, amount_per_payment=Decimal('15000')):
|
||||
"""Több fizetés szimulálása a vetésforgó tesztelésére."""
|
||||
print(f"\n2. {num_payments} fizetés szimulálása (összeg: {amount_per_payment} HUF)...")
|
||||
|
||||
ev_used = 0
|
||||
kft_used = 0
|
||||
|
||||
for i in range(1, num_payments + 1):
|
||||
print(f" Fizetés {i}/{num_payments}...")
|
||||
try:
|
||||
result = await self.orchestrator.process_payment(
|
||||
db=self.session,
|
||||
user_id=self.test_user.id,
|
||||
amount=amount_per_payment,
|
||||
wallet_type=WalletType.EARNED,
|
||||
description=f"Teszt fizetés #{i}",
|
||||
is_company=False # Nem cég, így először EV-t választ
|
||||
)
|
||||
|
||||
issuer_id = result.get('issuer_id')
|
||||
issuer_type = result.get('issuer_type')
|
||||
print(f" -> issuer_id={issuer_id}, issuer_type={issuer_type}, ev_id={self.ev_issuer.id}, kft_id={self.kft_issuer.id}")
|
||||
if issuer_id == self.ev_issuer.id:
|
||||
ev_used += 1
|
||||
print(f" -> EV számlakiállító használva")
|
||||
elif issuer_id == self.kft_issuer.id:
|
||||
kft_used += 1
|
||||
print(f" -> KFT számlakiállító használva (vetésforgó!)")
|
||||
else:
|
||||
print(f" -> HIBA: Ismeretlen issuer_id={issuer_id}")
|
||||
|
||||
self.total_amount += amount_per_payment
|
||||
self.created_ledgers.append(result.get('ledger_id'))
|
||||
|
||||
except Exception as e:
|
||||
print(f" HIBA: {e}")
|
||||
raise
|
||||
|
||||
print(f" Összesítés: EV használva: {ev_used}, KFT használva: {kft_used}")
|
||||
return ev_used, kft_used
|
||||
|
||||
async def verify_double_entry(self):
|
||||
"""Double-entry integritás ellenőrzése: Ledger összegek vs Wallet egyenleg."""
|
||||
print("\n3. Double-Entry Integritás Ellenőrzése...")
|
||||
|
||||
# Összes létrehozott ledger bejegyzés összegének kiszámítása
|
||||
ledger_sum = Decimal('0')
|
||||
for ledger_id in self.created_ledgers:
|
||||
stmt = select(FinancialLedger).where(FinancialLedger.id == ledger_id)
|
||||
result = await self.session.execute(stmt)
|
||||
ledger = result.scalar_one()
|
||||
ledger_sum += ledger.amount
|
||||
|
||||
# Wallet aktuális egyenlegének lekérdezése
|
||||
stmt = select(Wallet).where(Wallet.id == self.test_wallet.id)
|
||||
result = await self.session.execute(stmt)
|
||||
wallet = result.scalar_one()
|
||||
# Összesített egyenleg: earned_credits + purchased_credits + service_coins
|
||||
# Convert all to Decimal for consistent arithmetic
|
||||
earned = Decimal(str(wallet.earned_credits))
|
||||
purchased = Decimal(str(wallet.purchased_credits))
|
||||
service = Decimal(str(wallet.service_coins))
|
||||
wallet_balance = earned + purchased + service
|
||||
|
||||
# Kezdeti egyenleg (1000000) mínusz a kifizetett összeg
|
||||
expected_balance = Decimal('1000000') - self.total_amount
|
||||
|
||||
print(f" Összes ledger tranzakció összege: {ledger_sum} HUF")
|
||||
print(f" Wallet aktuális egyenlege: {wallet_balance} HUF (earned: {earned}, purchased: {purchased}, service: {service})")
|
||||
print(f" Elvárt egyenleg (kezdeti - összes): {expected_balance} HUF")
|
||||
|
||||
# ASSERT 1: Ledger összeg megegyezik a teljes összeggel
|
||||
assert ledger_sum == self.total_amount, \
|
||||
f"Ledger összeg ({ledger_sum}) nem egyezik a teljes összeggel ({self.total_amount})"
|
||||
|
||||
# ASSERT 2: Wallet egyenleg helyes
|
||||
assert wallet_balance == expected_balance, \
|
||||
f"Wallet egyenleg ({wallet_balance}) nem egyezik az elvárt értékkel ({expected_balance})"
|
||||
|
||||
print(" ✅ Double-entry integritás OK: Ledger összegek és Wallet egyenleg konzisztens.")
|
||||
|
||||
async def verify_crop_rotation(self, ev_used, kft_used):
|
||||
"""Vetésforgó logika ellenőrzése: EV keret betelése után KFT-re váltás."""
|
||||
print("\n4. Vetésforgó Logika Ellenőrzése...")
|
||||
|
||||
# EV revenue limit: 50000
|
||||
# Egy fizetés összege: 15000
|
||||
# EV maximum 3 fizetést tud kezelni (3 * 15000 = 45000 < 50000)
|
||||
# A negyedik fizetésnél már túllépné a limitet, így KFT-nek kell váltania
|
||||
|
||||
expected_ev_max = 3 # 3 fizetés még belefér
|
||||
expected_kft_min = 1 # legalább 1 fizetés KFT-vel kell legyen (ha több mint 3 fizetés)
|
||||
|
||||
print(f" EV használva: {ev_used}, KFT használva: {kft_used}")
|
||||
print(f" Elvárás: EV ≤ {expected_ev_max}, KFT ≥ {expected_kft_min}")
|
||||
|
||||
# ASSERT 3: EV nem lépheti túl a limitjét
|
||||
assert ev_used <= expected_ev_max, \
|
||||
f"Túl sok EV használat ({ev_used}) a revenue limit ({self.ev_issuer.revenue_limit}) mellett"
|
||||
|
||||
# ASSERT 4: Ha több fizetés van, mint ami belefér az EV-be, akkor KFT-t kell használni
|
||||
if ev_used == expected_ev_max:
|
||||
assert kft_used >= expected_kft_min, \
|
||||
f"EV limit betelt, de KFT nem lett használva (ev={ev_used}, kft={kft_used})"
|
||||
|
||||
# Ellenőrizzük az aktuális current_revenue értékeket
|
||||
await self.session.refresh(self.ev_issuer)
|
||||
await self.session.refresh(self.kft_issuer)
|
||||
|
||||
print(f" EV aktuális bevétel: {self.ev_issuer.current_revenue}")
|
||||
print(f" KFT aktuális bevétel: {self.kft_issuer.current_revenue}")
|
||||
|
||||
# ASSERT 5: EV current_revenue nem haladhatja meg a limitet
|
||||
assert self.ev_issuer.current_revenue <= self.ev_issuer.revenue_limit, \
|
||||
f"EV current_revenue ({self.ev_issuer.current_revenue}) > limit ({self.ev_issuer.revenue_limit})"
|
||||
|
||||
print(" ✅ Vetésforgó logika OK: EV -> KFT váltás a limit betöltésekor.")
|
||||
|
||||
async def generate_report(self):
|
||||
"""Részletes riport generálása a teszt eredményeiről."""
|
||||
print("\n" + "="*60)
|
||||
print("FINANCIAL TRUTH VERIFICATION - TESZT EREDMÉNY")
|
||||
print("="*60)
|
||||
|
||||
# Ledger statisztikák
|
||||
stmt = select(func.count(FinancialLedger.id)).where(
|
||||
FinancialLedger.id.in_(self.created_ledgers)
|
||||
)
|
||||
result = await self.session.execute(stmt)
|
||||
ledger_count = result.scalar()
|
||||
|
||||
# Issuer statisztikák
|
||||
await self.session.refresh(self.ev_issuer)
|
||||
await self.session.refresh(self.kft_issuer)
|
||||
|
||||
print(f"Összes tranzakció: {ledger_count}")
|
||||
print(f"Teljes összeg: {self.total_amount} HUF")
|
||||
print(f"EV számlakiállító:")
|
||||
print(f" - ID: {self.ev_issuer.id}")
|
||||
print(f" - Aktuális bevétel: {self.ev_issuer.current_revenue} HUF")
|
||||
print(f" - Revenue limit: {self.ev_issuer.revenue_limit} HUF")
|
||||
print(f" - Felhasznált kapacitás: {self.ev_issuer.current_revenue / self.ev_issuer.revenue_limit * 100:.1f}%")
|
||||
print(f"KFT számlakiállító:")
|
||||
print(f" - ID: {self.kft_issuer.id}")
|
||||
print(f" - Aktuális bevétel: {self.kft_issuer.current_revenue} HUF")
|
||||
print(f" - Revenue limit: {self.kft_issuer.revenue_limit} HUF")
|
||||
|
||||
# Wallet állapot
|
||||
await self.session.refresh(self.test_wallet)
|
||||
print(f"Teszt Wallet:")
|
||||
print(f" - ID: {self.test_wallet.id}")
|
||||
# Összesített egyenleg: earned_credits + purchased_credits + service_coins
|
||||
total_balance = self.test_wallet.earned_credits + self.test_wallet.purchased_credits + self.test_wallet.service_coins
|
||||
print(f" - Egyenleg: {total_balance} HUF (earned: {self.test_wallet.earned_credits}, purchased: {self.test_wallet.purchased_credits}, service: {self.test_wallet.service_coins})")
|
||||
print(f" - Kezdeti egyenleg: 1000000 HUF")
|
||||
print(f" - Költség: {self.total_amount} HUF")
|
||||
|
||||
print("\n✅ ÖSSZEFOGLALÓ: A Financial Orchestrator matematikailag hibátlan.")
|
||||
print(" - Double-entry integritás: OK")
|
||||
print(" - Vetésforgó logika: OK")
|
||||
print(" - Tranzakció atomi végrehajtás: OK")
|
||||
print("="*60)
|
||||
|
||||
async def cleanup(self):
|
||||
"""Teszt adatok törlése rollback-kel."""
|
||||
print("\n5. Takarítás: tranzakció rollback (dev adatbázis érintetlen)...")
|
||||
# Mivel nested transaction van, rollback-eljük
|
||||
await self.session.rollback()
|
||||
# A külső tranzakciót is rollback (ha van)
|
||||
if self.session.in_transaction():
|
||||
await self.session.rollback()
|
||||
await self.session.close()
|
||||
print(" ✅ Rollback sikeres, dev adatbázis változatlan.")
|
||||
|
||||
async def run(self):
|
||||
"""Fő teszt folyamat."""
|
||||
try:
|
||||
await self.setup()
|
||||
ev_used, kft_used = await self.run_payment_cycle(num_payments=10, amount_per_payment=Decimal('15000'))
|
||||
await self.verify_double_entry()
|
||||
await self.verify_crop_rotation(ev_used, kft_used)
|
||||
await self.generate_report()
|
||||
await self.cleanup()
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"\n❌ TESZT SIKERTELEN: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
# Hiba esetén is rollback
|
||||
if self.session:
|
||||
await self.session.rollback()
|
||||
await self.session.close()
|
||||
return False
|
||||
|
||||
|
||||
async def main():
|
||||
"""Fő belépési pont."""
|
||||
test = FinancialTruthTest()
|
||||
success = await test.run()
|
||||
|
||||
if success:
|
||||
print("\n🎉 FINANCIAL TRUTH VERIFICATION SIKERES!")
|
||||
print(" Epic 3 Pénzügyi Motor matematikailag sebezhetetlen.")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("\n💥 FINANCIAL TRUTH VERIFICATION SIKERTELEN!")
|
||||
print(" A Financial Orchestrator hibát tartalmaz, javítás szükséges.")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user