Publish curated MobilityOps source snapshot
This commit is contained in:
commit
7adc072f5e
317 files changed
+48851
No files matched your search
@@ -0,0 +1,156 @@
|
||||
"""Pure unit tests for the shared vehicle-status evaluator -- no database needed, since
|
||||
evaluate_vehicle_status() only reasons over an already-gathered VehicleStatusFacts. See
|
||||
docs/fleet-ops-correction/vehicle-status-decision-table.md for the decision table these
|
||||
tests are asserting against."""
|
||||
|
||||
from types import SimpleNamespace
|
||||
|
||||
from app.services.vehicle_status import (
|
||||
RECOMMENDATION_CODE_ACTIVE_RENTAL,
|
||||
RECOMMENDATION_CODE_BOOKING_CONFLICT,
|
||||
RECOMMENDATION_CODE_MANUAL_REVIEW,
|
||||
RECOMMENDATION_CODE_NO_CONFLICT,
|
||||
RECOMMENDATION_CODE_RENTAL_ENDED,
|
||||
RECOMMENDATION_CODE_SERVICE_THRESHOLD,
|
||||
VehicleStatusFacts,
|
||||
compute_recommendation_token,
|
||||
evaluate_vehicle_status,
|
||||
)
|
||||
|
||||
|
||||
def _vehicle(status: str, *, version: int = 1):
|
||||
return SimpleNamespace(operational_status=status, version=version)
|
||||
|
||||
|
||||
def _facts(**overrides) -> VehicleStatusFacts:
|
||||
defaults = dict(
|
||||
active_booking_refs=[],
|
||||
overlapping_booking_pairs=[],
|
||||
service_threshold_reached=False,
|
||||
odometer_km=10_000,
|
||||
next_service_km=20_000,
|
||||
open_booking_overlap_issue_ref=None,
|
||||
)
|
||||
defaults.update(overrides)
|
||||
return VehicleStatusFacts(**defaults)
|
||||
|
||||
|
||||
def test_available_with_active_rental_recommends_rented():
|
||||
result = evaluate_vehicle_status(_vehicle("available"), _facts(active_booking_refs=["BK-0001"]))
|
||||
assert result.recommended_status == "rented"
|
||||
assert result.recommendation_code == RECOMMENDATION_CODE_ACTIVE_RENTAL
|
||||
assert result.safe_to_apply is True
|
||||
assert result.manual_review_required is False
|
||||
|
||||
|
||||
def test_maintenance_with_active_rental_never_auto_recommends_rented():
|
||||
# The exact unsafe shortcut this task explicitly forbids: maintenance + an active
|
||||
# booking must NEVER be auto-resolved to "rented".
|
||||
result = evaluate_vehicle_status(
|
||||
_vehicle("maintenance"), _facts(active_booking_refs=["BK-0001"])
|
||||
)
|
||||
assert result.recommended_status is None
|
||||
assert result.manual_review_required is True
|
||||
assert result.safe_to_apply is False
|
||||
assert result.recommendation_code == RECOMMENDATION_CODE_MANUAL_REVIEW
|
||||
|
||||
|
||||
def test_available_with_active_rental_and_service_threshold_requires_manual_review():
|
||||
result = evaluate_vehicle_status(
|
||||
_vehicle("available"),
|
||||
_facts(active_booking_refs=["BK-0001"], service_threshold_reached=True),
|
||||
)
|
||||
assert result.manual_review_required is True
|
||||
assert result.recommended_status is None
|
||||
|
||||
|
||||
def test_available_with_active_rental_and_booking_conflict_requires_manual_review():
|
||||
result = evaluate_vehicle_status(
|
||||
_vehicle("available"),
|
||||
_facts(active_booking_refs=["BK-0001"], open_booking_overlap_issue_ref="DQ-0001"),
|
||||
)
|
||||
assert result.manual_review_required is True
|
||||
assert result.recommended_status is None
|
||||
|
||||
|
||||
def test_rented_with_no_active_booking_recommends_available():
|
||||
result = evaluate_vehicle_status(_vehicle("rented"), _facts())
|
||||
assert result.recommended_status == "available"
|
||||
assert result.recommendation_code == RECOMMENDATION_CODE_RENTAL_ENDED
|
||||
assert result.safe_to_apply is True
|
||||
|
||||
|
||||
def test_service_threshold_reached_recommends_maintenance():
|
||||
result = evaluate_vehicle_status(_vehicle("available"), _facts(service_threshold_reached=True))
|
||||
assert result.recommended_status == "maintenance"
|
||||
assert result.recommendation_code == RECOMMENDATION_CODE_SERVICE_THRESHOLD
|
||||
|
||||
|
||||
def test_booking_conflict_recommends_blocked_not_a_generic_high_severity_proxy():
|
||||
# The evaluator must react to a *real* booking-conflict fact, not "does some other
|
||||
# open high-severity issue happen to exist" (the forbidden proxy).
|
||||
result = evaluate_vehicle_status(
|
||||
_vehicle("available"),
|
||||
_facts(overlapping_booking_pairs=[("BK-DEMO-OVERLAP-A", "BK-DEMO-OVERLAP-B")]),
|
||||
)
|
||||
assert result.recommended_status == "blocked"
|
||||
assert result.recommendation_code == RECOMMENDATION_CODE_BOOKING_CONFLICT
|
||||
|
||||
|
||||
def test_open_booking_overlap_issue_alone_also_triggers_blocked():
|
||||
result = evaluate_vehicle_status(
|
||||
_vehicle("available"), _facts(open_booking_overlap_issue_ref="DQ-DEMO-OVERLAP")
|
||||
)
|
||||
assert result.recommended_status == "blocked"
|
||||
assert result.recommendation_code == RECOMMENDATION_CODE_BOOKING_CONFLICT
|
||||
|
||||
|
||||
def test_maintenance_with_no_active_rental_and_no_blockers_stays_manual():
|
||||
# No fact here confirms maintenance is actually finished, so the evaluator must not
|
||||
# auto-clear it to "available" -- that release remains an explicit, manual decision.
|
||||
result = evaluate_vehicle_status(_vehicle("maintenance"), _facts())
|
||||
assert result.recommended_status is None
|
||||
assert result.recommendation_code == RECOMMENDATION_CODE_NO_CONFLICT
|
||||
assert result.safe_to_apply is False
|
||||
|
||||
|
||||
def test_no_conflict_when_status_already_matches_facts():
|
||||
result = evaluate_vehicle_status(_vehicle("available"), _facts())
|
||||
assert result.recommended_status is None
|
||||
assert result.recommendation_code == RECOMMENDATION_CODE_NO_CONFLICT
|
||||
assert result.safe_to_apply is False
|
||||
|
||||
result = evaluate_vehicle_status(_vehicle("rented"), _facts(active_booking_refs=["BK-1"]))
|
||||
assert result.recommendation_code == RECOMMENDATION_CODE_NO_CONFLICT
|
||||
|
||||
result = evaluate_vehicle_status(_vehicle("blocked"), _facts())
|
||||
assert result.recommendation_code == RECOMMENDATION_CODE_NO_CONFLICT
|
||||
|
||||
result = evaluate_vehicle_status(
|
||||
_vehicle("maintenance"), _facts(service_threshold_reached=True)
|
||||
)
|
||||
assert result.recommendation_code == RECOMMENDATION_CODE_NO_CONFLICT
|
||||
|
||||
|
||||
def test_recommendation_token_changes_when_facts_change():
|
||||
vehicle = _vehicle("available")
|
||||
facts_a = _facts()
|
||||
facts_b = _facts(service_threshold_reached=True)
|
||||
assert compute_recommendation_token(vehicle, facts_a) != compute_recommendation_token(
|
||||
vehicle, facts_b
|
||||
)
|
||||
|
||||
|
||||
def test_recommendation_token_is_stable_for_identical_facts():
|
||||
vehicle = _vehicle("available")
|
||||
facts = _facts(active_booking_refs=["BK-0001"])
|
||||
assert compute_recommendation_token(vehicle, facts) == compute_recommendation_token(
|
||||
vehicle, facts
|
||||
)
|
||||
|
||||
|
||||
def test_recommendation_token_changes_when_vehicle_version_changes():
|
||||
facts = _facts()
|
||||
assert compute_recommendation_token(
|
||||
_vehicle("available", version=1), facts
|
||||
) != compute_recommendation_token(_vehicle("available", version=2), facts)
|
||||
Reference in new issue
Block a user