from sqlalchemy import select from app.core.db import SessionLocal from app.models.booking import Booking from app.models.vehicle import Vehicle def test_demo_login_is_audited(ops_client): response = ops_client.get("/api/v1/audit", params={"action": "demo_login"}) assert response.status_code == 200 events = response.json() assert len(events) >= 1 assert events[0]["action"] == "demo_login" def test_audit_requires_authentication(client): response = client.get("/api/v1/audit") assert response.status_code == 401 def test_audit_requires_operations_manager(employee_client): response = employee_client.get("/api/v1/audit") assert response.status_code == 403 def test_audit_page_is_bounded_and_exposes_filter_metadata(ops_client): response = ops_client.get("/api/v1/audit", params={"page": 1, "page_size": 25}) assert response.status_code == 200 body = response.json() assert len(body["items"]) <= 25 assert body["page"] == 1 assert body["page_size"] == 25 assert body["total"] >= len(body["items"]) assert body["total_pages"] >= 1 def _activate_booking(vehicle_ref: str, start_odometer_km: int) -> str: db = SessionLocal() try: vehicle = db.scalar(select(Vehicle).where(Vehicle.public_ref == vehicle_ref)) booking = db.scalar( select(Booking).where(Booking.vehicle_id == vehicle.id, Booking.status == "returned") ) booking.status = "active" booking.start_odometer_km = start_odometer_km booking.end_odometer_km = None db.commit() return booking.public_ref finally: db.close() def test_return_registered_audit_event_exposes_before_after_and_link(ops_client): booking_ref = _activate_booking("MO-015", start_odometer_km=17000) vehicle_before = ops_client.get("/api/v1/vehicles/MO-015").json() ops_client.post( f"/api/v1/bookings/{booking_ref}/return", json={ "end_odometer_km": vehicle_before["odometer_km"] + 10, "fuel_level_percent": 50, "cleanliness_ok": True, "damage_reported": False, "technical_warning": False, }, headers={"Idempotency-Key": "test-audit-before-after-001"}, ) key = "test-audit-before-after-001" events = ops_client.get("/api/v1/audit", params={"action": "return_registered"}).json() event = next(e for e in events if e["metadata"]["idempotency_key"] == key) assert event["before"] == {"status": "active"} assert event["after"]["status"] == "returned" assert event["entity_ref"] == booking_ref assert event["entity_link"] == f"/bookings/{booking_ref}" vehicle_events = ops_client.get( "/api/v1/audit", params={"action": "vehicle_status_changed", "correlation_id": event["correlation_id"]}, ).json() assert len(vehicle_events) == 1 assert vehicle_events[0]["entity_ref"] == "MO-015" assert vehicle_events[0]["entity_link"] == "/vehicles/MO-015" assert vehicle_events[0]["before"]["odometer_km"] == vehicle_before["odometer_km"]