mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-07 04:53:47 +08:00
80fbb4f6db
Register vehicle controls, planner modes, UI actions and Galaxy services after their providers exist. Wire the shared runtime entrypoints and cover their combined behavior. Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com> Co-authored-by: Daniel Koepping <elkoled@gmail.com> Co-authored-by: Danny <dannyirwin31@yahoo.com> Co-authored-by: DimitriChrysafis <Dimitri.chrysafis@gmail.com> Co-authored-by: Dom <168790843+firestar5683@users.noreply.github.com> Co-authored-by: Harald Schäfer <harald.the.engineer@gmail.com> Co-authored-by: Kacper Rączy <gfw.kra@gmail.com> Co-authored-by: Prabhaav Pillai <prabhaav@msu.edu> Co-authored-by: dirwin31 <83434411+dirwin31@users.noreply.github.com> Co-authored-by: firestarsdog <229254897+firestarsdog@users.noreply.github.com> Co-authored-by: stef <19478336+stefpi@users.noreply.github.com> Co-authored-by: whoisdomi <dcantu83@gmail.com> Co-authored-by: Intelli <contact@intelli.software> Co-authored-by: haraschax <6804392+haraschax@users.noreply.github.com> Co-authored-by: prabhaavp <prabhaav@msu.edu>
109 lines
4.4 KiB
Python
109 lines
4.4 KiB
Python
"""Map GET results belong to the same authenticated request generation."""
|
|
|
|
import http.client
|
|
import json
|
|
from pathlib import Path
|
|
import tempfile
|
|
import threading
|
|
from types import SimpleNamespace
|
|
import unittest
|
|
|
|
from openpilot.starpilot.galaxy.access import GalaxyAccessOwner
|
|
from openpilot.starpilot.galaxy.map_operations import MapOperationError
|
|
from openpilot.starpilot.galaxy.server import make_server
|
|
|
|
|
|
class MapOperationsHTTPTest(unittest.TestCase):
|
|
def setUp(self):
|
|
self.temp = tempfile.TemporaryDirectory()
|
|
self.access = GalaxyAccessOwner(Path(self.temp.name))
|
|
self.access.configure('test-password-123', lambda: True)
|
|
self.operations = SimpleNamespace(request=lambda _operation: {'fixture': 'map-read'})
|
|
self.maps = SimpleNamespace(snapshot=lambda: {'fixture': 'status-read'})
|
|
self.server = make_server(port=0, owner=self.access, parked=lambda: True, map_operations=self.operations, maps=self.maps)
|
|
self.thread = threading.Thread(target=self.server.serve_forever, kwargs={'poll_interval': 0.01}, daemon=True)
|
|
self.thread.start()
|
|
self.cookie = ''
|
|
|
|
def tearDown(self):
|
|
self.server.shutdown()
|
|
self.thread.join(2)
|
|
self.server.server_close()
|
|
self.temp.cleanup()
|
|
|
|
def request(self, path, method='GET', payload=None):
|
|
headers = {
|
|
'Origin': f'http://127.0.0.1:{self.server.server_port}',
|
|
'Forwarded': 'for=203.0.113.8',
|
|
'Content-Type': 'application/json',
|
|
'Cookie': self.cookie,
|
|
}
|
|
connection = http.client.HTTPConnection('127.0.0.1', self.server.server_port, timeout=4)
|
|
try:
|
|
connection.request(method, path, None if payload is None else json.dumps(payload), headers)
|
|
response = connection.getresponse()
|
|
cookie = response.getheader('Set-Cookie')
|
|
return response.status, json.loads(response.read()), cookie.split(';')[0] if cookie else ''
|
|
finally:
|
|
connection.close()
|
|
|
|
def login(self):
|
|
status, _, self.cookie = self.request('/api/auth/login', 'POST', {'password': 'test-password-123'})
|
|
self.assertEqual(status, 200)
|
|
|
|
def test_map_get_success_and_errors_require_same_session(self):
|
|
paths = ['/api/maps/status', '/api/maps/catalog', '/api/maps/operation', '/api/maps/setup']
|
|
for path in paths:
|
|
with self.subTest(path=path):
|
|
self.login()
|
|
self.assertEqual(self.request(path)[0], 200)
|
|
entered, release = threading.Event(), threading.Event()
|
|
|
|
def waiting(_operation=None, *, entered=entered, release=release):
|
|
entered.set()
|
|
release.wait(2)
|
|
return {'private': 'old-session-output'}
|
|
|
|
original = self.maps.snapshot if path.endswith('/status') else self.operations.request
|
|
if path.endswith('/status'):
|
|
self.maps.snapshot = waiting
|
|
else:
|
|
self.operations.request = waiting
|
|
responses = []
|
|
thread = threading.Thread(target=lambda path=path, responses=responses: responses.append(self.request(path)))
|
|
thread.start()
|
|
self.assertTrue(entered.wait(2))
|
|
self.assertEqual(self.request('/api/auth/logout', 'POST', {})[0], 200)
|
|
self.login() # A new valid session cannot inherit the in-flight response.
|
|
release.set()
|
|
thread.join(3)
|
|
self.assertEqual(responses[0][0], 401)
|
|
self.assertNotIn('old-session-output', json.dumps(responses))
|
|
if path.endswith('/status'):
|
|
self.maps.snapshot = original
|
|
else:
|
|
self.operations.request = original
|
|
|
|
def test_map_error_response_discards_revoked_generation(self):
|
|
self.login()
|
|
entered, release = threading.Event(), threading.Event()
|
|
|
|
def waiting(_operation):
|
|
entered.set()
|
|
release.wait(2)
|
|
raise MapOperationError(503, 'package_unavailable')
|
|
|
|
self.operations.request = waiting
|
|
responses = []
|
|
thread = threading.Thread(target=lambda: responses.append(self.request('/api/maps/setup')))
|
|
thread.start()
|
|
self.assertTrue(entered.wait(2))
|
|
self.assertEqual(self.request('/api/auth/logout', 'POST', {})[0], 200)
|
|
self.login()
|
|
release.set()
|
|
thread.join(3)
|
|
self.assertEqual(responses[0][0], 401)
|
|
self.assertNotIn('package_unavailable', json.dumps(responses))
|
|
self.operations.request = lambda _operation: (_ for _ in ()).throw(MapOperationError(503, 'package_unavailable'))
|
|
self.assertEqual(self.request('/api/maps/setup')[0:2], (503, {'error': 'Map management is unavailable', 'code': 'package_unavailable'}))
|