"""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)