from datetime import datetime from types import SimpleNamespace from app.routers.reviews import _apply_device_corrections from app.routers.reviews import _effective_review_time from app.schemas import ApproveReportRequest, ReportDeviceCorrection from app.services.serializers import _datetime_changed def _device(id_: int, name: str, scanned_at: datetime, sort_order: int): return SimpleNamespace( id=id_, device_no=name, process_name="1", scanned_at=scanned_at, sort_order=sort_order, ) def _report(devices): return SimpleNamespace(session=SimpleNamespace(devices=devices)) def test_first_device_correction_syncs_report_start_time(): first_scan = datetime(2026, 5, 27, 8, 0) next_start = datetime(2026, 5, 27, 8, 0) next_end = datetime(2026, 5, 27, 10, 0) first = _device(1, "A", first_scan, 0) second = _device(2, "B", datetime(2026, 5, 27, 9, 30), 1) payload = ApproveReportRequest( device_corrections=[ ReportDeviceCorrection(id=1, scanned_at=datetime(2026, 5, 27, 8, 30)) ] ) changed, corrected_start = _apply_device_corrections(None, _report([first, second]), payload, next_start, next_end) assert changed is True assert corrected_start == datetime(2026, 5, 27, 8, 30) assert first.scanned_at == corrected_start def test_top_start_time_syncs_first_device_when_device_corrections_are_empty(): first = _device(1, "A", datetime(2026, 5, 27, 13, 56, 11), 0) payload = ApproveReportRequest() changed, corrected_start = _apply_device_corrections( None, _report([first]), payload, datetime(2026, 5, 27, 10, 0), datetime(2026, 5, 27, 13, 59), ) assert changed is True assert corrected_start == datetime(2026, 5, 27, 10, 0) assert first.scanned_at == corrected_start def test_non_first_device_correction_does_not_change_report_start_time(): first = _device(1, "A", datetime(2026, 5, 27, 8, 0), 0) second = _device(2, "B", datetime(2026, 5, 27, 9, 0), 1) payload = ApproveReportRequest( device_corrections=[ ReportDeviceCorrection(id=2, scanned_at=datetime(2026, 5, 27, 9, 30)) ] ) changed, corrected_start = _apply_device_corrections( None, _report([first, second]), payload, datetime(2026, 5, 27, 8, 0), datetime(2026, 5, 27, 10, 0), ) assert changed is True assert corrected_start == datetime(2026, 5, 27, 8, 0) assert first.scanned_at == datetime(2026, 5, 27, 8, 0) assert second.scanned_at == datetime(2026, 5, 27, 9, 30) def test_same_minute_picker_value_keeps_original_seconds(): current = datetime(2026, 5, 29, 15, 10, 6) payload = datetime(2026, 5, 29, 15, 10, 0) assert _effective_review_time(payload, current) == current assert _datetime_changed(current.isoformat(), payload.isoformat()) is False def test_adjacent_minute_picker_value_is_real_correction(): current = datetime(2026, 5, 29, 15, 10, 6) payload = datetime(2026, 5, 29, 15, 11, 0) assert _effective_review_time(payload, current) == payload assert _datetime_changed(current.isoformat(), payload.isoformat()) is True