Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 74c10b719b | |||
| 1f631f2fcc |
@@ -0,0 +1,21 @@
|
||||
"""Flask-Blueprint für Admin-Route."""
|
||||
|
||||
from flask import Blueprint, current_app, jsonify
|
||||
|
||||
admin_bp = Blueprint("admin", __name__)
|
||||
|
||||
|
||||
@admin_bp.route("/admin/queue", methods=["GET"])
|
||||
def queue_status() -> tuple:
|
||||
"""Gibt Queue-Status zurück."""
|
||||
qh = current_app.queue_handler # type: ignore[attr-defined]
|
||||
return jsonify({"queue_size": qh.queue_size(), "status": "ok"}), 200
|
||||
|
||||
|
||||
@admin_bp.route("/admin/stats", methods=["GET"])
|
||||
def stats() -> tuple:
|
||||
"""Gibt System-Statistiken zurück."""
|
||||
qh = current_app.queue_handler # type: ignore[attr-defined]
|
||||
return jsonify(
|
||||
{"queue_size": qh.queue_size(), "uptime": "placeholder", "version": "0.1.0"}
|
||||
), 200
|
||||
@@ -0,0 +1,48 @@
|
||||
"""Tests für Admin-Route."""
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from flask import Flask
|
||||
|
||||
from src.routes.admin import admin_bp
|
||||
|
||||
|
||||
class TestAdminRoute:
|
||||
"""Tests für Admin-Route."""
|
||||
|
||||
def setup_method(self) -> None:
|
||||
"""Erstellt Flask-App mit mocktem QueueHandler."""
|
||||
self.app = Flask(__name__)
|
||||
self.app.queue_handler = MagicMock()
|
||||
self.app.queue_handler.queue_size.return_value = 42
|
||||
self.app.register_blueprint(admin_bp, url_prefix="/")
|
||||
self.client = self.app.test_client()
|
||||
|
||||
def test_get_admin_queue_ergibt_200(self) -> None:
|
||||
"""GET /admin/queue → 200 + JSON mit queue_size."""
|
||||
response = self.client.get("/admin/queue")
|
||||
assert response.status_code == 200
|
||||
data = response.get_json()
|
||||
assert "queue_size" in data
|
||||
assert data["status"] == "ok"
|
||||
|
||||
def test_get_admin_queue_queue_size_ist_integer(self) -> None:
|
||||
"""GET /admin/queue → queue_size ist Integer."""
|
||||
response = self.client.get("/admin/queue")
|
||||
data = response.get_json()
|
||||
assert isinstance(data["queue_size"], int)
|
||||
|
||||
def test_get_admin_stats_ergibt_200(self) -> None:
|
||||
"""GET /admin/stats → 200 + version '0.1.0'."""
|
||||
response = self.client.get("/admin/stats")
|
||||
assert response.status_code == 200
|
||||
data = response.get_json()
|
||||
assert data["version"] == "0.1.0"
|
||||
|
||||
def test_get_admin_stats_json_enhaelt_alle_keys(self) -> None:
|
||||
"""GET /admin/stats → JSON enthält alle Keys."""
|
||||
response = self.client.get("/admin/stats")
|
||||
data = response.get_json()
|
||||
assert "queue_size" in data
|
||||
assert "uptime" in data
|
||||
assert "version" in data
|
||||
@@ -0,0 +1,137 @@
|
||||
"""Integration-Tests für WhatsApp/Telegram-Webhook-Flows."""
|
||||
|
||||
import hashlib
|
||||
import hmac
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from flask import Flask
|
||||
import pytest
|
||||
|
||||
from src.app import create_app
|
||||
from src.routes.whatsapp import whatsapp_bp
|
||||
from src.routes.telegram import telegram_bp
|
||||
from src.routes.admin import admin_bp
|
||||
from src.queue.worker import verarbeite_submission, worker_loop
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def flask_app() -> Flask:
|
||||
"""Erstellt Flask-App mit allen Blueprints."""
|
||||
app = create_app()
|
||||
app.queue_handler = MagicMock()
|
||||
app.queue_handler.queue_size.return_value = 0
|
||||
app.register_blueprint(whatsapp_bp, url_prefix="/")
|
||||
app.register_blueprint(telegram_bp, url_prefix="/")
|
||||
app.register_blueprint(admin_bp, url_prefix="/")
|
||||
return app
|
||||
|
||||
|
||||
class TestWebhookIntegration:
|
||||
"""Tests für Webhook-Integration."""
|
||||
|
||||
def test_whatsapp_post_mit_gueltiger_signatur(self, flask_app: Flask) -> None:
|
||||
"""WhatsApp POST mit gültiger Signatur → 200, Submission in Queue."""
|
||||
with patch("src.routes.whatsapp.verify_whatsapp_signature", return_value=True):
|
||||
response = flask_app.test_client().post(
|
||||
"/webhook/whatsapp",
|
||||
data=b'{"object":"whatsapp","entry":[]}',
|
||||
headers={"X-Hub-Signature-256": "sha256=test"},
|
||||
content_type="application/json",
|
||||
)
|
||||
assert response.status_code == 200
|
||||
data = response.get_json()
|
||||
assert data["status"] == "accepted"
|
||||
flask_app.queue_handler.enqueue.assert_called_once()
|
||||
|
||||
def test_whatsapp_post_mit_ungueltiger_signatur(self, flask_app: Flask) -> None:
|
||||
"""WhatsApp POST mit ungültiger Signatur → 401."""
|
||||
with patch("src.routes.whatsapp.verify_whatsapp_signature", return_value=False):
|
||||
response = flask_app.test_client().post(
|
||||
"/webhook/whatsapp",
|
||||
data=b'{"object":"whatsapp","entry":[]}',
|
||||
headers={"X-Hub-Signature-256": "sha256=fake"},
|
||||
content_type="application/json",
|
||||
)
|
||||
assert response.status_code == 401
|
||||
data = response.get_json()
|
||||
assert "error" in data
|
||||
|
||||
|
||||
class TestTelegramIntegration:
|
||||
"""Tests für Telegram-Integration."""
|
||||
|
||||
def test_telegram_post_mit_gueltigem_payload(self, flask_app: Flask) -> None:
|
||||
"""Telegram POST mit gültigem Payload → 200."""
|
||||
response = flask_app.test_client().post(
|
||||
"/webhook/telegram",
|
||||
data={"message": {"message_id": "123", "chat": {"id": "456"}, "text": "Hallo"}},
|
||||
content_type="application/json",
|
||||
)
|
||||
assert response.status_code == 200
|
||||
data = response.get_json()
|
||||
assert data["status"] == "accepted"
|
||||
flask_app.queue_handler.enqueue.assert_called_once()
|
||||
|
||||
|
||||
class TestWorkerIntegration:
|
||||
"""Tests für Worker-Integration."""
|
||||
|
||||
def test_vollstaendiger_flow_whatsapp(self, flask_app: Flask) -> None:
|
||||
"""Vollständiger Flow: WhatsApp POST → Worker verarbeitet → Status 'erledigt'."""
|
||||
mock_qh = flask_app.queue_handler
|
||||
mock_submission = MagicMock()
|
||||
mock_submission.message_id = "msg001"
|
||||
mock_submission.zeitstempel = "2024-01-01T00:00:00"
|
||||
mock_submission.absender = "+491****7890"
|
||||
mock_submission.text = "suche Catan"
|
||||
mock_submission.status = "neu"
|
||||
mock_submission.enqueue.return_value = "stream_id"
|
||||
|
||||
# Simuliere: Submit wurde enqueued, dann vom Worker geholt
|
||||
call_count = [0]
|
||||
|
||||
def dequeue_side_effect(count: int = 5):
|
||||
call_count[0] += 1
|
||||
if call_count[0] <= 1:
|
||||
return [mock_submission]
|
||||
return []
|
||||
|
||||
mock_qh.dequeue.side_effect = dequeue_side_effect
|
||||
|
||||
with (
|
||||
patch("src.queue.worker.verarbeite_submission", return_value=True) as mock_verarb,
|
||||
patch("src.queue.worker.time.sleep", return_value=None),
|
||||
):
|
||||
worker_loop(mock_qh, poll_interval=0, batch_size=5)
|
||||
|
||||
assert mock_verarb.call_count == 1
|
||||
assert mock_qh.mark_done.call_count == 1
|
||||
|
||||
def test_vollstaendiger_flow_telegram(self, flask_app: Flask) -> None:
|
||||
"""Vollständiger Flow: Telegram POST → Worker verarbeitet → Status 'erledigt'."""
|
||||
mock_qh = flask_app.queue_handler
|
||||
mock_submission = MagicMock()
|
||||
mock_submission.message_id = "msg002"
|
||||
mock_submission.zeitstempel = "2024-01-01T00:00:00"
|
||||
mock_submission.absender = "@user123"
|
||||
mock_submission.text = "hilfe"
|
||||
mock_submission.status = "neu"
|
||||
|
||||
call_count = [0]
|
||||
|
||||
def dequeue_side_effect(count: int = 5):
|
||||
call_count[0] += 1
|
||||
if call_count[0] <= 1:
|
||||
return [mock_submission]
|
||||
return []
|
||||
|
||||
mock_qh.dequeue.side_effect = dequeue_side_effect
|
||||
|
||||
with (
|
||||
patch("src.queue.worker.verarbeite_submission", return_value=True) as mock_verarb,
|
||||
patch("src.queue.worker.time.sleep", return_value=None),
|
||||
):
|
||||
worker_loop(mock_qh, poll_interval=0, batch_size=5)
|
||||
|
||||
assert mock_verarb.call_count == 1
|
||||
assert mock_qh.mark_done.call_count == 1
|
||||
Reference in New Issue
Block a user