Files

95 lines
4.6 KiB
Python

import importlib.util
import json
from pathlib import Path
import subprocess
import sys
import tomllib
from types import SimpleNamespace
from viano_bug_analysis import deployment
from viano_bug_analysis.api.config import APIConfig, Project, Client
from viano_bug_analysis.api.sessions import Sessions
from viano_bug_analysis.worker.config import WorkerConfig
ROOT=Path(__file__).resolve().parents[1]
def installer():
spec=importlib.util.spec_from_file_location('linux_install',ROOT/'deploy/install.py')
module=importlib.util.module_from_spec(spec);spec.loader.exec_module(module)
return module
def test_install_plan_and_templates():
result=subprocess.run([sys.executable,str(ROOT/'deploy/install.py'),'--dry-run'],capture_output=True,text=True,check=True)
assert json.loads(result.stdout)['services_started'] is False
files=installer().templates()
for name,text in files.items():
if name.endswith('.toml'):tomllib.loads(text)
project=tomllib.loads(files['project.toml'])
assert project['database']['password_env']=='VIANO_DB_PASSWORD'
assert 'password_file' not in project['database']
assert project['source']['commit']=='0'*40
assert tomllib.loads(files['api.toml'])['auth']['users_database'].startswith('/etc/')
def test_api_ready_and_missing_machine_credentials(tmp_path,monkeypatch):
config=APIConfig(tmp_path,(Project('p','wow','vcore'),),())
monkeypatch.setattr(deployment.APIConfig,'load',lambda path:config)
assert deployment.check('api','unused')['status']=='NOT_READY'
Sessions(config).users.manage('create_user','alice','example password')
assert deployment.check('api','unused')['status']=='READY'
config=APIConfig(tmp_path,config.projects,(Client('m','VBA_TEST_MISSING',('p',)),))
monkeypatch.delenv('VBA_TEST_MISSING',raising=False)
assert deployment.check('api','unused')['code']=='DEPLOY_MACHINE_CREDENTIALS'
def test_worker_checks_only_read_dependencies(tmp_path,monkeypatch):
config=APIConfig(tmp_path,(Project('p','wow','vcore'),),())
monkeypatch.setattr(deployment.WorkerConfig,'load',lambda path:SimpleNamespace(intake=config,min_free_bytes=1))
calls=[]
research=SimpleNamespace(source=SimpleNamespace(inventory=lambda:calls.append('git')),
database=SimpleNamespace(tables=['creature'],query=lambda query:calls.append(query)))
def loader(project):return object(),{'project_research':research},'hash'
result=deployment.check('worker','unused',loader=loader)
assert result['status']=='READY' and result['model_requests']==0 and not result['cloud_api_tested']
assert calls==['git',{'kind':'database.schema','table':'creature'}]
calls.clear()
assert deployment.check('worker','unused',offline=True,loader=loader)['status']=='CONFIG_READY'
assert calls==[]
def failing(project):raise RuntimeError('secret-value')
assert 'secret-value' not in json.dumps(deployment.check('worker','unused',loader=failing))
def test_units_keep_secrets_separate_and_code_readonly():
for role in ('api','worker'):
unit=(ROOT/'deploy'/f'viano-bug-analysis-{role}.service').read_text()
assert f'EnvironmentFile=/etc/viano-bug-analysis/{role}.env' in unit
assert f'deployment check --role {role}' in unit
assert 'ProtectSystem=strict' in unit
assert 'ReadWritePaths=/var/lib/viano-bug-analysis' in unit
assert 'Restart=always' in unit and '--once' not in unit
def test_installed_config_references_resolve(tmp_path):
for name,text in installer().templates().items():
(tmp_path/name).write_text(text,encoding='utf-8')
config=WorkerConfig.load(tmp_path/'worker.toml')
assert config.intake.projects[0].planning_mode=='joint'
assert all(path.is_file() for path in config.intake.projects[0].settings.values())
def test_https_proxy_preserves_secure_login_cookie(tmp_path):
from starlette.testclient import TestClient
from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware
from viano_bug_analysis.api.app import create_app
config=APIConfig(tmp_path,(Project('p','wow','vcore'),),())
app=create_app(config)
app.state.sessions.users.manage('create_user','alice','example password')
proxied=ProxyHeadersMiddleware(app,trusted_hosts=['127.0.0.1'])
with TestClient(proxied,client=('127.0.0.1',1234)) as client:
result=client.post('/v1/auth/login',headers={'X-Forwarded-Proto':'https','Origin':'https://testserver'},
json={'username':'alice','password':'example password'})
assert result.status_code==200
assert 'secure' in result.headers['set-cookie'].lower()