Add ruff linter/formatter for Python
- Add ruff as dev dependency - Configure ruff in pyproject.toml with strict 88-char line limit - Ignore B008 (FastAPI Depends pattern is standard) - Allow longer lines in tests for readability - Fix all lint issues in source files - Add Makefile targets: lint-backend, format-backend, fix-backend
This commit is contained in:
parent
69bc8413e0
commit
6c218130e9
31 changed files with 1234 additions and 876 deletions
|
|
@ -3,7 +3,9 @@ Availability API Tests
|
|||
|
||||
Tests for the admin availability management endpoints.
|
||||
"""
|
||||
from datetime import date, time, timedelta
|
||||
|
||||
from datetime import date, timedelta
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
|
|
@ -19,6 +21,7 @@ def in_days(n: int) -> date:
|
|||
# Permission Tests
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class TestAvailabilityPermissions:
|
||||
"""Test that only admins can access availability endpoints."""
|
||||
|
||||
|
|
@ -44,7 +47,9 @@ class TestAvailabilityPermissions:
|
|||
assert response.status_code == 200
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_regular_user_cannot_get_availability(self, client_factory, regular_user):
|
||||
async def test_regular_user_cannot_get_availability(
|
||||
self, client_factory, regular_user
|
||||
):
|
||||
async with client_factory.create(cookies=regular_user["cookies"]) as client:
|
||||
response = await client.get(
|
||||
"/api/admin/availability",
|
||||
|
|
@ -53,7 +58,9 @@ class TestAvailabilityPermissions:
|
|||
assert response.status_code == 403
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_regular_user_cannot_set_availability(self, client_factory, regular_user):
|
||||
async def test_regular_user_cannot_set_availability(
|
||||
self, client_factory, regular_user
|
||||
):
|
||||
async with client_factory.create(cookies=regular_user["cookies"]) as client:
|
||||
response = await client.put(
|
||||
"/api/admin/availability",
|
||||
|
|
@ -88,6 +95,7 @@ class TestAvailabilityPermissions:
|
|||
# Set Availability Tests
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class TestSetAvailability:
|
||||
"""Test setting availability for a date."""
|
||||
|
||||
|
|
@ -101,7 +109,7 @@ class TestSetAvailability:
|
|||
"slots": [{"start_time": "09:00:00", "end_time": "12:00:00"}],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["date"] == str(tomorrow())
|
||||
|
|
@ -122,13 +130,15 @@ class TestSetAvailability:
|
|||
],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert len(data["slots"]) == 2
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_set_empty_slots_clears_availability(self, client_factory, admin_user):
|
||||
async def test_set_empty_slots_clears_availability(
|
||||
self, client_factory, admin_user
|
||||
):
|
||||
async with client_factory.create(cookies=admin_user["cookies"]) as client:
|
||||
# First set some availability
|
||||
await client.put(
|
||||
|
|
@ -138,13 +148,13 @@ class TestSetAvailability:
|
|||
"slots": [{"start_time": "09:00:00", "end_time": "12:00:00"}],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# Then clear it
|
||||
response = await client.put(
|
||||
"/api/admin/availability",
|
||||
json={"date": str(tomorrow()), "slots": []},
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert len(data["slots"]) == 0
|
||||
|
|
@ -160,22 +170,22 @@ class TestSetAvailability:
|
|||
"slots": [{"start_time": "09:00:00", "end_time": "12:00:00"}],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# Replace with different slots
|
||||
response = await client.put(
|
||||
await client.put(
|
||||
"/api/admin/availability",
|
||||
json={
|
||||
"date": str(tomorrow()),
|
||||
"slots": [{"start_time": "14:00:00", "end_time": "16:00:00"}],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# Verify the replacement
|
||||
get_response = await client.get(
|
||||
"/api/admin/availability",
|
||||
params={"from": str(tomorrow()), "to": str(tomorrow())},
|
||||
)
|
||||
|
||||
|
||||
data = get_response.json()
|
||||
assert len(data["days"]) == 1
|
||||
assert len(data["days"][0]["slots"]) == 1
|
||||
|
|
@ -186,6 +196,7 @@ class TestSetAvailability:
|
|||
# Validation Tests
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class TestAvailabilityValidation:
|
||||
"""Test validation rules for availability."""
|
||||
|
||||
|
|
@ -200,7 +211,7 @@ class TestAvailabilityValidation:
|
|||
"slots": [{"start_time": "09:00:00", "end_time": "12:00:00"}],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == 400
|
||||
assert "past" in response.json()["detail"].lower()
|
||||
|
||||
|
|
@ -214,7 +225,7 @@ class TestAvailabilityValidation:
|
|||
"slots": [{"start_time": "09:00:00", "end_time": "12:00:00"}],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == 400
|
||||
assert "past" in response.json()["detail"].lower()
|
||||
|
||||
|
|
@ -229,7 +240,7 @@ class TestAvailabilityValidation:
|
|||
"slots": [{"start_time": "09:00:00", "end_time": "12:00:00"}],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == 400
|
||||
assert "30" in response.json()["detail"]
|
||||
|
||||
|
|
@ -243,7 +254,7 @@ class TestAvailabilityValidation:
|
|||
"slots": [{"start_time": "09:05:00", "end_time": "12:00:00"}],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == 422 # Pydantic validation error
|
||||
assert "15-minute" in response.json()["detail"][0]["msg"]
|
||||
|
||||
|
|
@ -257,7 +268,7 @@ class TestAvailabilityValidation:
|
|||
"slots": [{"start_time": "12:00:00", "end_time": "09:00:00"}],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == 400
|
||||
assert "after" in response.json()["detail"].lower()
|
||||
|
||||
|
|
@ -274,7 +285,7 @@ class TestAvailabilityValidation:
|
|||
],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == 400
|
||||
assert "overlap" in response.json()["detail"].lower()
|
||||
|
||||
|
|
@ -283,6 +294,7 @@ class TestAvailabilityValidation:
|
|||
# Get Availability Tests
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class TestGetAvailability:
|
||||
"""Test retrieving availability."""
|
||||
|
||||
|
|
@ -293,7 +305,7 @@ class TestGetAvailability:
|
|||
"/api/admin/availability",
|
||||
params={"from": str(tomorrow()), "to": str(in_days(7))},
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["days"] == []
|
||||
|
|
@ -310,13 +322,13 @@ class TestGetAvailability:
|
|||
"slots": [{"start_time": "09:00:00", "end_time": "12:00:00"}],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# Get range that includes all
|
||||
response = await client.get(
|
||||
"/api/admin/availability",
|
||||
params={"from": str(in_days(1)), "to": str(in_days(3))},
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert len(data["days"]) == 3
|
||||
|
|
@ -333,13 +345,13 @@ class TestGetAvailability:
|
|||
"slots": [{"start_time": "09:00:00", "end_time": "12:00:00"}],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# Get only a subset
|
||||
response = await client.get(
|
||||
"/api/admin/availability",
|
||||
params={"from": str(in_days(2)), "to": str(in_days(4))},
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert len(data["days"]) == 3
|
||||
|
|
@ -351,7 +363,7 @@ class TestGetAvailability:
|
|||
"/api/admin/availability",
|
||||
params={"from": str(in_days(7)), "to": str(in_days(1))},
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == 400
|
||||
assert "before" in response.json()["detail"].lower()
|
||||
|
||||
|
|
@ -360,6 +372,7 @@ class TestGetAvailability:
|
|||
# Copy Availability Tests
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class TestCopyAvailability:
|
||||
"""Test copying availability from one day to others."""
|
||||
|
||||
|
|
@ -377,7 +390,7 @@ class TestCopyAvailability:
|
|||
],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# Copy to another day
|
||||
response = await client.post(
|
||||
"/api/admin/availability/copy",
|
||||
|
|
@ -386,7 +399,7 @@ class TestCopyAvailability:
|
|||
"target_dates": [str(in_days(2))],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert len(data["days"]) == 1
|
||||
|
|
@ -404,7 +417,7 @@ class TestCopyAvailability:
|
|||
"slots": [{"start_time": "10:00:00", "end_time": "11:00:00"}],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# Copy to multiple days
|
||||
response = await client.post(
|
||||
"/api/admin/availability/copy",
|
||||
|
|
@ -413,7 +426,7 @@ class TestCopyAvailability:
|
|||
"target_dates": [str(in_days(2)), str(in_days(3)), str(in_days(4))],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert len(data["days"]) == 3
|
||||
|
|
@ -429,7 +442,7 @@ class TestCopyAvailability:
|
|||
"slots": [{"start_time": "08:00:00", "end_time": "09:00:00"}],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# Set source availability
|
||||
await client.put(
|
||||
"/api/admin/availability",
|
||||
|
|
@ -438,7 +451,7 @@ class TestCopyAvailability:
|
|||
"slots": [{"start_time": "14:00:00", "end_time": "15:00:00"}],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# Copy (should replace)
|
||||
await client.post(
|
||||
"/api/admin/availability/copy",
|
||||
|
|
@ -447,13 +460,13 @@ class TestCopyAvailability:
|
|||
"target_dates": [str(in_days(2))],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# Verify target was replaced
|
||||
response = await client.get(
|
||||
"/api/admin/availability",
|
||||
params={"from": str(in_days(2)), "to": str(in_days(2))},
|
||||
)
|
||||
|
||||
|
||||
data = response.json()
|
||||
assert len(data["days"]) == 1
|
||||
assert len(data["days"][0]["slots"]) == 1
|
||||
|
|
@ -469,7 +482,7 @@ class TestCopyAvailability:
|
|||
"target_dates": [str(in_days(2))],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == 400
|
||||
assert "no availability" in response.json()["detail"].lower()
|
||||
|
||||
|
|
@ -484,7 +497,7 @@ class TestCopyAvailability:
|
|||
"slots": [{"start_time": "09:00:00", "end_time": "10:00:00"}],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# Copy including self in targets
|
||||
response = await client.post(
|
||||
"/api/admin/availability/copy",
|
||||
|
|
@ -493,7 +506,7 @@ class TestCopyAvailability:
|
|||
"target_dates": [str(in_days(1)), str(in_days(2))],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
# Should only have copied to day 2, not day 1 (self)
|
||||
|
|
@ -511,7 +524,7 @@ class TestCopyAvailability:
|
|||
"slots": [{"start_time": "09:00:00", "end_time": "10:00:00"}],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# Try to copy to a date beyond 30 days
|
||||
response = await client.post(
|
||||
"/api/admin/availability/copy",
|
||||
|
|
@ -520,7 +533,7 @@ class TestCopyAvailability:
|
|||
"target_dates": [str(in_days(31))],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == 400
|
||||
assert "30" in response.json()["detail"]
|
||||
|
||||
|
|
@ -535,6 +548,6 @@ class TestCopyAvailability:
|
|||
"target_dates": [str(in_days(1))],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == 400
|
||||
assert "past" in response.json()["detail"].lower()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue