added tests
This commit is contained in:
parent
4e359cf3ef
commit
cbe9369712
9 changed files with 960 additions and 150 deletions
|
@ -11,6 +11,8 @@ Eine RESTful API für Tschunk-Bestellungen, entwickelt mit FastAPI.
|
|||
- Wahl zwischen Flora Mate und Club Mate
|
||||
- Mengenangabe für jedes Getränk (Standard: 1)
|
||||
- Anmerkungen und Sonderwünsche für jedes Getränk
|
||||
- **WebSocket-Unterstützung für Echtzeit-Updates**
|
||||
- **Automatisierte Tests mit pytest**
|
||||
|
||||
## Verfügbare Getränke
|
||||
|
||||
|
@ -115,6 +117,7 @@ Beispiel-Request:
|
|||
**Hinweise:**
|
||||
- Das `quantity`-Feld ist optional. Falls nicht angegeben, wird automatisch 1 verwendet.
|
||||
- Das `notes`-Feld ist optional und kann für Sonderwünsche oder Anmerkungen verwendet werden (max. 500 Zeichen).
|
||||
- **Alle verbundenen WebSocket-Clients erhalten automatisch ein Update.**
|
||||
|
||||
### 2. Alle Bestellungen abrufen
|
||||
```
|
||||
|
@ -126,11 +129,51 @@ GET /orders
|
|||
DELETE /orders/{order_id}
|
||||
```
|
||||
|
||||
**Hinweis:** Alle verbundenen WebSocket-Clients erhalten automatisch ein Update.
|
||||
|
||||
### 4. Verfügbare Getränke anzeigen
|
||||
```
|
||||
GET /drinks
|
||||
```
|
||||
|
||||
### 5. WebSocket für Echtzeit-Updates
|
||||
```
|
||||
WS /ws
|
||||
```
|
||||
|
||||
**WebSocket-Nachrichten-Formate:**
|
||||
|
||||
#### Neue Bestellung:
|
||||
```json
|
||||
{
|
||||
"type": "order_created",
|
||||
"timestamp": "2024-01-15T10:30:00",
|
||||
"order": {
|
||||
"id": "uuid-here",
|
||||
"order_date": "2024-01-15T10:30:00",
|
||||
"drinks": [...]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Bestellung gelöscht:
|
||||
```json
|
||||
{
|
||||
"type": "order_deleted",
|
||||
"timestamp": "2024-01-15T10:35:00",
|
||||
"order_id": "uuid-here"
|
||||
}
|
||||
```
|
||||
|
||||
#### Alle Bestellungen (bei Verbindung):
|
||||
```json
|
||||
{
|
||||
"type": "all_orders",
|
||||
"timestamp": "2024-01-15T10:30:00",
|
||||
"orders": [...]
|
||||
}
|
||||
```
|
||||
|
||||
## Beispiel-Verwendung mit curl
|
||||
|
||||
### Bestellung erstellen:
|
||||
|
@ -164,10 +207,75 @@ curl -X GET "http://localhost:8000/orders"
|
|||
curl -X DELETE "http://localhost:8000/orders/{order_id}"
|
||||
```
|
||||
|
||||
## WebSocket-Client Beispiel
|
||||
|
||||
### JavaScript (Browser):
|
||||
```javascript
|
||||
const ws = new WebSocket('ws://localhost:8000/ws');
|
||||
|
||||
ws.onopen = function() {
|
||||
console.log('WebSocket verbunden');
|
||||
};
|
||||
|
||||
ws.onmessage = function(event) {
|
||||
const data = JSON.parse(event.data);
|
||||
|
||||
switch(data.type) {
|
||||
case 'order_created':
|
||||
console.log('Neue Bestellung:', data.order);
|
||||
break;
|
||||
case 'order_deleted':
|
||||
console.log('Bestellung gelöscht:', data.order_id);
|
||||
break;
|
||||
case 'all_orders':
|
||||
console.log('Alle Bestellungen:', data.orders);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = function() {
|
||||
console.log('WebSocket getrennt');
|
||||
};
|
||||
```
|
||||
|
||||
### Python:
|
||||
```python
|
||||
import asyncio
|
||||
import websockets
|
||||
import json
|
||||
|
||||
async def websocket_client():
|
||||
async with websockets.connect('ws://localhost:8000/ws') as websocket:
|
||||
while True:
|
||||
message = await websocket.recv()
|
||||
data = json.loads(message)
|
||||
print(f"Empfangen: {data}")
|
||||
|
||||
asyncio.run(websocket_client())
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
Das Projekt enthält ein Test-Skript, das alle API-Endpunkte testet:
|
||||
Das Projekt enthält mehrere Test-Optionen:
|
||||
|
||||
### 1. Automatisierte Tests (Empfohlen)
|
||||
```bash
|
||||
# Stelle sicher, dass das virtuelle Environment aktiviert ist
|
||||
source venv/bin/activate
|
||||
|
||||
# Führe alle Tests automatisch aus
|
||||
python run_tests.py
|
||||
```
|
||||
|
||||
**Features der automatisierten Tests:**
|
||||
- ✅ **Kein Server-Start nötig** - Tests laufen automatisch
|
||||
- ✅ **Vollständige API-Abdeckung** - Alle Endpunkte getestet
|
||||
- ✅ **WebSocket-Tests** - Echtzeit-Funktionalität getestet
|
||||
- ✅ **Datenbank-Tests** - CRUD-Operationen getestet
|
||||
- ✅ **Validierung-Tests** - Pydantic-Modelle getestet
|
||||
- ✅ **Fehlerbehandlung** - Edge Cases getestet
|
||||
|
||||
### 2. Manuelle API-Tests
|
||||
```bash
|
||||
# Stelle sicher, dass das virtuelle Environment aktiviert ist
|
||||
source venv/bin/activate
|
||||
|
@ -176,18 +284,47 @@ source venv/bin/activate
|
|||
python test_api.py
|
||||
```
|
||||
|
||||
### 3. WebSocket-Tests
|
||||
```bash
|
||||
# Stelle sicher, dass das virtuelle Environment aktiviert ist
|
||||
source venv/bin/activate
|
||||
|
||||
# WebSocket-Test ausführen
|
||||
python test_websocket.py
|
||||
```
|
||||
|
||||
### 4. Erweiterte Test-Optionen
|
||||
```bash
|
||||
# Detaillierte pytest-Ausgabe
|
||||
python -m pytest test_automated.py -v
|
||||
|
||||
# Nur bestimmte Tests ausführen
|
||||
python -m pytest test_automated.py -k "test_create"
|
||||
|
||||
# Tests mit Coverage
|
||||
python -m pytest test_automated.py --cov=.
|
||||
|
||||
# Tests parallel ausführen
|
||||
python -m pytest test_automated.py -n auto
|
||||
```
|
||||
|
||||
## Projektstruktur
|
||||
|
||||
```
|
||||
backend/
|
||||
├── main.py # FastAPI-Anwendung
|
||||
├── models.py # Pydantic-Modelle
|
||||
├── database.py # In-Memory-Datenbank
|
||||
├── test_api.py # Test-Skript
|
||||
├── requirements.txt # Python-Abhängigkeiten
|
||||
├── .gitignore # Git-Ignore-Datei
|
||||
├── venv/ # Virtuelles Environment (wird ignoriert)
|
||||
└── README.md # Diese Datei
|
||||
├── main.py # FastAPI-Anwendung
|
||||
├── models.py # Pydantic-Modelle
|
||||
├── database.py # In-Memory-Datenbank
|
||||
├── websocket_manager.py # WebSocket-Management
|
||||
├── test_automated.py # Automatisierte Tests (pytest)
|
||||
├── test_api.py # Manueller API-Test
|
||||
├── test_websocket.py # WebSocket-Test
|
||||
├── run_tests.py # Test-Runner
|
||||
├── pytest.ini # pytest-Konfiguration
|
||||
├── requirements.txt # Python-Abhängigkeiten
|
||||
├── .gitignore # Git-Ignore-Datei
|
||||
├── venv/ # Virtuelles Environment (wird ignoriert)
|
||||
└── README.md # Diese Datei
|
||||
```
|
||||
|
||||
## Hinweise
|
||||
|
@ -198,6 +335,8 @@ backend/
|
|||
- **Validierung**: Die API validiert automatisch alle Eingaben mit Pydantic
|
||||
- **Mengenangabe**: Jedes Getränk kann eine Menge haben (mindestens 1, Standard: 1)
|
||||
- **Anmerkungen**: Jedes Getränk kann optionale Anmerkungen haben (max. 500 Zeichen)
|
||||
- **Echtzeit-Updates**: WebSocket-Clients erhalten automatisch Updates bei Änderungen
|
||||
- **Automatisierte Tests**: Vollständige Test-Suite mit pytest für alle Funktionen
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
@ -217,4 +356,16 @@ Falls Port 8000 bereits belegt ist, kannst du einen anderen Port verwenden:
|
|||
|
||||
```bash
|
||||
uvicorn main:app --reload --port 8001
|
||||
```
|
||||
```
|
||||
|
||||
### WebSocket-Verbindungsprobleme
|
||||
Falls WebSocket-Verbindungen nicht funktionieren:
|
||||
- Stelle sicher, dass der Server läuft
|
||||
- Prüfe Firewall-Einstellungen
|
||||
- Verwende `ws://` für lokale Verbindungen, `wss://` für HTTPS
|
||||
|
||||
### Test-Probleme
|
||||
Falls Tests fehlschlagen:
|
||||
- Stelle sicher, dass alle Abhängigkeiten installiert sind: `pip install -r requirements.txt`
|
||||
- Aktiviere das virtuelle Environment: `source venv/bin/activate`
|
||||
- Führe Tests mit detaillierter Ausgabe aus: `python -m pytest test_automated.py -v`
|
|
@ -2,13 +2,14 @@ from typing import List, Optional
|
|||
import uuid
|
||||
from datetime import datetime
|
||||
from models import Order, Drink
|
||||
from websocket_manager import websocket_manager
|
||||
|
||||
|
||||
class OrderDatabase:
|
||||
def __init__(self):
|
||||
self.orders: dict[str, Order] = {}
|
||||
|
||||
def create_order(self, drinks: List[Drink]) -> Order:
|
||||
async def create_order(self, drinks: List[Drink]) -> Order:
|
||||
"""Create a new order with the given drinks."""
|
||||
order_id = str(uuid.uuid4())
|
||||
order = Order(
|
||||
|
@ -17,6 +18,10 @@ class OrderDatabase:
|
|||
drinks=drinks
|
||||
)
|
||||
self.orders[order_id] = order
|
||||
|
||||
# WebSocket-Broadcast für neue Bestellung
|
||||
await websocket_manager.broadcast_order_created(order.model_dump())
|
||||
|
||||
return order
|
||||
|
||||
def get_all_orders(self) -> List[Order]:
|
||||
|
@ -27,10 +32,14 @@ class OrderDatabase:
|
|||
"""Get a specific order by ID."""
|
||||
return self.orders.get(order_id)
|
||||
|
||||
def delete_order(self, order_id: str) -> bool:
|
||||
async def delete_order(self, order_id: str) -> bool:
|
||||
"""Delete an order by ID. Returns True if successful, False if not found."""
|
||||
if order_id in self.orders:
|
||||
del self.orders[order_id]
|
||||
|
||||
# WebSocket-Broadcast für gelöschte Bestellung
|
||||
await websocket_manager.broadcast_order_deleted(order_id)
|
||||
|
||||
return True
|
||||
return False
|
||||
|
||||
|
|
|
@ -1,25 +1,83 @@
|
|||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect
|
||||
from typing import List
|
||||
from models import CreateOrderRequest, Order, DrinkType, MateType
|
||||
from database import db
|
||||
from websocket_manager import websocket_manager
|
||||
|
||||
app = FastAPI(
|
||||
title="Tschunk Order API",
|
||||
description="Eine RESTful API für Tschunk-Bestellungen",
|
||||
description="Eine RESTful API für Tschunk-Bestellungen mit WebSocket-Unterstützung",
|
||||
version="1.0.0"
|
||||
)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
"""Root endpoint with API information."""
|
||||
return {
|
||||
"message": "Willkommen zur Tschunk Order API!",
|
||||
"version": "1.0.0",
|
||||
"endpoints": {
|
||||
"POST /orders": "Neue Bestellung erstellen",
|
||||
"GET /orders": "Alle Bestellungen abrufen",
|
||||
"DELETE /orders/{order_id}": "Bestellung löschen",
|
||||
"WS /ws": "WebSocket für Echtzeit-Updates"
|
||||
},
|
||||
"available_drinks": [drink.value for drink in DrinkType],
|
||||
"available_mate_types": [mate.value for mate in MateType]
|
||||
}
|
||||
|
||||
|
||||
@app.websocket("/ws")
|
||||
async def websocket_endpoint(websocket: WebSocket):
|
||||
"""
|
||||
WebSocket-Endpunkt für Echtzeit-Updates der Bestellungen.
|
||||
|
||||
Clients erhalten automatisch Updates bei:
|
||||
- Neuen Bestellungen
|
||||
- Gelöschten Bestellungen
|
||||
|
||||
Nachrichten-Formate:
|
||||
- order_created: {"type": "order_created", "order": {...}}
|
||||
- order_deleted: {"type": "order_deleted", "order_id": "..."}
|
||||
- all_orders: {"type": "all_orders", "orders": [...]}
|
||||
"""
|
||||
await websocket_manager.connect(websocket)
|
||||
|
||||
try:
|
||||
# Sende alle aktuellen Bestellungen beim Verbinden
|
||||
all_orders = [order.model_dump() for order in db.get_all_orders()]
|
||||
await websocket_manager.broadcast_all_orders(all_orders)
|
||||
|
||||
# Halte die Verbindung aufrecht und warte auf Nachrichten
|
||||
while True:
|
||||
# Warte auf Nachrichten vom Client (kann für Pings/Pongs verwendet werden)
|
||||
data = await websocket.receive_text()
|
||||
|
||||
# Optional: Echo für Pings
|
||||
if data == "ping":
|
||||
await websocket.send_text("pong")
|
||||
|
||||
except WebSocketDisconnect:
|
||||
websocket_manager.disconnect(websocket)
|
||||
except Exception as e:
|
||||
print(f"WebSocket-Fehler: {e}")
|
||||
websocket_manager.disconnect(websocket)
|
||||
|
||||
|
||||
@app.post("/orders", response_model=Order, status_code=201)
|
||||
async def create_order(order_request: CreateOrderRequest):
|
||||
"""
|
||||
Erstellt eine neue Bestellung.
|
||||
|
||||
- **drinks**: Liste der Getränke mit Typ und Mate-Sorte
|
||||
|
||||
Alle verbundenen WebSocket-Clients erhalten automatisch ein Update.
|
||||
"""
|
||||
if not order_request.drinks:
|
||||
raise HTTPException(status_code=400, detail="Mindestens ein Getränk muss bestellt werden")
|
||||
|
||||
order = db.create_order(order_request.drinks)
|
||||
order = await db.create_order(order_request.drinks)
|
||||
return order
|
||||
|
||||
|
||||
|
@ -38,8 +96,10 @@ async def delete_order(order_id: str):
|
|||
Löscht eine spezifische Bestellung.
|
||||
|
||||
- **order_id**: ID der zu löschenden Bestellung
|
||||
|
||||
Alle verbundenen WebSocket-Clients erhalten automatisch ein Update.
|
||||
"""
|
||||
success = db.delete_order(order_id)
|
||||
success = await db.delete_order(order_id)
|
||||
if not success:
|
||||
raise HTTPException(status_code=404, detail="Bestellung nicht gefunden")
|
||||
|
||||
|
|
12
backend/pytest.ini
Normal file
12
backend/pytest.ini
Normal file
|
@ -0,0 +1,12 @@
|
|||
[tool:pytest]
|
||||
testpaths = .
|
||||
python_files = test_*.py
|
||||
python_classes = Test*
|
||||
python_functions = test_*
|
||||
addopts =
|
||||
-v
|
||||
--tb=short
|
||||
--strict-markers
|
||||
--disable-warnings
|
||||
markers =
|
||||
asyncio: marks tests as async (deselect with '-m "not asyncio"')
|
|
@ -2,4 +2,8 @@ fastapi>=0.115.0
|
|||
uvicorn[standard]>=0.32.0
|
||||
pydantic>=2.10.0
|
||||
python-multipart>=0.0.20
|
||||
requests>=2.32.0
|
||||
requests>=2.32.0
|
||||
websockets>=12.0
|
||||
pytest>=7.0.0
|
||||
pytest-asyncio>=0.23.0
|
||||
httpx>=0.25.0
|
83
backend/run_tests.py
Normal file
83
backend/run_tests.py
Normal file
|
@ -0,0 +1,83 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Einfacher Test-Runner für die Tschunk Order API
|
||||
Führt automatisch alle Tests aus
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
|
||||
def run_tests():
|
||||
"""Führe alle Tests aus"""
|
||||
print("🧪 Tschunk Order API - Automatisierte Tests")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
# Prüfe ob wir im richtigen Verzeichnis sind
|
||||
if not os.path.exists("main.py"):
|
||||
print("❌ Fehler: main.py nicht gefunden!")
|
||||
print(" Stelle sicher, dass du im backend/ Verzeichnis bist.")
|
||||
return False
|
||||
|
||||
# Prüfe ob virtuelle Environment aktiviert ist
|
||||
if not hasattr(sys, 'real_prefix') and not (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix):
|
||||
print("⚠️ Warnung: Virtuelles Environment scheint nicht aktiviert zu sein.")
|
||||
print(" Führe 'source venv/bin/activate' aus, falls Tests fehlschlagen.")
|
||||
print()
|
||||
|
||||
try:
|
||||
# Führe pytest aus
|
||||
print("🚀 Starte Tests...")
|
||||
print()
|
||||
|
||||
result = subprocess.run([
|
||||
sys.executable, "-m", "pytest",
|
||||
"test_automated.py",
|
||||
"-v",
|
||||
"--tb=short",
|
||||
"--color=yes"
|
||||
], capture_output=False, text=True)
|
||||
|
||||
print()
|
||||
print("=" * 60)
|
||||
|
||||
if result.returncode == 0:
|
||||
print("✅ Alle Tests erfolgreich!")
|
||||
print("🎉 Die API funktioniert einwandfrei!")
|
||||
return True
|
||||
else:
|
||||
print("❌ Einige Tests fehlgeschlagen!")
|
||||
print("🔧 Überprüfe die Fehlermeldungen oben.")
|
||||
return False
|
||||
|
||||
except FileNotFoundError:
|
||||
print("❌ Fehler: pytest nicht gefunden!")
|
||||
print(" Installiere pytest mit: pip install pytest")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"❌ Unerwarteter Fehler: {e}")
|
||||
return False
|
||||
|
||||
def show_test_info():
|
||||
"""Zeige Informationen über die Tests"""
|
||||
print("📋 Test-Übersicht:")
|
||||
print(" • HTTP-Endpunkte (GET, POST, DELETE)")
|
||||
print(" • Datenvalidierung (Pydantic)")
|
||||
print(" • WebSocket-Funktionalität")
|
||||
print(" • Datenbankoperationen")
|
||||
print(" • Fehlerbehandlung")
|
||||
print()
|
||||
print("🔧 Verfügbare Befehle:")
|
||||
print(" python run_tests.py - Führe alle Tests aus")
|
||||
print(" python -m pytest - Führe pytest direkt aus")
|
||||
print(" python -m pytest -v - Detaillierte Ausgabe")
|
||||
print(" python -m pytest -k 'test_create' - Nur bestimmte Tests")
|
||||
print()
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) > 1 and sys.argv[1] == "--info":
|
||||
show_test_info()
|
||||
else:
|
||||
success = run_tests()
|
||||
sys.exit(0 if success else 1)
|
|
@ -1,133 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test-Skript für die Tschunk Order API
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
BASE_URL = "http://localhost:8000"
|
||||
|
||||
def test_api():
|
||||
"""Testet alle API-Endpunkte."""
|
||||
|
||||
print("🚀 Starte API-Tests...\n")
|
||||
|
||||
# Test 1: Root endpoint
|
||||
print("1. Teste Root-Endpoint...")
|
||||
try:
|
||||
response = requests.get(f"{BASE_URL}/")
|
||||
print(f"✅ Root endpoint: {response.status_code}")
|
||||
print(f" Verfügbare Getränke: {response.json()['available_drinks']}")
|
||||
print(f" Verfügbare Mate-Sorten: {response.json()['available_mate_types']}\n")
|
||||
except Exception as e:
|
||||
print(f"❌ Fehler beim Root endpoint: {e}\n")
|
||||
|
||||
# Test 2: Getränke anzeigen
|
||||
print("2. Teste Getränke-Endpoint...")
|
||||
try:
|
||||
response = requests.get(f"{BASE_URL}/drinks")
|
||||
print(f"✅ Getränke endpoint: {response.status_code}")
|
||||
print(f" Getränke: {response.json()}\n")
|
||||
except Exception as e:
|
||||
print(f"❌ Fehler beim Getränke endpoint: {e}\n")
|
||||
|
||||
# Test 3: Bestellung erstellen (mit quantity und notes)
|
||||
print("3. Teste Bestellung erstellen (mit Mengenangabe und Sonderwünschen)...")
|
||||
test_order = {
|
||||
"drinks": [
|
||||
{
|
||||
"drink_type": "Tschunk",
|
||||
"mate_type": "Club Mate",
|
||||
"quantity": 2,
|
||||
"notes": "Bitte extra kalt servieren"
|
||||
},
|
||||
{
|
||||
"drink_type": "Virgin Tschunk",
|
||||
"mate_type": "Flora Mate"
|
||||
# quantity und notes werden nicht angegeben, sollten automatisch 1 bzw. None sein
|
||||
},
|
||||
{
|
||||
"drink_type": "Tschunk Hannover-Mische",
|
||||
"mate_type": "Club Mate",
|
||||
"quantity": 3,
|
||||
"notes": "Ohne Eiswürfel, bitte mit Limettenscheibe"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/orders",
|
||||
json=test_order,
|
||||
headers={"Content-Type": "application/json"}
|
||||
)
|
||||
print(f"✅ Bestellung erstellt: {response.status_code}")
|
||||
order_data = response.json()
|
||||
print(f" Bestell-ID: {order_data['id']}")
|
||||
print(f" Bestelldatum: {order_data['order_date']}")
|
||||
print(f" Anzahl Getränke: {len(order_data['drinks'])}")
|
||||
|
||||
# Zeige Details der Getränke
|
||||
for i, drink in enumerate(order_data['drinks']):
|
||||
notes_info = f" (Anmerkung: {drink['notes']})" if drink.get('notes') else ""
|
||||
print(f" - Getränk {i+1}: {drink['drink_type']} mit {drink['mate_type']} (Menge: {drink['quantity']}){notes_info}")
|
||||
|
||||
# Speichere Order-ID für späteren Test
|
||||
order_id = order_data['id']
|
||||
print()
|
||||
except Exception as e:
|
||||
print(f"❌ Fehler beim Erstellen der Bestellung: {e}")
|
||||
return
|
||||
|
||||
# Test 4: Alle Bestellungen abrufen
|
||||
print("4. Teste Alle Bestellungen abrufen...")
|
||||
try:
|
||||
response = requests.get(f"{BASE_URL}/orders")
|
||||
print(f"✅ Alle Bestellungen: {response.status_code}")
|
||||
orders = response.json()
|
||||
print(f" Anzahl Bestellungen: {len(orders)}")
|
||||
for order in orders:
|
||||
total_quantity = sum(drink['quantity'] for drink in order['drinks'])
|
||||
drinks_with_notes = sum(1 for drink in order['drinks'] if drink.get('notes'))
|
||||
print(f" - Bestellung {order['id']}: {len(order['drinks'])} verschiedene Getränke (Gesamtmenge: {total_quantity}, {drinks_with_notes} mit Anmerkungen)")
|
||||
print()
|
||||
except Exception as e:
|
||||
print(f"❌ Fehler beim Abrufen der Bestellungen: {e}\n")
|
||||
|
||||
# Test 5: Bestellung löschen
|
||||
print("5. Teste Bestellung löschen...")
|
||||
try:
|
||||
response = requests.delete(f"{BASE_URL}/orders/{order_id}")
|
||||
print(f"✅ Bestellung gelöscht: {response.status_code}")
|
||||
print(f" Nachricht: {response.json()['message']}")
|
||||
print()
|
||||
except Exception as e:
|
||||
print(f"❌ Fehler beim Löschen der Bestellung: {e}\n")
|
||||
|
||||
# Test 6: Bestätige Löschung
|
||||
print("6. Bestätige Löschung...")
|
||||
try:
|
||||
response = requests.get(f"{BASE_URL}/orders")
|
||||
orders = response.json()
|
||||
print(f"✅ Verbleibende Bestellungen: {len(orders)}")
|
||||
print()
|
||||
except Exception as e:
|
||||
print(f"❌ Fehler beim Abrufen der Bestellungen: {e}\n")
|
||||
|
||||
print("🎉 API-Tests abgeschlossen!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 50)
|
||||
print("Tschunk Order API - Test Suite")
|
||||
print("=" * 50)
|
||||
print()
|
||||
print("⚠️ Stelle sicher, dass der Server auf http://localhost:8000 läuft!")
|
||||
print()
|
||||
|
||||
# Frage nach Bestätigung
|
||||
input("Drücke Enter um fortzufahren...")
|
||||
print()
|
||||
|
||||
test_api()
|
550
backend/test_automated.py
Normal file
550
backend/test_automated.py
Normal file
|
@ -0,0 +1,550 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Automatisierte Tests für die Tschunk Order API
|
||||
Verwendet pytest und FastAPI TestClient
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import asyncio
|
||||
import json
|
||||
from httpx import AsyncClient
|
||||
from fastapi.testclient import TestClient
|
||||
from main import app
|
||||
from models import DrinkType, MateType
|
||||
|
||||
|
||||
class TestTschunkOrderAPI:
|
||||
"""Test-Klasse für die Tschunk Order API"""
|
||||
|
||||
@pytest.fixture
|
||||
def client(self):
|
||||
"""TestClient für HTTP-Endpunkte"""
|
||||
return TestClient(app)
|
||||
|
||||
@pytest.fixture
|
||||
def sample_order(self):
|
||||
"""Beispiel-Bestellung für Tests"""
|
||||
return {
|
||||
"drinks": [
|
||||
{
|
||||
"drink_type": "Tschunk",
|
||||
"mate_type": "Club Mate",
|
||||
"quantity": 2,
|
||||
"notes": "Test-Bestellung"
|
||||
},
|
||||
{
|
||||
"drink_type": "Virgin Tschunk",
|
||||
"mate_type": "Flora Mate",
|
||||
"quantity": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
def test_root_endpoint(self, client):
|
||||
"""Test des Root-Endpunkts"""
|
||||
response = client.get("/")
|
||||
assert response.status_code == 200
|
||||
|
||||
data = response.json()
|
||||
assert data["message"] == "Willkommen zur Tschunk Order API!"
|
||||
assert data["version"] == "1.0.0"
|
||||
assert "POST /orders" in data["endpoints"]
|
||||
assert "WS /ws" in data["endpoints"]
|
||||
|
||||
# Prüfe verfügbare Getränke
|
||||
assert "Tschunk" in data["available_drinks"]
|
||||
assert "Virgin Tschunk" in data["available_drinks"]
|
||||
assert "Flora Mate" in data["available_mate_types"]
|
||||
assert "Club Mate" in data["available_mate_types"]
|
||||
|
||||
def test_drinks_endpoint(self, client):
|
||||
"""Test des Drinks-Endpunkts"""
|
||||
response = client.get("/drinks")
|
||||
assert response.status_code == 200
|
||||
|
||||
data = response.json()
|
||||
assert "drink_types" in data
|
||||
assert "mate_types" in data
|
||||
|
||||
# Prüfe alle Getränketypen
|
||||
expected_drinks = [
|
||||
"Tschunk",
|
||||
"Tschunk Hannover-Mische",
|
||||
"Tschunk alkoholfreier Rum",
|
||||
"Virgin Tschunk"
|
||||
]
|
||||
for drink in expected_drinks:
|
||||
assert drink in data["drink_types"]
|
||||
|
||||
# Prüfe Mate-Sorten
|
||||
expected_mates = ["Flora Mate", "Club Mate"]
|
||||
for mate in expected_mates:
|
||||
assert mate in data["mate_types"]
|
||||
|
||||
def test_create_order_success(self, client, sample_order):
|
||||
"""Test erfolgreiche Bestellerstellung"""
|
||||
response = client.post("/orders", json=sample_order)
|
||||
assert response.status_code == 201
|
||||
|
||||
order = response.json()
|
||||
assert "id" in order
|
||||
assert "order_date" in order
|
||||
assert "drinks" in order
|
||||
assert len(order["drinks"]) == 2
|
||||
|
||||
# Prüfe erste Bestellung
|
||||
first_drink = order["drinks"][0]
|
||||
assert first_drink["drink_type"] == "Tschunk"
|
||||
assert first_drink["mate_type"] == "Club Mate"
|
||||
assert first_drink["quantity"] == 2
|
||||
assert first_drink["notes"] == "Test-Bestellung"
|
||||
|
||||
# Prüfe zweite Bestellung (ohne notes)
|
||||
second_drink = order["drinks"][1]
|
||||
assert second_drink["drink_type"] == "Virgin Tschunk"
|
||||
assert second_drink["mate_type"] == "Flora Mate"
|
||||
assert second_drink["quantity"] == 1
|
||||
assert second_drink["notes"] is None
|
||||
|
||||
def test_create_order_empty_drinks(self, client):
|
||||
"""Test Bestellerstellung ohne Getränke"""
|
||||
response = client.post("/orders", json={"drinks": []})
|
||||
assert response.status_code == 400
|
||||
assert "Mindestens ein Getränk muss bestellt werden" in response.json()["detail"]
|
||||
|
||||
def test_create_order_invalid_drink_type(self, client):
|
||||
"""Test Bestellerstellung mit ungültigem Getränketyp"""
|
||||
invalid_order = {
|
||||
"drinks": [
|
||||
{
|
||||
"drink_type": "Ungültiges Getränk",
|
||||
"mate_type": "Club Mate"
|
||||
}
|
||||
]
|
||||
}
|
||||
response = client.post("/orders", json=invalid_order)
|
||||
assert response.status_code == 422 # Validation Error
|
||||
|
||||
def test_get_all_orders(self, client, sample_order):
|
||||
"""Test Abrufen aller Bestellungen"""
|
||||
# Erstelle zuerst eine Bestellung
|
||||
create_response = client.post("/orders", json=sample_order)
|
||||
assert create_response.status_code == 201
|
||||
|
||||
# Hole alle Bestellungen
|
||||
response = client.get("/orders")
|
||||
assert response.status_code == 200
|
||||
|
||||
orders = response.json()
|
||||
assert isinstance(orders, list)
|
||||
assert len(orders) >= 1
|
||||
|
||||
# Prüfe, dass unsere Bestellung dabei ist
|
||||
order_ids = [order["id"] for order in orders]
|
||||
assert create_response.json()["id"] in order_ids
|
||||
|
||||
def test_delete_order_success(self, client, sample_order):
|
||||
"""Test erfolgreiche Bestelllöschung"""
|
||||
# Erstelle zuerst eine Bestellung
|
||||
create_response = client.post("/orders", json=sample_order)
|
||||
assert create_response.status_code == 201
|
||||
order_id = create_response.json()["id"]
|
||||
|
||||
# Lösche die Bestellung
|
||||
response = client.delete(f"/orders/{order_id}")
|
||||
assert response.status_code == 200
|
||||
assert f"Bestellung {order_id} wurde erfolgreich gelöscht" in response.json()["message"]
|
||||
|
||||
# Prüfe, dass die Bestellung wirklich gelöscht wurde
|
||||
get_response = client.get("/orders")
|
||||
orders = get_response.json()
|
||||
order_ids = [order["id"] for order in orders]
|
||||
assert order_id not in order_ids
|
||||
|
||||
def test_delete_order_not_found(self, client):
|
||||
"""Test Löschung einer nicht existierenden Bestellung"""
|
||||
response = client.delete("/orders/non-existent-id")
|
||||
assert response.status_code == 404
|
||||
assert "Bestellung nicht gefunden" in response.json()["detail"]
|
||||
|
||||
def test_order_with_default_quantity(self, client):
|
||||
"""Test Bestellung ohne quantity (sollte 1 sein)"""
|
||||
order_without_quantity = {
|
||||
"drinks": [
|
||||
{
|
||||
"drink_type": "Tschunk",
|
||||
"mate_type": "Club Mate"
|
||||
# quantity wird nicht angegeben
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
response = client.post("/orders", json=order_without_quantity)
|
||||
assert response.status_code == 201
|
||||
|
||||
order = response.json()
|
||||
assert order["drinks"][0]["quantity"] == 1
|
||||
|
||||
def test_order_with_notes(self, client):
|
||||
"""Test Bestellung mit Anmerkungen"""
|
||||
order_with_notes = {
|
||||
"drinks": [
|
||||
{
|
||||
"drink_type": "Virgin Tschunk",
|
||||
"mate_type": "Flora Mate",
|
||||
"quantity": 1,
|
||||
"notes": "Bitte extra kalt servieren"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
response = client.post("/orders", json=order_with_notes)
|
||||
assert response.status_code == 201
|
||||
|
||||
order = response.json()
|
||||
assert order["drinks"][0]["notes"] == "Bitte extra kalt servieren"
|
||||
|
||||
|
||||
class TestWebSocket:
|
||||
"""Test-Klasse für WebSocket-Funktionalität"""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_websocket_connection(self):
|
||||
"""Test WebSocket-Verbindung und initiale Nachricht"""
|
||||
from websocket_manager import websocket_manager
|
||||
|
||||
# Erstelle einen Test-WebSocket
|
||||
class MockWebSocket:
|
||||
def __init__(self):
|
||||
self.messages = []
|
||||
self.closed = False
|
||||
|
||||
async def accept(self):
|
||||
pass
|
||||
|
||||
async def send_text(self, message):
|
||||
self.messages.append(message)
|
||||
|
||||
async def receive_text(self):
|
||||
return "ping"
|
||||
|
||||
async def close(self):
|
||||
self.closed = True
|
||||
|
||||
# Teste WebSocket-Manager
|
||||
mock_ws = MockWebSocket()
|
||||
|
||||
# Verbinde
|
||||
await websocket_manager.connect(mock_ws)
|
||||
assert len(websocket_manager.active_connections) == 1
|
||||
|
||||
# Teste Broadcast
|
||||
test_message = {"type": "test", "data": "test"}
|
||||
await websocket_manager.broadcast(test_message)
|
||||
assert len(mock_ws.messages) == 1
|
||||
|
||||
# Teste spezifische Broadcasts
|
||||
await websocket_manager.broadcast_order_created({"id": "test", "drinks": []})
|
||||
assert len(mock_ws.messages) == 2
|
||||
|
||||
await websocket_manager.broadcast_order_deleted("test-id")
|
||||
assert len(mock_ws.messages) == 3
|
||||
|
||||
# Trenne Verbindung
|
||||
websocket_manager.disconnect(mock_ws)
|
||||
assert len(websocket_manager.active_connections) == 0
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_websocket_integration_create_order(self):
|
||||
"""Test WebSocket-Broadcast bei Bestellerstellung über API"""
|
||||
from websocket_manager import websocket_manager
|
||||
from database import OrderDatabase
|
||||
from models import Drink, DrinkType, MateType
|
||||
|
||||
# Erstelle einen Test-WebSocket
|
||||
class MockWebSocket:
|
||||
def __init__(self):
|
||||
self.messages = []
|
||||
self.closed = False
|
||||
|
||||
async def accept(self):
|
||||
pass
|
||||
|
||||
async def send_text(self, message):
|
||||
self.messages.append(message)
|
||||
|
||||
async def receive_text(self):
|
||||
return "ping"
|
||||
|
||||
async def close(self):
|
||||
self.closed = True
|
||||
|
||||
# Verbinde Mock-WebSocket
|
||||
mock_ws = MockWebSocket()
|
||||
await websocket_manager.connect(mock_ws)
|
||||
|
||||
# Erstelle Test-Datenbank
|
||||
test_db = OrderDatabase()
|
||||
|
||||
# Erstelle Bestellung über Datenbank (sollte WebSocket-Broadcast auslösen)
|
||||
drinks = [
|
||||
Drink(
|
||||
drink_type=DrinkType.TSCHUNK,
|
||||
mate_type=MateType.CLUB_MATE,
|
||||
quantity=2,
|
||||
notes="Test-Bestellung"
|
||||
)
|
||||
]
|
||||
|
||||
order = await test_db.create_order(drinks)
|
||||
|
||||
# Prüfe, dass WebSocket-Nachricht gesendet wurde
|
||||
assert len(mock_ws.messages) >= 1
|
||||
|
||||
# Finde die "order_created" Nachricht
|
||||
order_created_message = None
|
||||
for message in mock_ws.messages:
|
||||
try:
|
||||
data = json.loads(message)
|
||||
if data.get("type") == "order_created":
|
||||
order_created_message = data
|
||||
break
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
|
||||
assert order_created_message is not None
|
||||
assert order_created_message["type"] == "order_created"
|
||||
assert "order" in order_created_message
|
||||
assert order_created_message["order"]["id"] == order.id
|
||||
|
||||
# Cleanup
|
||||
websocket_manager.disconnect(mock_ws)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_websocket_integration_delete_order(self):
|
||||
"""Test WebSocket-Broadcast bei Bestelllöschung über API"""
|
||||
from websocket_manager import websocket_manager
|
||||
from database import OrderDatabase
|
||||
from models import Drink, DrinkType, MateType
|
||||
|
||||
# Erstelle einen Test-WebSocket
|
||||
class MockWebSocket:
|
||||
def __init__(self):
|
||||
self.messages = []
|
||||
self.closed = False
|
||||
|
||||
async def accept(self):
|
||||
pass
|
||||
|
||||
async def send_text(self, message):
|
||||
self.messages.append(message)
|
||||
|
||||
async def receive_text(self):
|
||||
return "ping"
|
||||
|
||||
async def close(self):
|
||||
self.closed = True
|
||||
|
||||
# Verbinde Mock-WebSocket
|
||||
mock_ws = MockWebSocket()
|
||||
await websocket_manager.connect(mock_ws)
|
||||
|
||||
# Erstelle Test-Datenbank
|
||||
test_db = OrderDatabase()
|
||||
|
||||
# Erstelle zuerst eine Bestellung
|
||||
drinks = [
|
||||
Drink(
|
||||
drink_type=DrinkType.VIRGIN_TSCHUNK,
|
||||
mate_type=MateType.FLORA_MATE,
|
||||
quantity=1
|
||||
)
|
||||
]
|
||||
|
||||
order = await test_db.create_order(drinks)
|
||||
order_id = order.id
|
||||
|
||||
# Lösche die Bestellung (sollte WebSocket-Broadcast auslösen)
|
||||
success = await test_db.delete_order(order_id)
|
||||
assert success is True
|
||||
|
||||
# Prüfe, dass WebSocket-Nachricht gesendet wurde
|
||||
assert len(mock_ws.messages) >= 2 # Mindestens create + delete
|
||||
|
||||
# Finde die "order_deleted" Nachricht
|
||||
order_deleted_message = None
|
||||
for message in mock_ws.messages:
|
||||
try:
|
||||
data = json.loads(message)
|
||||
if data.get("type") == "order_deleted":
|
||||
order_deleted_message = data
|
||||
break
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
|
||||
assert order_deleted_message is not None
|
||||
assert order_deleted_message["type"] == "order_deleted"
|
||||
assert "order_id" in order_deleted_message
|
||||
assert order_deleted_message["order_id"] == order_id
|
||||
|
||||
# Cleanup
|
||||
websocket_manager.disconnect(mock_ws)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_websocket_multiple_clients(self):
|
||||
"""Test WebSocket-Broadcast an mehrere verbundene Clients"""
|
||||
from websocket_manager import websocket_manager
|
||||
from database import OrderDatabase
|
||||
from models import Drink, DrinkType, MateType
|
||||
|
||||
# Erstelle mehrere Test-WebSockets
|
||||
class MockWebSocket:
|
||||
def __init__(self, client_id):
|
||||
self.client_id = client_id
|
||||
self.messages = []
|
||||
self.closed = False
|
||||
|
||||
async def accept(self):
|
||||
pass
|
||||
|
||||
async def send_text(self, message):
|
||||
self.messages.append(message)
|
||||
|
||||
async def receive_text(self):
|
||||
return "ping"
|
||||
|
||||
async def close(self):
|
||||
self.closed = True
|
||||
|
||||
# Verbinde mehrere Mock-WebSockets
|
||||
mock_ws1 = MockWebSocket("client1")
|
||||
mock_ws2 = MockWebSocket("client2")
|
||||
mock_ws3 = MockWebSocket("client3")
|
||||
|
||||
await websocket_manager.connect(mock_ws1)
|
||||
await websocket_manager.connect(mock_ws2)
|
||||
await websocket_manager.connect(mock_ws3)
|
||||
|
||||
assert len(websocket_manager.active_connections) == 3
|
||||
|
||||
# Erstelle Test-Datenbank
|
||||
test_db = OrderDatabase()
|
||||
|
||||
# Erstelle Bestellung (sollte an alle Clients broadcasten)
|
||||
drinks = [
|
||||
Drink(
|
||||
drink_type=DrinkType.TSCHUNK_HANNOVER_MISCHE,
|
||||
mate_type=MateType.CLUB_MATE,
|
||||
quantity=3,
|
||||
notes="Multi-Client Test"
|
||||
)
|
||||
]
|
||||
|
||||
order = await test_db.create_order(drinks)
|
||||
|
||||
# Prüfe, dass alle Clients die Nachricht erhalten haben
|
||||
for mock_ws in [mock_ws1, mock_ws2, mock_ws3]:
|
||||
assert len(mock_ws.messages) >= 1
|
||||
|
||||
# Finde die "order_created" Nachricht
|
||||
order_created_found = False
|
||||
for message in mock_ws.messages:
|
||||
try:
|
||||
data = json.loads(message)
|
||||
if data.get("type") == "order_created":
|
||||
order_created_found = True
|
||||
assert data["order"]["id"] == order.id
|
||||
break
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
|
||||
assert order_created_found, f"Client {mock_ws.client_id} hat keine order_created Nachricht erhalten"
|
||||
|
||||
# Cleanup
|
||||
websocket_manager.disconnect(mock_ws1)
|
||||
websocket_manager.disconnect(mock_ws2)
|
||||
websocket_manager.disconnect(mock_ws3)
|
||||
|
||||
|
||||
class TestDatabase:
|
||||
"""Test-Klasse für Datenbankfunktionalität"""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_database_operations(self):
|
||||
"""Test Datenbankoperationen"""
|
||||
from database import OrderDatabase
|
||||
from models import Drink, DrinkType, MateType
|
||||
|
||||
# Erstelle neue Datenbank-Instanz für Tests
|
||||
test_db = OrderDatabase()
|
||||
|
||||
# Teste Bestellerstellung
|
||||
drinks = [
|
||||
Drink(
|
||||
drink_type=DrinkType.TSCHUNK,
|
||||
mate_type=MateType.CLUB_MATE,
|
||||
quantity=2,
|
||||
notes="Test"
|
||||
)
|
||||
]
|
||||
|
||||
order = await test_db.create_order(drinks)
|
||||
assert order.id is not None
|
||||
assert len(order.drinks) == 1
|
||||
assert order.drinks[0].drink_type == DrinkType.TSCHUNK
|
||||
|
||||
# Teste Abrufen aller Bestellungen
|
||||
all_orders = test_db.get_all_orders()
|
||||
assert len(all_orders) == 1
|
||||
assert all_orders[0].id == order.id
|
||||
|
||||
# Teste Abrufen spezifischer Bestellung
|
||||
retrieved_order = test_db.get_order_by_id(order.id)
|
||||
assert retrieved_order is not None
|
||||
assert retrieved_order.id == order.id
|
||||
|
||||
# Teste Löschung
|
||||
success = await test_db.delete_order(order.id)
|
||||
assert success is True
|
||||
|
||||
# Prüfe, dass Bestellung gelöscht wurde
|
||||
all_orders_after = test_db.get_all_orders()
|
||||
assert len(all_orders_after) == 0
|
||||
|
||||
# Teste Löschung nicht existierender Bestellung
|
||||
success = await test_db.delete_order("non-existent")
|
||||
assert success is False
|
||||
|
||||
|
||||
def run_all_tests():
|
||||
"""Führe alle Tests aus"""
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
print("🧪 Starte automatisierte Tests...")
|
||||
print("=" * 50)
|
||||
|
||||
# Führe pytest aus
|
||||
result = subprocess.run([
|
||||
sys.executable, "-m", "pytest",
|
||||
"test_automated.py",
|
||||
"-v",
|
||||
"--tb=short"
|
||||
], capture_output=True, text=True)
|
||||
|
||||
print(result.stdout)
|
||||
if result.stderr:
|
||||
print("STDERR:", result.stderr)
|
||||
|
||||
print("=" * 50)
|
||||
if result.returncode == 0:
|
||||
print("✅ Alle Tests erfolgreich!")
|
||||
else:
|
||||
print("❌ Einige Tests fehlgeschlagen!")
|
||||
|
||||
return result.returncode == 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = run_all_tests()
|
||||
exit(0 if success else 1)
|
74
backend/websocket_manager.py
Normal file
74
backend/websocket_manager.py
Normal file
|
@ -0,0 +1,74 @@
|
|||
from fastapi import WebSocket
|
||||
from typing import List, Dict, Any
|
||||
import json
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class WebSocketManager:
|
||||
def __init__(self):
|
||||
self.active_connections: List[WebSocket] = []
|
||||
|
||||
async def connect(self, websocket: WebSocket):
|
||||
"""Neue WebSocket-Verbindung hinzufügen."""
|
||||
await websocket.accept()
|
||||
self.active_connections.append(websocket)
|
||||
print(f"WebSocket verbunden. Aktive Verbindungen: {len(self.active_connections)}")
|
||||
|
||||
def disconnect(self, websocket: WebSocket):
|
||||
"""WebSocket-Verbindung entfernen."""
|
||||
if websocket in self.active_connections:
|
||||
self.active_connections.remove(websocket)
|
||||
print(f"WebSocket getrennt. Aktive Verbindungen: {len(self.active_connections)}")
|
||||
|
||||
async def broadcast(self, message: Dict[str, Any]):
|
||||
"""Nachricht an alle verbundenen Clients senden."""
|
||||
if not self.active_connections:
|
||||
return
|
||||
|
||||
# Nachricht als JSON serialisieren
|
||||
message_json = json.dumps(message, default=str)
|
||||
|
||||
# An alle verbundenen Clients senden
|
||||
disconnected = []
|
||||
for connection in self.active_connections:
|
||||
try:
|
||||
await connection.send_text(message_json)
|
||||
except Exception as e:
|
||||
print(f"Fehler beim Senden an WebSocket: {e}")
|
||||
disconnected.append(connection)
|
||||
|
||||
# Getrennte Verbindungen entfernen
|
||||
for connection in disconnected:
|
||||
self.disconnect(connection)
|
||||
|
||||
async def broadcast_order_created(self, order: Dict[str, Any]):
|
||||
"""Broadcast für neue Bestellung."""
|
||||
message = {
|
||||
"type": "order_created",
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"order": order
|
||||
}
|
||||
await self.broadcast(message)
|
||||
|
||||
async def broadcast_order_deleted(self, order_id: str):
|
||||
"""Broadcast für gelöschte Bestellung."""
|
||||
message = {
|
||||
"type": "order_deleted",
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"order_id": order_id
|
||||
}
|
||||
await self.broadcast(message)
|
||||
|
||||
async def broadcast_all_orders(self, orders: List[Dict[str, Any]]):
|
||||
"""Broadcast für alle Bestellungen (z.B. bei initialer Verbindung)."""
|
||||
message = {
|
||||
"type": "all_orders",
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"orders": orders
|
||||
}
|
||||
await self.broadcast(message)
|
||||
|
||||
|
||||
# Globale WebSocket-Manager-Instanz
|
||||
websocket_manager = WebSocketManager()
|
Loading…
Add table
Add a link
Reference in a new issue