This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/backend/tests/test_messenger_api.py
Benjamin Admin 21a844cb8a fix: Restore all files lost during destructive rebase
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>
2026-02-09 09:51:32 +01:00

933 lines
34 KiB
Python

"""
Tests fuer die Messenger API.
Testet:
- Kontakt CRUD Operationen
- Konversations-Management
- Nachrichten-Versand
- CSV Import/Export
- Vorlagen-Verwaltung
- Gruppen-Verwaltung
- Statistiken
Hinweis: Diese Tests nutzen den Docker-Container.
Starten Sie den Backend-Container vor dem Testen:
docker compose up -d backend
"""
import pytest
import requests
import uuid
from typing import Optional
# Backend URL (Docker)
BASE_URL = "http://localhost:8000/api/messenger"
def skip_if_backend_unavailable():
"""Skip tests if backend is not running."""
try:
requests.get(f"{BASE_URL}/contacts", timeout=2)
except requests.exceptions.RequestException:
pytest.skip("Backend not available - start with 'docker compose up -d backend'")
def generate_unique_email():
"""Generiert eine eindeutige Email-Adresse fuer Tests."""
return f"test_{uuid.uuid4().hex[:8]}@example.com"
class TestMessengerContacts:
"""Tests fuer Kontakt-Endpoints."""
@pytest.fixture(autouse=True)
def check_backend(self):
skip_if_backend_unavailable()
def test_get_contacts(self):
"""Test: Kontaktliste abrufen."""
response = requests.get(f"{BASE_URL}/contacts")
assert response.status_code == 200
assert isinstance(response.json(), list)
def test_create_and_delete_contact(self):
"""Test: Kontakt erstellen und loeschen."""
contact_data = {
"name": "Test Familie Mueller",
"email": generate_unique_email(),
"phone": "+49 123 456789",
"student_name": "Max Mueller",
"class_name": "10a"
}
# Erstellen
response = requests.post(f"{BASE_URL}/contacts", json=contact_data)
assert response.status_code == 200
data = response.json()
assert data["name"] == "Test Familie Mueller"
assert "id" in data
assert "created_at" in data
assert "updated_at" in data
# Loeschen
contact_id = data["id"]
delete_response = requests.delete(f"{BASE_URL}/contacts/{contact_id}")
assert delete_response.status_code == 200
assert delete_response.json()["status"] == "deleted"
def test_update_contact(self):
"""Test: Kontakt aktualisieren."""
# Erstellen
contact_data = {"name": "Original Name", "email": generate_unique_email()}
create_response = requests.post(f"{BASE_URL}/contacts", json=contact_data)
contact_id = create_response.json()["id"]
# Aktualisieren
update_data = {"name": "Aktualisierter Name", "email": generate_unique_email()}
response = requests.put(f"{BASE_URL}/contacts/{contact_id}", json=update_data)
assert response.status_code == 200
assert response.json()["name"] == "Aktualisierter Name"
# Cleanup
requests.delete(f"{BASE_URL}/contacts/{contact_id}")
def test_get_single_contact(self):
"""Test: Einzelnen Kontakt abrufen."""
# Erstellen
contact_data = {"name": "Single Test", "email": generate_unique_email()}
create_response = requests.post(f"{BASE_URL}/contacts", json=contact_data)
contact_id = create_response.json()["id"]
# Abrufen
response = requests.get(f"{BASE_URL}/contacts/{contact_id}")
assert response.status_code == 200
assert response.json()["name"] == "Single Test"
# Cleanup
requests.delete(f"{BASE_URL}/contacts/{contact_id}")
def test_get_nonexistent_contact(self):
"""Test: Nicht existierenden Kontakt abrufen gibt 404."""
response = requests.get(f"{BASE_URL}/contacts/nonexistent-uuid")
assert response.status_code == 404
def test_filter_contacts_by_role(self):
"""Test: Kontakte nach Rolle filtern."""
# Kontakt mit Rolle erstellen
email = generate_unique_email()
contact_data = {"name": "Teacher Test", "email": email, "role": "teacher"}
create_response = requests.post(f"{BASE_URL}/contacts", json=contact_data)
contact_id = create_response.json()["id"]
# Nach Rolle filtern
response = requests.get(f"{BASE_URL}/contacts", params={"role": "teacher"})
assert response.status_code == 200
contacts = response.json()
assert all(c.get("role") == "teacher" for c in contacts)
# Cleanup
requests.delete(f"{BASE_URL}/contacts/{contact_id}")
def test_filter_contacts_by_class(self):
"""Test: Kontakte nach Klasse filtern."""
email = generate_unique_email()
contact_data = {"name": "Class Test", "email": email, "class_name": "12b"}
create_response = requests.post(f"{BASE_URL}/contacts", json=contact_data)
contact_id = create_response.json()["id"]
# Nach Klasse filtern
response = requests.get(f"{BASE_URL}/contacts", params={"class_name": "12b"})
assert response.status_code == 200
contacts = response.json()
assert all(c.get("class_name") == "12b" for c in contacts)
# Cleanup
requests.delete(f"{BASE_URL}/contacts/{contact_id}")
def test_search_contacts(self):
"""Test: Kontakte durchsuchen."""
unique_name = f"Suchtest_{uuid.uuid4().hex[:8]}"
email = generate_unique_email()
contact_data = {"name": unique_name, "email": email}
create_response = requests.post(f"{BASE_URL}/contacts", json=contact_data)
contact_id = create_response.json()["id"]
# Suchen
response = requests.get(f"{BASE_URL}/contacts", params={"search": unique_name})
assert response.status_code == 200
contacts = response.json()
assert len(contacts) >= 1
assert any(c["name"] == unique_name for c in contacts)
# Cleanup
requests.delete(f"{BASE_URL}/contacts/{contact_id}")
def test_create_contact_with_tags(self):
"""Test: Kontakt mit Tags erstellen."""
contact_data = {
"name": "Tags Test",
"email": generate_unique_email(),
"tags": ["Elternbeirat", "Foerderverein"]
}
response = requests.post(f"{BASE_URL}/contacts", json=contact_data)
assert response.status_code == 200
assert "Elternbeirat" in response.json()["tags"]
# Cleanup
requests.delete(f"{BASE_URL}/contacts/{response.json()['id']}")
def test_duplicate_email_rejected(self):
"""Test: Doppelte Email-Adresse wird abgelehnt."""
email = generate_unique_email()
contact_data = {"name": "First Contact", "email": email}
response1 = requests.post(f"{BASE_URL}/contacts", json=contact_data)
assert response1.status_code == 200
contact_id = response1.json()["id"]
# Zweiter Kontakt mit gleicher Email
contact_data2 = {"name": "Second Contact", "email": email}
response2 = requests.post(f"{BASE_URL}/contacts", json=contact_data2)
assert response2.status_code == 400
# Cleanup
requests.delete(f"{BASE_URL}/contacts/{contact_id}")
class TestMessengerConversations:
"""Tests fuer Konversations-Endpoints."""
@pytest.fixture(autouse=True)
def check_backend(self):
skip_if_backend_unavailable()
def test_get_conversations(self):
"""Test: Konversationsliste abrufen."""
response = requests.get(f"{BASE_URL}/conversations")
assert response.status_code == 200
assert isinstance(response.json(), list)
def test_create_conversation_with_contact(self):
"""Test: Konversation mit Kontakt erstellen."""
# Kontakt erstellen
contact_data = {"name": "Conv Test Contact", "email": generate_unique_email()}
contact_response = requests.post(f"{BASE_URL}/contacts", json=contact_data)
contact_id = contact_response.json()["id"]
# Konversation erstellen
response = requests.post(f"{BASE_URL}/conversations", params={"contact_id": contact_id})
assert response.status_code == 200
conv = response.json()
assert conv["name"] == "Conv Test Contact"
assert contact_id in conv["participant_ids"]
# Cleanup
requests.delete(f"{BASE_URL}/conversations/{conv['id']}")
requests.delete(f"{BASE_URL}/contacts/{contact_id}")
def test_create_conversation_returns_existing(self):
"""Test: Erneutes Erstellen gibt bestehende Konversation zurueck."""
# Kontakt erstellen
contact_data = {"name": "Existing Conv Test", "email": generate_unique_email()}
contact_response = requests.post(f"{BASE_URL}/contacts", json=contact_data)
contact_id = contact_response.json()["id"]
# Erste Konversation
response1 = requests.post(f"{BASE_URL}/conversations", params={"contact_id": contact_id})
conv_id1 = response1.json()["id"]
# Zweite Anfrage sollte gleiche Konversation zurueckgeben
response2 = requests.post(f"{BASE_URL}/conversations", params={"contact_id": contact_id})
conv_id2 = response2.json()["id"]
assert conv_id1 == conv_id2
# Cleanup
requests.delete(f"{BASE_URL}/conversations/{conv_id1}")
requests.delete(f"{BASE_URL}/contacts/{contact_id}")
def test_create_conversation_without_params_fails(self):
"""Test: Konversation ohne Parameter gibt Fehler."""
response = requests.post(f"{BASE_URL}/conversations")
assert response.status_code == 400
def test_delete_conversation(self):
"""Test: Konversation loeschen."""
# Kontakt und Konversation erstellen
contact_data = {"name": "Delete Conv Test", "email": generate_unique_email()}
contact_response = requests.post(f"{BASE_URL}/contacts", json=contact_data)
contact_id = contact_response.json()["id"]
conv_response = requests.post(f"{BASE_URL}/conversations", params={"contact_id": contact_id})
conv_id = conv_response.json()["id"]
# Loeschen
delete_response = requests.delete(f"{BASE_URL}/conversations/{conv_id}")
assert delete_response.status_code == 200
# Cleanup
requests.delete(f"{BASE_URL}/contacts/{contact_id}")
class TestMessengerMessages:
"""Tests fuer Nachrichten-Endpoints."""
@pytest.fixture(autouse=True)
def check_backend(self):
skip_if_backend_unavailable()
def test_send_and_get_messages(self):
"""Test: Nachricht senden und abrufen."""
# Kontakt und Konversation erstellen
contact_data = {"name": "Message Test", "email": generate_unique_email()}
contact_response = requests.post(f"{BASE_URL}/contacts", json=contact_data)
contact_id = contact_response.json()["id"]
conv_response = requests.post(f"{BASE_URL}/conversations", params={"contact_id": contact_id})
conv_id = conv_response.json()["id"]
# Nachricht senden
msg_data = {"content": "Test Nachricht"}
send_response = requests.post(f"{BASE_URL}/conversations/{conv_id}/messages", json=msg_data)
assert send_response.status_code == 200
msg = send_response.json()
assert msg["content"] == "Test Nachricht"
assert msg["sender_id"] == "self"
# Nachrichten abrufen
get_response = requests.get(f"{BASE_URL}/conversations/{conv_id}/messages")
assert get_response.status_code == 200
messages = get_response.json()
assert len(messages) >= 1
# Cleanup
requests.delete(f"{BASE_URL}/conversations/{conv_id}")
requests.delete(f"{BASE_URL}/contacts/{contact_id}")
def test_message_updates_conversation(self):
"""Test: Nachricht aktualisiert Konversation."""
# Setup
contact_data = {"name": "Update Conv Test", "email": generate_unique_email()}
contact_response = requests.post(f"{BASE_URL}/contacts", json=contact_data)
contact_id = contact_response.json()["id"]
conv_response = requests.post(f"{BASE_URL}/conversations", params={"contact_id": contact_id})
conv_id = conv_response.json()["id"]
# Nachricht senden
msg_data = {"content": "Aktualisiert letzte Nachricht"}
requests.post(f"{BASE_URL}/conversations/{conv_id}/messages", json=msg_data)
# Konversation pruefen
conv = requests.get(f"{BASE_URL}/conversations/{conv_id}").json()
assert "Aktualisiert" in conv.get("last_message", "")
# Cleanup
requests.delete(f"{BASE_URL}/conversations/{conv_id}")
requests.delete(f"{BASE_URL}/contacts/{contact_id}")
def test_mark_message_as_read(self):
"""Test: Nachricht als gelesen markieren."""
# Setup
contact_data = {"name": "Read Test", "email": generate_unique_email()}
contact_response = requests.post(f"{BASE_URL}/contacts", json=contact_data)
contact_id = contact_response.json()["id"]
conv_response = requests.post(f"{BASE_URL}/conversations", params={"contact_id": contact_id})
conv_id = conv_response.json()["id"]
msg_response = requests.post(
f"{BASE_URL}/conversations/{conv_id}/messages",
json={"content": "Read me"}
)
msg_id = msg_response.json()["id"]
# Als gelesen markieren
read_response = requests.put(f"{BASE_URL}/messages/{msg_id}/read")
assert read_response.status_code == 200
assert read_response.json()["status"] == "read"
# Cleanup
requests.delete(f"{BASE_URL}/conversations/{conv_id}")
requests.delete(f"{BASE_URL}/contacts/{contact_id}")
def test_mark_all_messages_read(self):
"""Test: Alle Nachrichten einer Konversation als gelesen markieren."""
# Setup
contact_data = {"name": "Read All Test", "email": generate_unique_email()}
contact_response = requests.post(f"{BASE_URL}/contacts", json=contact_data)
contact_id = contact_response.json()["id"]
conv_response = requests.post(f"{BASE_URL}/conversations", params={"contact_id": contact_id})
conv_id = conv_response.json()["id"]
# Mehrere Nachrichten senden
requests.post(f"{BASE_URL}/conversations/{conv_id}/messages", json={"content": "Msg 1"})
requests.post(f"{BASE_URL}/conversations/{conv_id}/messages", json={"content": "Msg 2"})
# Alle als gelesen markieren
response = requests.put(f"{BASE_URL}/conversations/{conv_id}/read-all")
assert response.status_code == 200
assert response.json()["status"] == "all_read"
# Cleanup
requests.delete(f"{BASE_URL}/conversations/{conv_id}")
requests.delete(f"{BASE_URL}/contacts/{contact_id}")
def test_messages_pagination(self):
"""Test: Nachrichten mit Limit abrufen."""
# Setup
contact_data = {"name": "Pagination Test", "email": generate_unique_email()}
contact_response = requests.post(f"{BASE_URL}/contacts", json=contact_data)
contact_id = contact_response.json()["id"]
conv_response = requests.post(f"{BASE_URL}/conversations", params={"contact_id": contact_id})
conv_id = conv_response.json()["id"]
# Mehrere Nachrichten senden
for i in range(5):
requests.post(f"{BASE_URL}/conversations/{conv_id}/messages", json={"content": f"Msg {i}"})
# Mit Limit abrufen
response = requests.get(f"{BASE_URL}/conversations/{conv_id}/messages", params={"limit": 3})
assert response.status_code == 200
assert len(response.json()) <= 3
# Cleanup
requests.delete(f"{BASE_URL}/conversations/{conv_id}")
requests.delete(f"{BASE_URL}/contacts/{contact_id}")
class TestMessengerTemplates:
"""Tests fuer Vorlagen-Endpoints."""
@pytest.fixture(autouse=True)
def check_backend(self):
skip_if_backend_unavailable()
def test_get_templates(self):
"""Test: Vorlagenliste abrufen."""
response = requests.get(f"{BASE_URL}/templates")
assert response.status_code == 200
templates = response.json()
assert isinstance(templates, list)
# Standard-Vorlagen sollten vorhanden sein
assert len(templates) >= 1
def test_create_and_delete_template(self):
"""Test: Vorlage erstellen und loeschen."""
# Erstellen (Query-Parameter)
response = requests.post(
f"{BASE_URL}/templates",
params={
"name": "Test Vorlage",
"content": "Test mit [PLATZHALTER]",
"category": "test"
}
)
assert response.status_code == 200
template = response.json()
assert template["name"] == "Test Vorlage"
assert "[PLATZHALTER]" in template["content"]
# Loeschen
delete_response = requests.delete(f"{BASE_URL}/templates/{template['id']}")
assert delete_response.status_code == 200
def test_default_templates_exist(self):
"""Test: Standard-Vorlagen sind vorhanden."""
response = requests.get(f"{BASE_URL}/templates")
templates = response.json()
# Mindestens eine Vorlage sollte existieren
assert len(templates) >= 1
# Jede Vorlage hat die erforderlichen Felder
for template in templates:
assert "id" in template
assert "name" in template
assert "content" in template
assert "category" in template
class TestMessengerGroups:
"""Tests fuer Gruppen-Endpoints."""
@pytest.fixture(autouse=True)
def check_backend(self):
skip_if_backend_unavailable()
def test_get_groups(self):
"""Test: Gruppenliste abrufen."""
response = requests.get(f"{BASE_URL}/groups")
assert response.status_code == 200
assert isinstance(response.json(), list)
def test_create_and_delete_group(self):
"""Test: Gruppe erstellen und loeschen."""
group_data = {
"name": f"Test Klasse {uuid.uuid4().hex[:4]}",
"description": "Test Elterngruppe",
"group_type": "class",
"member_ids": []
}
# Erstellen
response = requests.post(f"{BASE_URL}/groups", json=group_data)
assert response.status_code == 200
group = response.json()
assert "Test Klasse" in group["name"]
assert group["group_type"] == "class"
# Loeschen
delete_response = requests.delete(f"{BASE_URL}/groups/{group['id']}")
assert delete_response.status_code == 200
def test_update_group_members(self):
"""Test: Gruppen-Mitglieder aktualisieren."""
# Kontakte erstellen
contact1 = requests.post(
f"{BASE_URL}/contacts",
json={"name": "Member 1", "email": generate_unique_email()}
).json()
contact2 = requests.post(
f"{BASE_URL}/contacts",
json={"name": "Member 2", "email": generate_unique_email()}
).json()
# Gruppe erstellen
group = requests.post(
f"{BASE_URL}/groups",
json={"name": "Members Test", "description": "Test", "member_ids": []}
).json()
# Mitglieder hinzufuegen
member_ids = [contact1["id"], contact2["id"]]
response = requests.put(
f"{BASE_URL}/groups/{group['id']}/members",
json=member_ids
)
assert response.status_code == 200
assert contact1["id"] in response.json()["member_ids"]
assert contact2["id"] in response.json()["member_ids"]
# Cleanup
requests.delete(f"{BASE_URL}/groups/{group['id']}")
requests.delete(f"{BASE_URL}/contacts/{contact1['id']}")
requests.delete(f"{BASE_URL}/contacts/{contact2['id']}")
def test_create_group_conversation(self):
"""Test: Gruppenkonversation erstellen."""
# Gruppe erstellen
group = requests.post(
f"{BASE_URL}/groups",
json={"name": "Group Conv Test", "description": "Test"}
).json()
# Konversation mit Gruppe erstellen
response = requests.post(
f"{BASE_URL}/conversations",
params={"group_id": group["id"]}
)
assert response.status_code == 200
conv = response.json()
assert conv["is_group"] == True
assert conv["group_id"] == group["id"]
# Cleanup
requests.delete(f"{BASE_URL}/conversations/{conv['id']}")
requests.delete(f"{BASE_URL}/groups/{group['id']}")
class TestMessengerCSV:
"""Tests fuer CSV Import/Export."""
@pytest.fixture(autouse=True)
def check_backend(self):
skip_if_backend_unavailable()
def test_export_csv(self):
"""Test: Kontakte als CSV exportieren."""
response = requests.get(f"{BASE_URL}/contacts/export/csv")
assert response.status_code == 200
assert "text/csv" in response.headers.get("content-type", "")
# CSV-Inhalt pruefen
content = response.text
assert "name" in content.lower()
assert "email" in content.lower()
def test_import_csv_semicolon(self):
"""Test: Kontakte aus CSV mit Semikolon-Trennung importieren."""
unique_email = generate_unique_email()
csv_content = f"Name;Email;Telefon;Schueler;Klasse\nCSV Test Import;{unique_email};0123;CSV Schueler;5b"
files = {"file": ("test.csv", csv_content, "text/csv")}
response = requests.post(f"{BASE_URL}/contacts/import", files=files)
assert response.status_code == 200
result = response.json()
assert "imported" in result
assert result["imported"] >= 1
# Cleanup - importierten Kontakt loeschen
if result.get("contacts"):
for contact in result["contacts"]:
requests.delete(f"{BASE_URL}/contacts/{contact['id']}")
def test_import_csv_german_columns(self):
"""Test: CSV mit deutschen Spaltennamen importieren."""
unique_email = generate_unique_email()
csv_content = f"Name;E-Mail;Telefon;Kind;Klasse;Notizen\nDeutsche Spalten;{unique_email};0123;Kind Name;7c;Test Notizen"
files = {"file": ("german.csv", csv_content, "text/csv")}
response = requests.post(f"{BASE_URL}/contacts/import", files=files)
assert response.status_code == 200
result = response.json()
assert result["imported"] >= 1
# Cleanup
if result.get("contacts"):
for contact in result["contacts"]:
requests.delete(f"{BASE_URL}/contacts/{contact['id']}")
def test_import_csv_skip_duplicates(self):
"""Test: CSV Import ueberspringt doppelte Emails."""
# Ersten Kontakt erstellen
email = generate_unique_email()
requests.post(f"{BASE_URL}/contacts", json={"name": "Original", "email": email})
# CSV mit gleicher Email importieren
csv_content = f"Name;Email\nDuplikat;{email}"
files = {"file": ("dup.csv", csv_content, "text/csv")}
response = requests.post(f"{BASE_URL}/contacts/import", files=files)
assert response.status_code == 200
result = response.json()
assert result["skipped"] >= 1
assert any("existiert bereits" in err for err in result.get("errors", []))
def test_import_csv_missing_name(self):
"""Test: CSV Import meldet fehlende Namen."""
csv_content = "Name;Email\n;noname@test.com"
files = {"file": ("noname.csv", csv_content, "text/csv")}
response = requests.post(f"{BASE_URL}/contacts/import", files=files)
assert response.status_code == 200
result = response.json()
assert result["skipped"] >= 1
assert any("Name fehlt" in err for err in result.get("errors", []))
def test_import_non_csv_rejected(self):
"""Test: Nicht-CSV Dateien werden abgelehnt."""
files = {"file": ("test.txt", "not a csv", "text/plain")}
response = requests.post(f"{BASE_URL}/contacts/import", files=files)
assert response.status_code == 400
class TestMessengerStats:
"""Tests fuer Statistik-Endpoint."""
@pytest.fixture(autouse=True)
def check_backend(self):
skip_if_backend_unavailable()
def test_get_stats(self):
"""Test: Statistiken abrufen."""
response = requests.get(f"{BASE_URL}/stats")
assert response.status_code == 200
stats = response.json()
assert "total_contacts" in stats
assert "total_groups" in stats
assert "total_conversations" in stats
assert "total_messages" in stats
assert "unread_messages" in stats
assert "contacts_by_role" in stats
def test_stats_reflect_data(self):
"""Test: Statistiken spiegeln Daten wider."""
# Initiale Stats
initial_stats = requests.get(f"{BASE_URL}/stats").json()
initial_contacts = initial_stats["total_contacts"]
# Kontakt hinzufuegen
contact = requests.post(
f"{BASE_URL}/contacts",
json={"name": "Stats Test", "email": generate_unique_email()}
).json()
# Neue Stats
new_stats = requests.get(f"{BASE_URL}/stats").json()
assert new_stats["total_contacts"] == initial_contacts + 1
# Cleanup
requests.delete(f"{BASE_URL}/contacts/{contact['id']}")
class TestMessengerEdgeCases:
"""Tests fuer Randfaelle und Fehlerbehandlung."""
@pytest.fixture(autouse=True)
def check_backend(self):
skip_if_backend_unavailable()
def test_update_nonexistent_contact(self):
"""Test: Update nicht existierendem Kontakt gibt 404."""
response = requests.put(
f"{BASE_URL}/contacts/nonexistent-uuid",
json={"name": "Test"}
)
assert response.status_code == 404
def test_message_to_nonexistent_conversation(self):
"""Test: Nachricht an nicht existierende Konversation gibt 404."""
response = requests.post(
f"{BASE_URL}/conversations/nonexistent-uuid/messages",
json={"content": "Test"}
)
assert response.status_code == 404
def test_create_conversation_nonexistent_contact(self):
"""Test: Konversation mit nicht existierendem Kontakt gibt 404."""
response = requests.post(
f"{BASE_URL}/conversations",
params={"contact_id": "nonexistent-uuid"}
)
assert response.status_code == 404
def test_create_conversation_nonexistent_group(self):
"""Test: Konversation mit nicht existierender Gruppe gibt 404."""
response = requests.post(
f"{BASE_URL}/conversations",
params={"group_id": "nonexistent-uuid"}
)
assert response.status_code == 404
def test_mark_nonexistent_message_read(self):
"""Test: Nicht existierende Nachricht als gelesen markieren gibt 404."""
response = requests.put(f"{BASE_URL}/messages/nonexistent-uuid/read")
assert response.status_code == 404
def test_contact_without_email(self):
"""Test: Kontakt ohne Email erstellen ist erlaubt."""
contact_data = {"name": "Nur Name"}
response = requests.post(f"{BASE_URL}/contacts", json=contact_data)
assert response.status_code == 200
assert response.json()["email"] is None
# Cleanup
requests.delete(f"{BASE_URL}/contacts/{response.json()['id']}")
def test_empty_message_rejected(self):
"""Test: Leere Nachricht wird abgelehnt."""
# Setup
contact = requests.post(
f"{BASE_URL}/contacts",
json={"name": "Empty Msg Test", "email": generate_unique_email()}
).json()
conv = requests.post(
f"{BASE_URL}/conversations",
params={"contact_id": contact["id"]}
).json()
# Leere Nachricht sollte Fehler geben
response = requests.post(
f"{BASE_URL}/conversations/{conv['id']}/messages",
json={"content": ""}
)
assert response.status_code == 422 # Validation error
# Cleanup
requests.delete(f"{BASE_URL}/conversations/{conv['id']}")
requests.delete(f"{BASE_URL}/contacts/{contact['id']}")
class TestMessengerMatrixFeatures:
"""Tests fuer Matrix-ID und bevorzugten Kanal."""
@pytest.fixture(autouse=True)
def check_backend(self):
skip_if_backend_unavailable()
def test_create_contact_with_matrix_id(self):
"""Test: Kontakt mit Matrix-ID erstellen."""
contact_data = {
"name": "Matrix User",
"email": generate_unique_email(),
"matrix_id": "@testuser:matrix.org"
}
response = requests.post(f"{BASE_URL}/contacts", json=contact_data)
assert response.status_code == 200
data = response.json()
assert data["matrix_id"] == "@testuser:matrix.org"
# Cleanup
requests.delete(f"{BASE_URL}/contacts/{data['id']}")
def test_create_contact_with_preferred_channel(self):
"""Test: Kontakt mit bevorzugtem Kanal erstellen."""
contact_data = {
"name": "Channel Test",
"email": generate_unique_email(),
"preferred_channel": "matrix"
}
response = requests.post(f"{BASE_URL}/contacts", json=contact_data)
assert response.status_code == 200
data = response.json()
assert data["preferred_channel"] == "matrix"
# Cleanup
requests.delete(f"{BASE_URL}/contacts/{data['id']}")
def test_update_contact_matrix_id(self):
"""Test: Matrix-ID eines Kontakts aktualisieren."""
# Kontakt erstellen
contact_data = {"name": "Update Matrix Test", "email": generate_unique_email()}
create_response = requests.post(f"{BASE_URL}/contacts", json=contact_data)
contact_id = create_response.json()["id"]
# Matrix-ID hinzufuegen
update_data = {"matrix_id": "@updated:matrix.org"}
response = requests.put(f"{BASE_URL}/contacts/{contact_id}", json=update_data)
assert response.status_code == 200
assert response.json()["matrix_id"] == "@updated:matrix.org"
# Cleanup
requests.delete(f"{BASE_URL}/contacts/{contact_id}")
def test_default_preferred_channel_is_email(self):
"""Test: Standard bevorzugter Kanal ist 'email'."""
contact_data = {"name": "Default Channel Test", "email": generate_unique_email()}
response = requests.post(f"{BASE_URL}/contacts", json=contact_data)
assert response.status_code == 200
data = response.json()
assert data["preferred_channel"] == "email"
# Cleanup
requests.delete(f"{BASE_URL}/contacts/{data['id']}")
def test_csv_export_includes_matrix_fields(self):
"""Test: CSV-Export enthaelt Matrix-ID und bevorzugten Kanal."""
# Kontakt mit Matrix-Feldern erstellen
contact_data = {
"name": "CSV Matrix Test",
"email": generate_unique_email(),
"matrix_id": "@csvtest:matrix.org",
"preferred_channel": "matrix"
}
contact = requests.post(f"{BASE_URL}/contacts", json=contact_data).json()
# CSV exportieren
response = requests.get(f"{BASE_URL}/contacts/export/csv")
assert response.status_code == 200
csv_content = response.text.lower()
assert "matrix" in csv_content
# Cleanup
requests.delete(f"{BASE_URL}/contacts/{contact['id']}")
class TestMessengerEmailFeatures:
"""Tests fuer Email-Versand bei Nachrichten."""
@pytest.fixture(autouse=True)
def check_backend(self):
skip_if_backend_unavailable()
def test_send_message_with_email(self):
"""Test: Nachricht mit Email-Versand senden."""
# Kontakt mit Email erstellen
contact_data = {
"name": "Email Test Contact",
"email": generate_unique_email()
}
contact = requests.post(f"{BASE_URL}/contacts", json=contact_data).json()
# Konversation erstellen
conv = requests.post(
f"{BASE_URL}/conversations",
params={"contact_id": contact["id"]}
).json()
# Nachricht mit send_email=true senden
msg_data = {
"content": "Diese Nachricht wird per Email gesendet.",
"send_email": True
}
response = requests.post(
f"{BASE_URL}/conversations/{conv['id']}/messages",
json=msg_data
)
assert response.status_code == 200
data = response.json()
assert data["content"] == "Diese Nachricht wird per Email gesendet."
# Email-Status wird zurueckgegeben
assert "email_sent" in data
# Cleanup
requests.delete(f"{BASE_URL}/conversations/{conv['id']}")
requests.delete(f"{BASE_URL}/contacts/{contact['id']}")
def test_send_message_without_email(self):
"""Test: Nachricht ohne Email-Versand senden."""
# Kontakt erstellen
contact = requests.post(
f"{BASE_URL}/contacts",
json={"name": "No Email Test", "email": generate_unique_email()}
).json()
# Konversation erstellen
conv = requests.post(
f"{BASE_URL}/conversations",
params={"contact_id": contact["id"]}
).json()
# Nachricht ohne send_email senden (Standard: false)
msg_data = {"content": "Normale Nachricht ohne Email."}
response = requests.post(
f"{BASE_URL}/conversations/{conv['id']}/messages",
json=msg_data
)
assert response.status_code == 200
data = response.json()
# Email sollte nicht gesendet worden sein
assert data.get("email_sent", False) == False
# Cleanup
requests.delete(f"{BASE_URL}/conversations/{conv['id']}")
requests.delete(f"{BASE_URL}/contacts/{contact['id']}")
def test_send_message_email_to_contact_without_email_address(self):
"""Test: Email-Versand an Kontakt ohne Email-Adresse."""
# Kontakt ohne Email erstellen
contact = requests.post(
f"{BASE_URL}/contacts",
json={"name": "No Email Address"}
).json()
# Konversation erstellen
conv = requests.post(
f"{BASE_URL}/conversations",
params={"contact_id": contact["id"]}
).json()
# Nachricht mit send_email=true senden
msg_data = {
"content": "Versuch Email zu senden ohne Adresse.",
"send_email": True
}
response = requests.post(
f"{BASE_URL}/conversations/{conv['id']}/messages",
json=msg_data
)
assert response.status_code == 200
# Nachricht sollte gespeichert werden, aber Email schlaegt fehl
data = response.json()
assert data["email_sent"] == False
# Cleanup
requests.delete(f"{BASE_URL}/conversations/{conv['id']}")
requests.delete(f"{BASE_URL}/contacts/{contact['id']}")