test: guard durable chat deployment packaging

This commit is contained in:
snowspeeder committed 2026-10-03 08:58:36 -04:00
1 parent 550f7c2e91
commit 1a04dab031
2 files changed
+78 -1

No files matched your search

+1 -1
View File
@@ -27,7 +27,7 @@ jobs:
tests/unit/test_chat_store.py tests/unit/test_chat_runs.py \ tests/unit/test_chat_store.py tests/unit/test_chat_runs.py \
tests/unit/test_chat_api.py tests/unit/test_chat_exports.py \ tests/unit/test_chat_api.py tests/unit/test_chat_exports.py \
tests/unit/test_chat_features.py tests/unit/test_chat_security_review.py \ tests/unit/test_chat_features.py tests/unit/test_chat_security_review.py \
tests/unit/test_chat_cross_client.py -q tests/unit/test_chat_cross_client.py tests/unit/test_chat_packaging.py -q
' '
- name: Build images - name: Build images
run: | run: |
+77
View File
@@ -0,0 +1,77 @@
"""Offline source packaging guards; no image build, service or Docker calls."""
from html.parser import HTMLParser
from pathlib import Path
import re
ROOT = Path(__file__).resolve().parents[2]
class AssetReferences(HTMLParser):
def __init__(self):
super().__init__()
self.paths = []
def handle_starttag(self, tag, attrs):
attrs = dict(attrs)
path = attrs.get('src') if tag == 'script' else attrs.get('href') if tag == 'link' and attrs.get('rel') == 'stylesheet' else None
if path and not path.startswith(('http:', 'https:', '//', 'data:')):
self.paths.append(path.split('?')[0])
def test_speech_image_packages_coordinator_and_all_entrypoint_assets():
dockerfile = (ROOT/'Dockerfile').read_text()
for source, destination in (
('agent/agent.py', '/opt/voice-agent/agent.py'),
('agent/hermes_llm.py', '/opt/voice-agent/hermes_llm.py'),
('agent/conversation_llm.py', '/opt/voice-agent/conversation_llm.py'),
):
assert (ROOT/source).is_file()
assert re.search(r'^COPY\s+'+re.escape(source)+r'\s+'+re.escape(destination)+r'\s*$', dockerfile, re.M), source
references = AssetReferences()
references.feed((ROOT/'web/index.html').read_text())
assert {'chat.js', 'features.js', 'hope-themes.css'} <= set(references.paths)
for path in references.paths:
assert (ROOT/'web'/path).is_file(), path
assert re.search(r'^COPY\s+web/'+re.escape(path)+r'\s+/var/www/voice/\s*$', dockerfile, re.M), path
assert 'chmod -R a+rX /var/www/voice' in dockerfile
assert 'chmod 644 /opt/voice-agent/*.py /opt/voice/*.py' in dockerfile
assert '.venv-tests' in (ROOT/'.dockerignore').read_text().splitlines()
def test_control_packages_chat_imports_pdf_and_mounted_private_roots():
dockerfile = (ROOT/'control.Dockerfile').read_text()
assert re.search(r'^COPY\s+control\s+/app/control\s*$', dockerfile, re.M)
assert re.search(r'\bpypdf==6\.\d+\.\d+', dockerfile)
for module in ('chat_api', 'chat_store', 'chat_runs', 'chat_features', 'chat_exports'):
assert (ROOT/'control'/(module+'.py')).is_file()
deploy = (ROOT/'scripts/deploy-unraid.sh').read_text()
assert '-e CONTROL_DB_PATH=/var/lib/hope/accounts.sqlite3' in deploy
assert '-e HOPE_HERMES_DATA_DIR="$ROOT/hermes"' in deploy
assert '-v "$ROOT/accounts:/var/lib/hope"' in deploy
assert '-v "$ROOT/hermes:$ROOT/hermes"' in deploy
app = (ROOT/'control/app.py').read_text()
assert "Path(settings.db_path).absolute().parent / 'chat-features'" in app
def test_nginx_preserves_strict_auth_and_allows_declared_advanced_bodies():
nginx = (ROOT/'nginx.conf').read_text()
assert re.search(r'client_max_body_size\s+16k;', nginx)
for route, limit in ((r'/api/attachments', '20m'), (r'~ ^/api/(artifacts|memory)(/|$)', '1m'), (r'/api/tasks', '64k')):
block = re.search(r'location\s+'+re.escape(route)+r'\s*\{([^}]+)\}', nginx)
assert block is not None, route
assert f'client_max_body_size {limit};' in block[1]
assert 'proxy_pass http://127.0.0.1:8092;' in block[1]
assert re.search(r'location\s+/internal/\s*\{\s*return 404;', nginx)
api = re.search(r'location\s+/api/\s*\{([^}]+)\}', nginx)[1]
assert 'proxy_buffering off;' in api and 'proxy_read_timeout 700s;' in api
def test_ci_qualification_gates_images_and_deployment_without_live_conftest():
workflow = (ROOT/'.gitea/workflows/rebuild.yml').read_text()
assert workflow.index('Qualify offline') < workflow.index('Build images') < workflow.index('Deploy using')
assert '--noconftest -p no:cacheprovider' in workflow
for name in ('test_chat_packaging.py', 'test_chat_exports.py', 'test_chat_cross_client.py', 'test_chat_security_review.py', 'test_deploy_rollback.py'):
assert 'tests/unit/'+name in workflow
assert 'pytest-asyncio -r control/requirements.txt' in workflow
assert 'scripts/generate_hope_themes.py --check' in workflow
assert '-v "$PWD:/src:ro"' in workflow