8cea7dee20
- SiteForge Enterprise V8.10.0-dev.1 upstream source (210 tests) - Fix: v8_workflows._mapped_input now reads both 类型/分类 columns so WXR imports keep post/product types instead of collapsing to page - Docs: trade-pro theme port spec (R3) and implementation plan Co-Authored-By: Claude Code <noreply@anthropic.com>
88 lines
4.2 KiB
Python
88 lines
4.2 KiB
Python
"""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='''<html><head><base href="/"></head><body><a href="/products/">Products</a>
|
|
<img src="/assets/example.png"><form action="/contact/"></form>
|
|
<video poster="/media/poster.jpg"></video><a href="//cdn.example/image"></a>
|
|
<a href="https://outside.example/about/"></a><a href="#part"></a>
|
|
<a href="/preview/s1/b1/old/">Old</a></body></html>'''
|
|
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('</body>','<a href="/products/">go to products</a></body>')
|
|
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'])
|