"""HTTP/HTMX flows against the real core metadata/prepare protocol, no execution.""" import json import re import pytest from fastapi.testclient import TestClient from aim_webgui.app import create_app from aim_webgui.workflows import Workflows @pytest.fixture def browser(auth,admin): token,session=auth.new_session(admin) with TestClient(create_app(auth.settings),base_url=auth.settings.public_url,follow_redirects=False) as client: client.cookies.set(auth.settings.cookie_name,token) client.headers.update({'Origin':auth.settings.public_url,'X-CSRF-Token':session['csrf']}) yield client def test_html_new_run_review_queue_and_save_optional(browser,auth,admin): response=browser.get('/plan?customer=example&playbook=debug_test_connection') assert response.status_code==200, response.text assert 'Load customer SSH key' in response.text response=browser.post('/_partials/preflight',data={'customer':'example','playbook':'debug_test_connection', 'targets':'test01.example','mode':'check','key_mode':'none'},headers={'HX-Request':'true'}) assert response.status_code==200,response.text assert 'Run once' in response.text and 'Save as a reusable plan (optional)' in response.text review=re.search(r'name="review_id" value="([a-f0-9]+)"',response.text).group(1) assert 'name="name"' in response.text and not re.search('name="name"[^>]+required',response.text) response=browser.post('/jobs',data={'review_id':review,'submission_key':review,'confirm':'reviewed'}) assert response.status_code==303,response.text path=response.headers['location'] response=browser.get(path) assert response.status_code==200,response.text assert 'queued' in response.text assert Workflows(auth.settings).plans(admin)==[] saved=browser.post('/plans',data={'review_id':review,'name':''}) assert saved.status_code==303,saved.text assert len(Workflows(auth.settings).plans(admin))==1 title=Workflows(auth.settings).plans(admin)[0]['name'] conflict=browser.post('/plans',data={'review_id':review,'name':title.upper()}) assert conflict.status_code==409 assert 'nothing was overwritten' in conflict.text assert len(Workflows(auth.settings).plans(admin))==1 def test_json_one_run_idempotency_and_api_version(browser,auth,admin): r=browser.post('/api/v2/preflight',json={'customer':'example','playbook':'debug_test_connection','targets':['test01.example'],'overrides':{}}) assert r.status_code==200,r.text data={'review_id':r.json()['review_id'],'confirm':True} r=browser.post('/api/v2/runs',json=data,headers={'Idempotency-Key':'test-run-once'}) assert r.status_code==202,r.text ident=r.json()['id'] repeat=browser.post('/api/v2/runs',json=data,headers={'Idempotency-Key':'test-run-once'}) assert repeat.json()['id']==ident assert browser.get('/api/v2/runs/'+ident).json()['plan']['core_request']['check'] is True assert browser.get('/api/v1/runs/'+ident).status_code==404 def test_security_and_private_review(browser,auth,admin): response=browser.post('/api/v2/preflight',headers={'Origin':'https://evil.invalid'},json={}) assert response.status_code==403 browser.headers.pop('X-CSRF-Token') assert browser.post('/jobs',data={}).status_code==403 assert browser.get('/users').status_code==200 auth.create_user('viewer','Long-fixture-password-2026','viewer') with auth.store.transaction()as db: db.execute("UPDATE users SET must_change_password=0 WHERE username='viewer'") user=db.execute("SELECT id FROM users WHERE username='viewer'").fetchone()[0] token,session=auth.new_session(user) browser.cookies.set(auth.settings.cookie_name,token) browser.headers['X-CSRF-Token']=session['csrf'] assert browser.get('/users').status_code==403 assert browser.post('/api/v2/runs',json={'review_id':'not-mine','confirm':True},headers={'Idempotency-Key':'x'}).status_code in(403,409) def test_core_outage_keeps_local_login_recovery(auth,admin,monkeypatch): from aim_webgui.adapters.core_v1 import CoreAdapter from aim_webgui.errors import WebError def down(*args,**kwargs):raise WebError('executor_unavailable','Offline',503) monkeypatch.setattr(CoreAdapter,'customers',down) with TestClient(create_app(auth.settings),base_url=auth.settings.public_url)as client: assert client.get('/login').status_code==200 assert client.get('/healthz').status_code==200 assert client.get('/readyz').status_code==503 def test_retained_overviews_and_admin_pages(browser): for path in ('/','/customers','/customers/example/hosts','/playbooks','/jobs','/plans','/users','/setup','/audit','/system','/password'): response=browser.get(path) assert response.status_code==200,(path,response.status_code,response.text[:500]) response=browser.get('/users') assert response.text.count('Scoped execution grants')==1