Files
MobilityOps/backend/tests/test_audit.py
T
NuklearRabbit 7e34f55005 feat(audit): expose structured before/after evidence
audit_events already stored before_json/after_json, but the API and UI only
ever surfaced metadata -- the audit trail could say something happened but
never show what changed. Add before/after to AuditEventOut, resolve a safe
entity_ref/entity_link for vehicle/booking/data-quality-issue entities
(customer stays label-only; no customer detail route exists in this PoC),
and render a human-readable change summary in the UI with the raw
before/after/metadata JSON kept behind a <details> disclosure rather than
shown by default.
2026-08-02 05:33:23 +02:00

75 lines
2.6 KiB
Python

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 _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"]