feat: refresh model and voice test catalog with favicon
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
import hashlib
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
import unittest
|
||||
from console.settings import Settings
|
||||
from console.store import Store
|
||||
from console.catalog import discover
|
||||
from deploy.refresh_catalog import refresh, restore
|
||||
|
||||
|
||||
class CatalogRefreshTests(unittest.TestCase):
|
||||
def test_refresh_preserves_options_and_history_and_restores_exactly(self):
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
base = Path(temporary); source = base/'source'; source.mkdir()
|
||||
(source/'old.py').write_text('print(1)')
|
||||
settings = Settings(base/'runtime', [{'id':'suite','path':str(source)}]); store = Store(settings)
|
||||
discover(store)
|
||||
old = store.scripts()[0]; old.update(enabled=True,status='ready',reason='')
|
||||
old['config']['timeout_seconds'] = 457
|
||||
store.save_script(old)
|
||||
old_runs = store.runs()
|
||||
(source/'old.py').write_text('print(5)')
|
||||
(source/'new.py').write_text('print(2)'); (source/'blocked.py').write_text('print(3)')
|
||||
(source/'support.py').write_text('print(4)')
|
||||
settings.native_catalog['profiles'] = [
|
||||
{'id':name,'source_group':'suite','entry':name+'.py','cwd':str(source),'interpreter':'/usr/bin/python3',
|
||||
'user':'root','available':name!='blocked','unavailable_reason':'原服务预设已过期','parameters':[]}
|
||||
for name in ('old','new','blocked')]
|
||||
snapshot = base/'migration.json'
|
||||
self.assertFalse(refresh(store, snapshot)['applied'])
|
||||
self.assertFalse(snapshot.exists())
|
||||
refresh(store, snapshot, True)
|
||||
by_name = {s['name']:s for s in store.scripts()}
|
||||
self.assertEqual(by_name['old.py']['config']['timeout_seconds'],457)
|
||||
self.assertFalse(by_name['old.py']['enabled'])
|
||||
self.assertEqual(by_name['old.py']['version'],2)
|
||||
self.assertTrue(by_name['new.py']['enabled'])
|
||||
self.assertFalse(by_name['blocked.py']['enabled'])
|
||||
self.assertFalse(by_name['support.py']['enabled'])
|
||||
self.assertEqual(store.runs(),old_runs)
|
||||
self.assertEqual(refresh(store,base/'again.json',True)['changed'],0)
|
||||
restore(store,snapshot)
|
||||
self.assertEqual(store.scripts(),[old])
|
||||
@@ -0,0 +1,69 @@
|
||||
import json
|
||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
import threading
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
from console.model_preflight import check_model
|
||||
|
||||
|
||||
class ModelPreflightTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.temp = tempfile.TemporaryDirectory()
|
||||
self.root = Path(self.temp.name)
|
||||
self.credential = self.root / 'vllm.env'
|
||||
self.credential.write_text('VLLM_API_KEY="dummy-metadata-key"\n')
|
||||
self.calls = []
|
||||
self.status = 200
|
||||
self.body = {'data': [{'id': 'current-model', 'max_model_len': 262144}]}
|
||||
case = self
|
||||
class Handler(BaseHTTPRequestHandler):
|
||||
def log_message(self, *args): pass
|
||||
def do_GET(self):
|
||||
case.calls.append((self.path, self.headers.get('Authorization')))
|
||||
self.send_response(case.status)
|
||||
if case.status == 302:
|
||||
self.send_header('Location', '/redirect-target')
|
||||
self.end_headers()
|
||||
self.wfile.write(json.dumps(case.body).encode())
|
||||
self.server = HTTPServer(('127.0.0.1', 0), Handler)
|
||||
self.thread = threading.Thread(target=self.server.serve_forever, daemon=True)
|
||||
self.thread.start()
|
||||
self.target = {'url': 'http://127.0.0.1:' + str(self.server.server_port), 'model': 'current-model'}
|
||||
|
||||
def tearDown(self):
|
||||
self.server.shutdown(); self.server.server_close(); self.thread.join()
|
||||
self.temp.cleanup()
|
||||
|
||||
def test_metadata_only_one_request_with_runtime_credentials(self):
|
||||
result = check_model(self.target, [str(self.credential)])
|
||||
self.assertTrue(result['ok'])
|
||||
self.assertEqual(self.calls, [('/v1/models', 'Bearer dummy-metadata-key')])
|
||||
self.assertNotIn('dummy-metadata-key', json.dumps(result))
|
||||
|
||||
def test_wrong_model_fails_even_when_port_is_open(self):
|
||||
self.target['model'] = 'stale-model'
|
||||
result = check_model(self.target, [str(self.credential)])
|
||||
self.assertFalse(result['ok'])
|
||||
self.assertIn('模型名称', result['message'])
|
||||
|
||||
def test_auth_failure_and_redirect_do_not_retry_or_leak(self):
|
||||
for status in (401, 302):
|
||||
self.status = status; self.calls.clear()
|
||||
result = check_model(self.target, [str(self.credential)])
|
||||
self.assertFalse(result['ok'])
|
||||
self.assertEqual(len(self.calls), 1)
|
||||
self.assertNotIn('dummy-metadata-key', json.dumps(result))
|
||||
|
||||
def test_external_address_rejected_before_network(self):
|
||||
self.target['url'] = 'http://example.com'
|
||||
with patch('console.model_preflight.urllib.request.build_opener') as build:
|
||||
self.assertFalse(check_model(self.target, [])['ok'])
|
||||
build.assert_not_called()
|
||||
|
||||
def test_malformed_or_oversize_metadata_is_not_success(self):
|
||||
for body in ({'data': {}}, {'data': ['bad']}, {'data': [], 'padding': 'x' * 262145}):
|
||||
self.body = body
|
||||
self.assertFalse(check_model(self.target, [str(self.credential)])['ok'])
|
||||
@@ -0,0 +1,59 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
import unittest
|
||||
import tempfile
|
||||
from unittest.mock import patch
|
||||
from console.native_container import command, cleanup
|
||||
from console.native_io import read_secret_tables, SecretFilter
|
||||
|
||||
|
||||
class NativeContainerTests(unittest.TestCase):
|
||||
def test_registered_key_table_is_redacted_without_environment_injection(self):
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
path = Path(temporary) / 'keys.txt'
|
||||
path.write_text('# local fixture\noperator dummy-audio-key scope\n')
|
||||
values = read_secret_tables([{'path': str(path), 'key_column': 1}])
|
||||
self.assertEqual(values, ['dummy-audio-key'])
|
||||
self.assertNotIn(b'dummy-audio-key', SecretFilter(values).clean(b'header=dummy-audio-key'))
|
||||
|
||||
def setUp(self):
|
||||
self.identity = 'a' * 32
|
||||
self.profile = {'docker': {'image': 'sha256:' + 'b' * 64, 'mounts': [{'source': '/original/tests', 'target': '/t'}]},
|
||||
'interpreter': '/opt/omni/bin/python'}
|
||||
|
||||
def test_fixed_image_readonly_sources_and_owned_output_without_gpu(self):
|
||||
args = command(self.profile, Path('/original/tests/test_api.py'), ['x'], '/runs/' + self.identity, self.identity)
|
||||
self.assertIn('xas.native-run=' + self.identity, args)
|
||||
self.assertIn('type=bind,src=/original/tests,dst=/t,readonly', args)
|
||||
self.assertIn('type=bind,src=/runs/' + self.identity + '/work,dst=/out', args)
|
||||
self.assertEqual(args[-4:], [self.profile['docker']['image'], '-u', '/t/test_api.py', 'x'])
|
||||
self.assertNotIn('--gpus', args)
|
||||
self.assertIn('runc', args)
|
||||
|
||||
def test_cleanup_rejects_foreign_container(self):
|
||||
inspected = SimpleNamespace(returncode=0, stdout=json.dumps([{'Name': '/adamwen-native-run-' + self.identity,
|
||||
'Config': {'Labels': {'xas.native-run': 'other'}}}]), stderr='')
|
||||
with patch('console.native_container.subprocess.run', return_value=inspected) as call:
|
||||
with self.assertRaises(ValueError): cleanup(self.identity)
|
||||
self.assertEqual(call.call_count, 1)
|
||||
|
||||
def test_daemon_error_is_not_treated_as_container_missing(self):
|
||||
error = SimpleNamespace(returncode=1, stdout='', stderr='Cannot connect to Docker daemon')
|
||||
with patch('console.native_container.subprocess.run', return_value=error):
|
||||
with self.assertRaises(OSError): cleanup(self.identity)
|
||||
|
||||
def test_missing_owned_container_is_already_clean(self):
|
||||
missing = SimpleNamespace(returncode=1, stdout='', stderr='error: no such object: adamwen-native-run-' + self.identity)
|
||||
with patch('console.native_container.subprocess.run', return_value=missing) as call:
|
||||
cleanup(self.identity)
|
||||
self.assertEqual(call.call_count, 1)
|
||||
|
||||
def test_auto_removed_container_race_is_not_an_uncertain_cleanup(self):
|
||||
container_id = 'c' * 64
|
||||
inspected = SimpleNamespace(returncode=0, stdout=json.dumps([{'Id':container_id,
|
||||
'Name':'/adamwen-native-run-' + self.identity,'Config':{'Labels':{'xas.native-run':self.identity}}}]),stderr='')
|
||||
gone = SimpleNamespace(returncode=1,stdout='',stderr='Error: No such container: ' + container_id)
|
||||
with patch('console.native_container.subprocess.run',side_effect=[inspected,gone,gone]) as call:
|
||||
cleanup(self.identity)
|
||||
self.assertEqual(call.call_args_list[1].args[0][-1],container_id)
|
||||
Reference in New Issue
Block a user