from unittest.mock import patch from pathlib import Path from types import SimpleNamespace as NS import tempfile from openpilot.starpilot.galaxy.tests import test_navigation_http as helpers from openpilot.starpilot.drive_state.owner import DriveStateOwner, Mode from openpilot.starpilot.drive_state.control import DriveStateControl from openpilot.starpilot.drive_state.tests.test_owner import Params, BOOT class ForceHttpTest(helpers.NavigationHttpTest): close = helpers.NavigationHttpTest.close request = helpers.NavigationHttpTest.request def setUp(self): temporary = tempfile.TemporaryDirectory() self.addCleanup(temporary.cleanup) self.params = Params() self.drive = DriveStateOwner(self.params, Path(temporary.name) / 'drive', alive=lambda _: True) self.drive.initialize(pid=1, birth=2, boot=BOOT) self.allowed = True self.physical = NS(allowed=lambda: self.allowed, status=lambda **kwargs: {"allowed": self.allowed, "reason": None}) self.control = DriveStateControl(self.drive, self.physical, effective=lambda: False) original = helpers.make_server with patch.object(helpers, 'make_server', side_effect=lambda **kwargs: original(**kwargs, drive_state=self.control)): helpers.NavigationHttpTest.setUp(self) def drive_action(self, mode, remote=False, revision=None, **kwargs): return self.request( '/api/drive-state/action', payload={'mode': mode, 'revision': revision or self.drive.snapshot().revision}, remote=remote, cookie=self.remote_cookie if remote else self.local_cookie, **kwargs, ) def test_local_remote_same_owner_status_actual_not_requested_and_strict_revision(self): result = self.drive_action('onroad') self.assertEqual(result[0], 200) self.assertEqual((result[1]['mode'], result[1]['effective']), ('onroad', 'offroad')) status = self.request('/api/drive-state/status', remote=True, cookie=self.remote_cookie) self.assertEqual(status[1]['revision'], result[1]['revision']) self.assertEqual(self.drive_action('offroad', remote=True)[0], 200) self.control.change('offroad', self.drive.snapshot().revision, lambda: True) self.assertEqual(self.drive_action('auto', revision=result[1]['revision'])[0], 409) def test_galaxy_requires_park_but_onroad_and_auto_remain_recoverable(self): self.assertEqual(self.drive_action('offroad')[0], 200) self.control.change('offroad', self.drive.snapshot().revision, lambda: True) self.allowed = False self.assertEqual(self.drive_action('onroad')[0], 200) self.assertEqual(self.drive.snapshot().mode, Mode.ONROAD) self.assertEqual(self.drive_action('offroad')[0], 409) self.assertEqual(self.drive.snapshot().mode, Mode.ONROAD) self.assertEqual(self.drive_action('auto', remote=True)[0], 200) self.assertEqual(self.drive.snapshot().mode, Mode.AUTO) def test_auth_origin_schema_and_midcommit_revocation_write_nothing(self): before = list(self.params.writes) self.assertEqual(self.request('/api/drive-state/action', payload={'mode': 'onroad', 'revision': self.drive.snapshot().revision})[0], 401) self.assertEqual(self.drive_action('onroad', origin='https://unrelated.invalid')[0], 403) self.assertEqual(self.drive_action('badmode')[0], 409) self.assertEqual(self.params.writes, before) original_change = self.control.change_galaxy def revoke_remote(mode, revision, authorized): self.assertEqual(mode, 'onroad') self.assertTrue(authorized()) self.pairing.unpair() # Real HTTP credential changes inside the route's effect lock. self.assertFalse(authorized()) return original_change(mode, revision, authorized) with patch.object(self.control, 'change_galaxy', side_effect=revoke_remote): self.assertEqual(self.drive_action('onroad', remote=True)[0], 409) self.assertEqual(self.params.writes, before) def test_native_force_offroad_does_not_query_park_permission_during_commit(self): def unexpected(): raise AssertionError('Local Force Offroad does not require parked admission') self.physical.allowed = unexpected self.control.change('offroad', self.drive.snapshot().revision, lambda: True) self.assertEqual(self.drive.snapshot().mode, Mode.OFFROAD) def test_galaxy_offroad_rechecks_park_for_local_and_remote_sessions(self): for remote in (False, True): self.allowed = False before = list(self.params.writes) self.assertEqual(self.drive_action('offroad', remote=remote)[0], 409) self.assertEqual(self.params.writes, before) self.allowed = True self.assertEqual(self.drive_action('offroad', remote=remote)[0], 200) self.assertEqual(self.drive_action('auto', remote=remote)[0], 200) def test_authenticated_force_offroad_from_live_onroad_state(self): self._force_offroad_from_live_onroad_state(True) def test_authenticated_force_offroad_with_invalid_can_from_onroad(self): self._force_offroad_from_live_onroad_state(False) def _force_offroad_from_live_onroad_state(self, can_valid): from openpilot.starpilot.drive_state.evidence import PhysicalSource from openpilot.starpilot.drive_state.tests.test_evidence import Messages from openpilot.starpilot.drive_state.resolver import should_start messages = Messages() messages.after_mono_ns = 1_000_000_000 messages.values['deviceState'] = NS(started=True) self.enterContext(patch.object(messages.values['carState'], 'canValid', can_valid, create=True)) pandas = messages.values['pandaStates'] assert isinstance(pandas, list) pandas[0].ignitionLine = True pandas[0].safetyModel = 'hyundai' physical = PhysicalSource(messages, mono=lambda: 2_100_000_000, boot=lambda: 12_100_000_000) self.addCleanup(physical.close) self.control.physical = physical self.control.effective = lambda: messages.values['deviceState'].started status = self.request('/api/drive-state/status', remote=True, cookie=self.remote_cookie) self.assertEqual(status[1]['effective'], 'onroad') self.assertEqual(status[1]['overrideAllowed'], can_valid) result = self.drive_action('offroad', remote=True) self.assertEqual(result[0], 200 if can_valid else 409) self.control.change('offroad', self.drive.snapshot().revision, lambda: True) self.assertEqual(self.drive.snapshot().mode, Mode.OFFROAD) # hardwared consumes this same owner request through the actual resolver. messages.values['deviceState'].started = should_start(self.drive.snapshot().mode, {'ignition': True}, {'ready': True}, already_started=True) self.assertFalse(messages.values['deviceState'].started) self.assertEqual(self.request('/api/drive-state/status', remote=True, cookie=self.remote_cookie)[1]['effective'], 'offroad')