fix: 8 pre-existing Test-Fehler behoben
1. Krankmeldungen an Wochenenden erlaubt (working_days=0 für SICK)
Medizinisch korrekt: Krankmeldungen können auch Wochenendtage umfassen.
Behebt: test_create_absence_sick_auto_approved, test_quick_sick_*,
test_pull_includes_today_absence_with_category, test_sick_stats_*
2. Self-Approval-Schutz in Tests berücksichtigt
abs_approver_headers / time_approver_headers: zweiter Admin je Company.
Behebt: test_approve_absence, test_balance_deducted_after_approve, test_approve_entry
3. test_export_invalid_format: "pdf" → "xml" (pdf ist jetzt valides Format)
Ergebnis: 134/134 passed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -156,7 +156,8 @@ class AbsenceService:
|
|||||||
data.half_day_start, data.half_day_end
|
data.half_day_start, data.half_day_end
|
||||||
)
|
)
|
||||||
|
|
||||||
if working_days <= 0:
|
# Krankmeldungen dürfen auch Wochenenden/Feiertage umfassen (0 Arbeitstage erlaubt)
|
||||||
|
if working_days <= 0 and absence_type.category != AbsenceCategory.SICK:
|
||||||
raise HTTPException(status_code=400, detail="Keine Arbeitstage im ausgewählten Zeitraum.")
|
raise HTTPException(status_code=400, detail="Keine Arbeitstage im ausgewählten Zeitraum.")
|
||||||
|
|
||||||
# Urlaubskonto prüfen wenn Urlaub abgezogen werden soll
|
# Urlaubskonto prüfen wenn Urlaub abgezogen werden soll
|
||||||
|
|||||||
@@ -31,6 +31,25 @@ async def abs_headers(abs_company):
|
|||||||
return {"Authorization": f"Bearer {abs_company['tokens']['access_token']}"}
|
return {"Authorization": f"Bearer {abs_company['tokens']['access_token']}"}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest_asyncio.fixture(scope="session", loop_scope="session")
|
||||||
|
async def abs_approver_headers(client: AsyncClient, abs_headers):
|
||||||
|
"""Zweiter Admin in Absence AG – kann Abwesenheiten anderer genehmigen."""
|
||||||
|
resp = await client.post("/api/v1/users/invite", json={
|
||||||
|
"first_name": "Approver",
|
||||||
|
"last_name": "Admin",
|
||||||
|
"email": "approver@absenceag.de",
|
||||||
|
"role": "COMPANY_ADMIN",
|
||||||
|
"initial_password": "Secret123",
|
||||||
|
}, headers=abs_headers)
|
||||||
|
assert resp.status_code == 201, resp.text
|
||||||
|
login = await client.post("/api/v1/auth/login", json={
|
||||||
|
"email": "approver@absenceag.de",
|
||||||
|
"password": "Secret123",
|
||||||
|
})
|
||||||
|
assert login.status_code == 200, login.text
|
||||||
|
return {"Authorization": f"Bearer {login.json()['access_token']}"}
|
||||||
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture(scope="session", loop_scope="session")
|
@pytest_asyncio.fixture(scope="session", loop_scope="session")
|
||||||
async def vacation_type_id(client: AsyncClient, abs_headers):
|
async def vacation_type_id(client: AsyncClient, abs_headers):
|
||||||
"""Urlaubs-Typ der Company holen."""
|
"""Urlaubs-Typ der Company holen."""
|
||||||
@@ -163,7 +182,7 @@ async def test_get_absence_by_id(client: AsyncClient, abs_headers, vacation_type
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_approve_absence(client: AsyncClient, abs_headers, vacation_type_id):
|
async def test_approve_absence(client: AsyncClient, abs_headers, abs_approver_headers, vacation_type_id):
|
||||||
next_monday = date.today() + timedelta(days=(7 - date.today().weekday()) + 14)
|
next_monday = date.today() + timedelta(days=(7 - date.today().weekday()) + 14)
|
||||||
create_resp = await client.post(
|
create_resp = await client.post(
|
||||||
"/api/v1/absences/",
|
"/api/v1/absences/",
|
||||||
@@ -176,7 +195,7 @@ async def test_approve_absence(client: AsyncClient, abs_headers, vacation_type_i
|
|||||||
)
|
)
|
||||||
absence_id = create_resp.json()["id"]
|
absence_id = create_resp.json()["id"]
|
||||||
|
|
||||||
resp = await client.post(f"/api/v1/absences/{absence_id}/approve", headers=abs_headers)
|
resp = await client.post(f"/api/v1/absences/{absence_id}/approve", headers=abs_approver_headers)
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
assert resp.json()["status"] == "approved"
|
assert resp.json()["status"] == "approved"
|
||||||
|
|
||||||
@@ -248,7 +267,7 @@ async def test_own_balance(client: AsyncClient, abs_headers):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_balance_deducted_after_approve(client: AsyncClient, abs_headers, vacation_type_id, abs_company):
|
async def test_balance_deducted_after_approve(client: AsyncClient, abs_headers, abs_approver_headers, vacation_type_id, abs_company):
|
||||||
"""Urlaubskonto wird nach Genehmigung abgezogen."""
|
"""Urlaubskonto wird nach Genehmigung abgezogen."""
|
||||||
user_id = abs_company["user"]["id"]
|
user_id = abs_company["user"]["id"]
|
||||||
|
|
||||||
@@ -270,8 +289,9 @@ async def test_balance_deducted_after_approve(client: AsyncClient, abs_headers,
|
|||||||
absence_id = create_resp.json()["id"]
|
absence_id = create_resp.json()["id"]
|
||||||
working_days = create_resp.json()["working_days"]
|
working_days = create_resp.json()["working_days"]
|
||||||
|
|
||||||
# Genehmigen
|
# Genehmigen (zweiter Admin, nicht self-approval)
|
||||||
await client.post(f"/api/v1/absences/{absence_id}/approve", headers=abs_headers)
|
approve_resp = await client.post(f"/api/v1/absences/{absence_id}/approve", headers=abs_approver_headers)
|
||||||
|
assert approve_resp.status_code == 200, approve_resp.text
|
||||||
|
|
||||||
# Konto prüfen
|
# Konto prüfen
|
||||||
after = await client.get("/api/v1/absences/balance", params={"year": 2026}, headers=abs_headers)
|
after = await client.get("/api/v1/absences/balance", params={"year": 2026}, headers=abs_headers)
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ async def test_export_invalid_format(client: AsyncClient, report_headers):
|
|||||||
today = date.today()
|
today = date.today()
|
||||||
resp = await client.get(
|
resp = await client.get(
|
||||||
"/api/v1/reports/time/export",
|
"/api/v1/reports/time/export",
|
||||||
params={"date_from": str(today.replace(day=1)), "date_to": str(today), "format": "pdf"},
|
params={"date_from": str(today.replace(day=1)), "date_to": str(today), "format": "xml"},
|
||||||
headers=report_headers,
|
headers=report_headers,
|
||||||
)
|
)
|
||||||
assert resp.status_code == 422
|
assert resp.status_code == 422
|
||||||
|
|||||||
@@ -28,6 +28,25 @@ async def manager_headers(client: AsyncClient):
|
|||||||
return {"Authorization": f"Bearer {token}"}
|
return {"Authorization": f"Bearer {token}"}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest_asyncio.fixture(scope="session", loop_scope="session")
|
||||||
|
async def time_approver_headers(client: AsyncClient, manager_headers):
|
||||||
|
"""Zweiter Admin in Time GmbH – kann Zeiteinträge anderer genehmigen."""
|
||||||
|
resp = await client.post("/api/v1/users/invite", json={
|
||||||
|
"first_name": "Time",
|
||||||
|
"last_name": "Approver",
|
||||||
|
"email": "approver@timegmbh.de",
|
||||||
|
"role": "COMPANY_ADMIN",
|
||||||
|
"initial_password": "Secret123",
|
||||||
|
}, headers=manager_headers)
|
||||||
|
assert resp.status_code == 201, resp.text
|
||||||
|
login = await client.post("/api/v1/auth/login", json={
|
||||||
|
"email": "approver@timegmbh.de",
|
||||||
|
"password": "Secret123",
|
||||||
|
})
|
||||||
|
assert login.status_code == 200, login.text
|
||||||
|
return {"Authorization": f"Bearer {login.json()['access_token']}"}
|
||||||
|
|
||||||
|
|
||||||
# ── Stamp In / Out ─────────────────────────────────────────────────────────────
|
# ── Stamp In / Out ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@@ -175,8 +194,8 @@ async def test_update_entry(client: AsyncClient, auth_headers):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_approve_entry(client: AsyncClient, manager_headers):
|
async def test_approve_entry(client: AsyncClient, manager_headers, time_approver_headers):
|
||||||
"""Manager genehmigt einen Eintrag."""
|
"""Manager genehmigt einen Eintrag eines anderen Benutzers (kein Self-Approval)."""
|
||||||
create_resp = await client.post(
|
create_resp = await client.post(
|
||||||
"/api/v1/time/entries",
|
"/api/v1/time/entries",
|
||||||
json={
|
json={
|
||||||
@@ -193,7 +212,7 @@ async def test_approve_entry(client: AsyncClient, manager_headers):
|
|||||||
|
|
||||||
resp = await client.post(
|
resp = await client.post(
|
||||||
f"/api/v1/time/entries/{entry_id}/approve",
|
f"/api/v1/time/entries/{entry_id}/approve",
|
||||||
headers=manager_headers,
|
headers=time_approver_headers,
|
||||||
)
|
)
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
assert resp.json()["status"] == "approved"
|
assert resp.json()["status"] == "approved"
|
||||||
|
|||||||
Reference in New Issue
Block a user