feat: T03+T04 backend - FastAPI, DB models, Rentman integration, admin auth, APScheduler

This commit is contained in:
Implementation Engineer
2026-07-09 01:36:46 +02:00
parent 3bfa54b4b3
commit 88cff68f73
78 changed files with 2174 additions and 0 deletions
+120
View File
@@ -0,0 +1,120 @@
"""Tests for Rentman request submission pipeline (T04)."""
import pytest
from unittest.mock import AsyncMock, patch, MagicMock
from app.services.rentman_service import RentmanService
def test_build_project_request_payload():
data = {
"event_name": "Sommerfest 2026",
"date_start": "2026-08-15",
"date_end": "2026-08-16",
"contact_name": "Max Mustermann",
"contact_company": "Firma GmbH",
"contact_email": "max@example.com",
"contact_phone": "+49 170 1234567",
"contact_street": "Hauptstr. 42a",
"contact_postalcode": "80000",
"contact_city": "Muenchen",
"location": "Muenchen",
"message": "Brauchen PA",
}
payload = RentmanService.build_project_request_payload(data)
assert payload["name"] == "Sommerfest 2026"
assert payload["planperiod_start"] == "2026-08-15T08:00:00+02:00"
assert payload["planperiod_end"] == "2026-08-16T02:00:00+02:00"
assert payload["usageperiod_start"] == "2026-08-15T18:00:00+02:00"
assert payload["usageperiod_end"] == "2026-08-16T23:59:00+02:00"
assert payload["contact_name"] == "Firma GmbH"
assert payload["contact_person_first_name"] == "Max"
assert payload["contact_person_lastname"] == "Mustermann"
assert payload["contact_person_email"] == "max@example.com"
assert payload["location_mailing_street"] == "Hauptstr. 42a"
assert payload["location_mailing_number"] == "42a"
assert payload["location_mailing_postalcode"] == "80000"
assert payload["location_mailing_city"] == "Muenchen"
assert payload["remark"] == "Brauchen PA"
def test_build_project_request_payload_no_company():
"""When no company, contact_name should use contact_name."""
data = {
"event_name": "Test",
"date_start": "2026-08-15",
"date_end": "2026-08-16",
"contact_name": "Anna Schmidt",
"contact_company": None,
"contact_email": "anna@example.com",
"contact_street": "Testweg 5",
"contact_postalcode": "10000",
"contact_city": "Berlin",
"location": "Berlin",
}
payload = RentmanService.build_project_request_payload(data)
assert payload["contact_name"] == "Anna Schmidt"
assert payload["contact_person_first_name"] == "Anna"
assert payload["contact_person_lastname"] == "Schmidt"
assert payload["location_mailing_number"] == "5"
def test_build_equipment_payload():
item = {
"equipment_name": "L-Acoustics K2",
"rentman_equipment_id": "101",
"quantity": 4,
"unit_price": 150.00,
}
payload = RentmanService.build_equipment_payload(item)
assert payload["name"] == "L-Acoustics K2"
assert payload["quantity"] == 4
assert payload["quantity_total"] == 4
assert payload["unit_price"] == 150.00
assert payload["linked_equipment"] == "/equipment/101"
@pytest.mark.asyncio
async def test_projectrequest_success():
"""Mock Rentman API: create project request returns ID, equipment added."""
svc = RentmanService(token="test-token")
svc.create_project_request = AsyncMock(return_value={"id": "9999", "name": "Test Event"})
svc.add_equipment_to_request = AsyncMock(return_value={"id": "equip-1"})
project_payload = {"name": "Test Event", "usageperiod_start": "2026-08-15T18:00:00+02:00"}
result = await svc.create_project_request(project_payload)
assert result["id"] == "9999"
equip_payload = {"name": "K2", "quantity": 2}
eq_result = await svc.add_equipment_to_request("9999", equip_payload)
assert eq_result["id"] == "equip-1"
@pytest.mark.asyncio
async def test_equipment_retry():
"""Failed equipment POST should be retried (exponential backoff simulation)."""
svc = RentmanService(token="test-token")
call_count = 0
async def mock_add(*args, **kwargs):
nonlocal call_count
call_count += 1
if call_count < 3:
raise Exception("Temporary failure")
return {"id": "success"}
svc.add_equipment_to_request = mock_add
# Simulate retry logic
max_retries = 3
result = None
for attempt in range(max_retries):
try:
result = await svc.add_equipment_to_request("1", {"name": "test"})
break
except Exception:
if attempt == max_retries - 1:
raise
import asyncio
await asyncio.sleep(0.01 * (2 ** attempt))
assert result == {"id": "success"}
assert call_count == 3