Files
MobilityOps/backend/tests/test_bookings.py
T

147 lines
5.0 KiB
Python

import threading
from fastapi.testclient import TestClient
from app.main import app
def test_list_bookings_filters_by_vehicle(ops_client):
response = ops_client.get("/api/v1/bookings", params={"vehicle_ref": "MO-024"})
assert response.status_code == 200
bookings = response.json()
assert len(bookings) >= 1
assert all(b["vehicle_ref"] == "MO-024" for b in bookings)
def test_list_bookings_supports_bounded_search_pages(ops_client):
response = ops_client.get(
"/api/v1/bookings",
params={"query": "BK-", "page": 1, "page_size": 25},
)
assert response.status_code == 200
body = response.json()
assert body["page"] == 1
assert body["page_size"] == 25
assert body["total"] > 25
assert body["total_pages"] > 1
assert len(body["items"]) == 25
def test_create_booking_rejects_overlap_and_audits_valid_booking(ops_client):
existing = ops_client.get("/api/v1/bookings/BK-DEMO-RETURN").json()
conflict = ops_client.post(
"/api/v1/bookings",
json={
"customer_ref": "CUS-0001",
"vehicle_ref": existing["vehicle_ref"],
"starts_at": existing["starts_at"],
"ends_at": existing["ends_at"],
},
)
assert conflict.status_code == 409
available_vehicle = ops_client.get(
"/api/v1/vehicles", params={"status": "available"}
).json()[0]["public_ref"]
created = ops_client.post(
"/api/v1/bookings",
json={
"customer_ref": "CUS-0001",
"vehicle_ref": available_vehicle,
"starts_at": "2030-09-01T10:00:00Z",
"ends_at": "2030-09-02T12:00:00Z",
},
)
assert created.status_code == 201
body = created.json()
assert body["status"] == "reserved"
assert body["vehicle_ref"] == available_vehicle
def test_customer_search_returns_canonical_customers(ops_client):
response = ops_client.get("/api/v1/customers", params={"query": "CUS-"})
assert response.status_code == 200
assert response.json()
assert all(item["public_ref"].startswith("CUS-") for item in response.json())
def test_booking_availability_excludes_overlapping_vehicle(ops_client):
existing = ops_client.get("/api/v1/bookings/BK-DEMO-RETURN").json()
response = ops_client.get(
"/api/v1/bookings/availability",
params={"starts_at": existing["starts_at"], "ends_at": existing["ends_at"]},
)
assert response.status_code == 200
assert existing["vehicle_ref"] not in {item["public_ref"] for item in response.json()}
def test_reserved_booking_can_be_cancelled_once(ops_client):
window = {"starts_at": "2032-09-01T10:00:00Z", "ends_at": "2032-09-02T12:00:00Z"}
available = ops_client.get("/api/v1/bookings/availability", params=window).json()
assert available
create_response = ops_client.post(
"/api/v1/bookings",
json={
"customer_ref": "CUS-0001",
"vehicle_ref": available[0]["public_ref"],
**window,
},
)
assert create_response.status_code == 201
created = create_response.json()
response = ops_client.post(
f"/api/v1/bookings/{created['public_ref']}/cancel",
json={"reason": "Customer changed plans"},
)
assert response.status_code == 200
assert response.json()["status"] == "cancelled"
repeated = ops_client.post(
f"/api/v1/bookings/{created['public_ref']}/cancel",
json={"reason": "Customer changed plans"},
)
assert repeated.status_code == 409
def test_concurrent_bookings_only_reserve_vehicle_once():
results: list[int] = []
seed_client = TestClient(app)
seed_client.post("/api/v1/demo/login", json={"role": "operations_manager"})
window = {"starts_at": "2040-09-01T10:00:00Z", "ends_at": "2040-09-02T12:00:00Z"}
available = seed_client.get("/api/v1/bookings/availability", params=window).json()
assert available
vehicle_ref = available[0]["public_ref"]
def submit() -> None:
client = TestClient(app)
client.post("/api/v1/demo/login", json={"role": "operations_manager"})
response = client.post(
"/api/v1/bookings",
json={
"customer_ref": "CUS-0001",
"vehicle_ref": vehicle_ref,
**window,
},
)
results.append(response.status_code)
threads = [threading.Thread(target=submit) for _ in range(2)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
assert results.count(201) == 1
assert results.count(409) == 1
def test_get_booking_detail(ops_client):
response = ops_client.get("/api/v1/bookings/BK-DEMO-RETURN")
assert response.status_code == 200
body = response.json()
assert body["status"] == "active"
assert body["vehicle_ref"] == "MO-024"
def test_get_booking_404_for_unknown_ref(ops_client):
response = ops_client.get("/api/v1/bookings/BK-UNKNOWN")
assert response.status_code == 404