M18: implement operational workspaces
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user