M18: implement operational workspaces

This commit is contained in:
NuklearRabbit
2026-08-10 12:41:28 +02:00
parent 8030753dbc
commit fe06ff75a1
33 changed files with 1082 additions and 95 deletions
+26 -8
View File
@@ -1,4 +1,5 @@
import threading
from datetime import datetime, timedelta
from fastapi.testclient import TestClient
@@ -27,6 +28,23 @@ def test_list_bookings_supports_bounded_search_pages(ops_client):
assert len(body["items"]) == 25
def test_list_bookings_filters_operational_window_and_location(ops_client):
booking = ops_client.get("/api/v1/bookings").json()[0]
vehicle = ops_client.get(f"/api/v1/vehicles/{booking['vehicle_ref']}").json()
starts_at = datetime.fromisoformat(booking["starts_at"])
response = ops_client.get(
"/api/v1/bookings",
params={
"starts_from": (starts_at - timedelta(minutes=1)).isoformat(),
"starts_to": (starts_at + timedelta(minutes=1)).isoformat(),
"location": vehicle["location"],
"sort": "starts_asc",
},
)
assert response.status_code == 200
assert booking["public_ref"] in {item["public_ref"] for item in response.json()}
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(
@@ -39,9 +57,9 @@ def test_create_booking_rejects_overlap_and_audits_valid_booking(ops_client):
},
)
assert conflict.status_code == 409
available_vehicle = ops_client.get(
"/api/v1/vehicles", params={"status": "available"}
).json()[0]["public_ref"]
available_vehicle = ops_client.get("/api/v1/vehicles", params={"status": "available"}).json()[
0
]["public_ref"]
created = ops_client.post(
"/api/v1/bookings",
json={
@@ -115,11 +133,11 @@ def test_concurrent_bookings_only_reserve_vehicle_once():
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,
},
json={
"customer_ref": "CUS-0001",
"vehicle_ref": vehicle_ref,
**window,
},
)
results.append(response.status_code)
+43
View File
@@ -53,6 +53,49 @@ def test_list_issues_requires_operations_manager(employee_client):
assert response.status_code == 403
def test_manager_can_assign_prioritised_quality_work_and_filter_it(ops_client):
assignee = next(user for user in ops_client.get("/api/v1/users").json() if user["active"])
open_issues = ops_client.get("/api/v1/data-quality/issues", params={"status": "open"}).json()
refs = [issue["public_ref"] for issue in open_issues[:2]]
due_at = "2030-01-15T12:00:00+00:00"
updated = ops_client.post(
"/api/v1/data-quality/issues/bulk-work",
json={
"issue_refs": refs,
"assigned_to_ref": assignee["public_ref"],
"due_at": due_at,
},
)
assert updated.status_code == 200
assert {issue["public_ref"] for issue in updated.json()["updated"]} == set(refs)
assert all(
issue["assigned_to_ref"] == assignee["public_ref"] for issue in updated.json()["updated"]
)
filtered = ops_client.get(
"/api/v1/data-quality/issues",
params={"status": "open", "assigned_to_ref": assignee["public_ref"]},
).json()
assert set(refs).issubset({issue["public_ref"] for issue in filtered})
audits = ops_client.get("/api/v1/audit", params={"action": "data_quality_work_updated"}).json()
assert len(audits) >= 2
cleared = ops_client.post(
"/api/v1/data-quality/issues/bulk-work",
json={"issue_refs": refs, "clear_assignment": True},
)
assert cleared.status_code == 200
assert all(issue["assigned_to_ref"] is None for issue in cleared.json()["updated"])
def test_employee_cannot_assign_quality_work(employee_client):
response = employee_client.post(
"/api/v1/data-quality/issues/bulk-work",
json={"issue_refs": ["DQ-DEMO-OVERLAP"], "clear_assignment": True},
)
assert response.status_code == 403
def test_issue_page_preserves_severity_filter_and_limits_results(ops_client):
response = ops_client.get(
"/api/v1/data-quality/issues",
+13 -2
View File
@@ -12,6 +12,18 @@ def test_attention_only_filters_flagged_vehicles(ops_client):
vehicles = response.json()
assert len(vehicles) > 0
assert all(v["attention"] for v in vehicles)
assert all(v["attention_reason"] for v in vehicles)
def test_vehicle_list_exposes_planning_and_service_context(ops_client):
vehicles = ops_client.get("/api/v1/vehicles").json()
assert vehicles
assert all("service_remaining_km" in vehicle for vehicle in vehicles)
assert all("next_booking_ref" in vehicle for vehicle in vehicles)
location = vehicles[0]["location"]
filtered = ops_client.get("/api/v1/vehicles", params={"location": location}).json()
assert filtered
assert all(vehicle["location"].casefold() == location.casefold() for vehicle in filtered)
def test_vehicle_page_preserves_filters_and_limits_rendered_records(ops_client):
@@ -56,8 +68,7 @@ def test_manager_can_record_maintenance_and_release_vehicle(ops_client):
detail = ops_client.get(f"/api/v1/vehicles/{vehicle['public_ref']}").json()
assert detail["operational_status"] == "maintenance"
assert any(
item["public_ref"] == response.json()["public_ref"]
for item in detail["maintenance"]
item["public_ref"] == response.json()["public_ref"] for item in detail["maintenance"]
)
released = ops_client.post(
f"/api/v1/vehicles/{vehicle['public_ref']}/release",