A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
231 lines
7.2 KiB
Python
231 lines
7.2 KiB
Python
"""
|
|
Test Registry Configuration
|
|
|
|
Project paths, environment setup, and global state management.
|
|
"""
|
|
|
|
import os
|
|
import json
|
|
import subprocess
|
|
from pathlib import Path
|
|
from typing import List, Dict, Any, Optional
|
|
from datetime import datetime
|
|
|
|
|
|
# Projekt-Basisverzeichnis - prüfe verschiedene Pfade
|
|
# 1. Docker mit Volume-Mount: /app/project
|
|
# 2. Lokale Entwicklung: /Users/benjaminadmin/Projekte/breakpilot-pwa
|
|
# 3. Fallback: Demo-Modus
|
|
DOCKER_PROJECT_PATH = Path("/app/project")
|
|
LOCAL_PROJECT_PATH = Path("/Users/benjaminadmin/Projekte/breakpilot-pwa")
|
|
|
|
if DOCKER_PROJECT_PATH.exists():
|
|
PROJECT_ROOT = DOCKER_PROJECT_PATH
|
|
RUN_MODE = "docker"
|
|
elif LOCAL_PROJECT_PATH.exists():
|
|
PROJECT_ROOT = LOCAL_PROJECT_PATH
|
|
RUN_MODE = "local"
|
|
else:
|
|
PROJECT_ROOT = LOCAL_PROJECT_PATH # Fallback für Demo
|
|
RUN_MODE = "demo"
|
|
|
|
# Pfad fuer persistierte Ergebnisse (Legacy JSON - wird noch als Fallback verwendet)
|
|
DATA_DIR = Path("/app/data")
|
|
RESULTS_FILE = DATA_DIR / "test_results.json"
|
|
|
|
# Deaktiviert - wir wollen IMMER echte Tests wenn Tools verfügbar sind
|
|
IS_DOCKER = False # Nie Demo-Modus verwenden
|
|
|
|
# Flag fuer PostgreSQL-Verfuegbarkeit
|
|
_use_postgres = True
|
|
|
|
|
|
# ==============================================================================
|
|
# In-Memory Storage
|
|
# ==============================================================================
|
|
|
|
# In-Memory Storage (wird parallel zu PostgreSQL gepflegt fuer Abwaertskompatibilitaet)
|
|
_test_runs: List[Dict] = []
|
|
_current_runs: Dict[str, Any] = {}
|
|
_running_tests: Dict[str, Dict] = {} # Progress-Tracking fuer laufende Tests
|
|
_persisted_results: Dict[str, Dict] = {} # Persistierte Testergebnisse (Legacy)
|
|
|
|
|
|
def get_test_runs() -> List[Dict]:
|
|
"""Get all test runs."""
|
|
return _test_runs
|
|
|
|
|
|
def get_current_runs() -> Dict[str, Any]:
|
|
"""Get currently running tests."""
|
|
return _current_runs
|
|
|
|
|
|
def get_running_tests() -> Dict[str, Dict]:
|
|
"""Get running test progress."""
|
|
return _running_tests
|
|
|
|
|
|
def get_persisted_results() -> Dict[str, Dict]:
|
|
"""Get persisted test results."""
|
|
return _persisted_results
|
|
|
|
|
|
def set_persisted_results(results: Dict[str, Dict]):
|
|
"""Set persisted test results."""
|
|
global _persisted_results
|
|
_persisted_results = results
|
|
|
|
|
|
def is_postgres_available() -> bool:
|
|
"""Check if PostgreSQL is available."""
|
|
return _use_postgres
|
|
|
|
|
|
def set_postgres_available(available: bool):
|
|
"""Set PostgreSQL availability flag."""
|
|
global _use_postgres
|
|
_use_postgres = available
|
|
|
|
|
|
# ==============================================================================
|
|
# Tool Availability Checks
|
|
# ==============================================================================
|
|
|
|
def check_go_available() -> bool:
|
|
"""Prüft ob Go installiert ist"""
|
|
try:
|
|
result = subprocess.run(["go", "version"], capture_output=True, timeout=5)
|
|
return result.returncode == 0
|
|
except:
|
|
return False
|
|
|
|
|
|
def check_pytest_available() -> bool:
|
|
"""Prüft ob pytest installiert ist"""
|
|
pytest_paths = ["/opt/venv/bin/pytest", "pytest"]
|
|
for path in pytest_paths:
|
|
try:
|
|
result = subprocess.run(path.split() + ["--version"], capture_output=True, timeout=5)
|
|
if result.returncode == 0:
|
|
return True
|
|
except:
|
|
continue
|
|
return False
|
|
|
|
|
|
def get_go_version() -> Optional[str]:
|
|
"""Gibt die Go-Version zurueck"""
|
|
try:
|
|
result = subprocess.run(["go", "version"], capture_output=True, text=True, timeout=5)
|
|
if result.returncode == 0:
|
|
# "go version go1.23.5 linux/arm64" -> "1.23.5"
|
|
parts = result.stdout.strip().split()
|
|
if len(parts) >= 3:
|
|
return parts[2].replace("go", "")
|
|
except:
|
|
pass
|
|
return None
|
|
|
|
|
|
def get_pytest_version() -> Optional[str]:
|
|
"""Gibt die pytest-Version zurueck"""
|
|
try:
|
|
result = subprocess.run(["/opt/venv/bin/pytest", "--version"], capture_output=True, text=True, timeout=5)
|
|
if result.returncode == 0:
|
|
# "pytest 8.x.x" -> "8.x.x"
|
|
return result.stdout.strip().split()[1] if result.stdout else None
|
|
except:
|
|
pass
|
|
return None
|
|
|
|
|
|
# ==============================================================================
|
|
# Persistence Functions
|
|
# ==============================================================================
|
|
|
|
def check_postgres_available() -> bool:
|
|
"""Prueft ob PostgreSQL verfuegbar ist."""
|
|
global _use_postgres
|
|
try:
|
|
from ..database import check_db_connection
|
|
_use_postgres = check_db_connection()
|
|
except Exception:
|
|
_use_postgres = False
|
|
return _use_postgres
|
|
|
|
|
|
def load_persisted_results():
|
|
"""Laedt persistierte Testergebnisse beim Start - erst aus DB, dann JSON als Fallback"""
|
|
global _persisted_results
|
|
|
|
# Versuche zuerst aus PostgreSQL zu laden
|
|
if check_postgres_available():
|
|
try:
|
|
from ..database import get_db_session
|
|
from ..repository import TestRepository
|
|
|
|
with get_db_session() as db:
|
|
repo = TestRepository(db)
|
|
stats = repo.get_all_service_stats()
|
|
for stat in stats:
|
|
_persisted_results[stat.service] = {
|
|
"total": stat.total_tests,
|
|
"passed": stat.passed_tests,
|
|
"failed": stat.failed_tests,
|
|
"last_run": stat.last_run_at.isoformat() if stat.last_run_at else None,
|
|
"status": stat.last_status or "unknown",
|
|
"failed_test_ids": [] # Wird spaeter nachgeladen
|
|
}
|
|
print(f"Test-Ergebnisse aus PostgreSQL geladen: {len(stats)} Services")
|
|
return
|
|
except Exception as e:
|
|
print(f"Fehler beim Laden aus PostgreSQL: {e}")
|
|
|
|
# Fallback: JSON-Datei
|
|
if RESULTS_FILE.exists():
|
|
try:
|
|
with open(RESULTS_FILE, "r") as f:
|
|
_persisted_results = json.load(f)
|
|
print(f"Test-Ergebnisse aus JSON geladen: {len(_persisted_results)} Services")
|
|
except Exception as e:
|
|
print(f"Fehler beim Laden der Testergebnisse: {e}")
|
|
_persisted_results = {}
|
|
|
|
|
|
def save_persisted_results():
|
|
"""Speichert Testergebnisse - in PostgreSQL und JSON als Backup"""
|
|
# JSON als Backup speichern
|
|
try:
|
|
DATA_DIR.mkdir(parents=True, exist_ok=True)
|
|
with open(RESULTS_FILE, "w") as f:
|
|
json.dump(_persisted_results, f, indent=2, default=str)
|
|
except Exception as e:
|
|
print(f"Fehler beim Speichern der JSON-Testergebnisse: {e}")
|
|
|
|
|
|
def migrate_json_to_postgres() -> int:
|
|
"""Migriert bestehende JSON-Daten nach PostgreSQL (einmalig)."""
|
|
if not _use_postgres:
|
|
return 0
|
|
|
|
if not _persisted_results:
|
|
return 0
|
|
|
|
try:
|
|
from ..database import get_db_session
|
|
from ..repository import TestRepository
|
|
|
|
with get_db_session() as db:
|
|
repo = TestRepository(db)
|
|
count = repo.migrate_from_json(_persisted_results)
|
|
print(f"Migration abgeschlossen: {count} Services migriert")
|
|
return count
|
|
except Exception as e:
|
|
print(f"Fehler bei Migration: {e}")
|
|
return 0
|
|
|
|
|
|
# Lade persistierte Ergebnisse beim Import
|
|
load_persisted_results()
|