"""Deterministic P0 stabilization regression tests (no external services).""" import json import time from pathlib import Path from bs4 import BeautifulSoup from fastapi.testclient import TestClient from app import db, builder from app.main import app from app.preview_rewrite import rewrite_preview_html from app.version import VERSION from app.worker import heartbeat from app.worker_health import healthy def test_preview_rewrites_only_internal_root_links(): old='''Products
Old''' new=rewrite_preview_html(old,'s1','b1') soup=BeautifulSoup(new,'html.parser') assert soup.base['href']=='/preview/s1/b1/' assert soup.find('a',string='Products')['href']=='/preview/s1/b1/products/' assert soup.img['src']=='/preview/s1/b1/assets/example.png' assert soup.form['action']=='/preview/s1/b1/contact/' assert soup.video['poster']=='/preview/s1/b1/media/poster.jpg' assert soup.select('a[href="//cdn.example/image"]') assert soup.select('a[href="https://outside.example/about/"]') assert soup.select('a[href="#part"]') assert soup.select('a[href="/preview/s1/b1/old/"]') assert 'href="/preview/s1/b1/preview/s1/b1/"' not in new def test_preview_does_not_modify_static_files(monkeypatch,tmp_path): monkeypatch.setattr(db,'DATA',tmp_path) monkeypatch.setattr(db,'DB_FILE',tmp_path/'siteforge.sqlite3') monkeypatch.setenv('SITEFORGE_ADMIN_PASSWORD','test-p0-password-12345') monkeypatch.setenv('SITEFORGE_SESSION_SECRET','test-p0-long-session-secret-value-123456') with TestClient(app) as client: assert client.get('/favicon.ico').status_code==200 health=client.get('/api/health').json() assert health['version']==VERSION assert isinstance(health['commit'],str) and health['commit'] assert client.post('/api/auth/login',json={'password':'test-p0-password-12345'}).status_code==200 client.headers['X-SiteForge-CSRF']=client.get('/api/auth/me').json()['csrf'] site=client.post('/api/sites',json={'name':'Preview Test','theme':'industrial'}).json() sid=site['id'] built=builder.build(sid)['version'] root=tmp_path/'builds'/sid/built/'dist' # Inject a link into an immutable build for precise comparison. f=root/'index.html' content=f.read_text(encoding='utf-8').replace('','go to products') f.write_text(content,encoding='utf-8') res=client.get(f'/preview/{sid}/{built}/') assert res.status_code==200 s=BeautifulSoup(res.text,'html.parser') assert s.find('a',string='go to products')['href']==f'/preview/{sid}/{built}/products/' assert client.get(f'/preview/{sid}/{built}/products/').status_code==200 assert 'href="/products/"' in f.read_text(encoding='utf-8') # Content types / 404 still preserved. assert client.get(f'/preview/{sid}/{built}/does-not-exist/').status_code==404 def test_worker_probe_does_not_depend_on_web(monkeypatch,tmp_path): monkeypatch.setattr(db,'DATA',tmp_path) assert not healthy(tmp_path/'worker-heartbeat') heartbeat() assert healthy(tmp_path/'worker-heartbeat') assert not healthy(tmp_path/'worker-heartbeat',max_age=0) import os old=time.time()-120 os.utime(tmp_path/'worker-heartbeat',(old,old)) assert not healthy(tmp_path/'worker-heartbeat',max_age=45) def test_worker_compose_reuses_image_and_has_own_healthcheck(): import yaml root=Path(__file__).resolve().parents[1] for config in ('compose.yaml','compose.enterprise.yaml'): comp=yaml.safe_load((root/config).read_text(encoding='utf-8')) web=comp['services']['siteforge'] worker=comp['services']['worker'] assert web['image']==worker['image'] assert 'build' in web and 'build' not in worker assert 'app.worker_health' in worker['healthcheck']['test'] assert 'http://127.0.0.1:8787/api/health' not in str(worker['healthcheck'])