chore: baseline V8.10.0-dev.1 with v8_workflows type-mapping fix and trade-pro spec
- 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>
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
"""Regression tests for release safety and CMS review persistence."""
|
||||
import json
|
||||
from fastapi.testclient import TestClient
|
||||
from fastapi import HTTPException
|
||||
import pytest
|
||||
from app import db, cms_pipeline, release_api
|
||||
from app.main import app
|
||||
|
||||
|
||||
def test_pending_and_failed_build_cannot_be_deployed(monkeypatch,tmp_path):
|
||||
monkeypatch.setattr(db,'DATA',tmp_path)
|
||||
monkeypatch.setattr(db,'DB_FILE',tmp_path/'pending.sqlite3')
|
||||
db.init()
|
||||
with db.connect() as c:
|
||||
c.execute('INSERT INTO sites(id,name,created_at,updated_at) VALUES (?,?,?,?)',('sitepending','Sample',db.now(),db.now()))
|
||||
for state in ['queued','running','failed','unknown']:
|
||||
c.execute('INSERT INTO builds(id,site_id,created_at,status,zip_path) VALUES (?,?,?,?,?)',
|
||||
(state,'sitepending',db.now(),state,str(tmp_path/'missing.zip')))
|
||||
for state in ['queued','running','failed','unknown']:
|
||||
with pytest.raises(HTTPException) as exc:
|
||||
release_api.approved('sitepending',state)
|
||||
assert exc.value.status_code==409
|
||||
|
||||
|
||||
def test_cms_warnings_saved_and_reviewed(monkeypatch,tmp_path):
|
||||
monkeypatch.setattr(db,'DATA',tmp_path)
|
||||
monkeypatch.setattr(db,'DB_FILE',tmp_path/'review.sqlite3')
|
||||
monkeypatch.setenv('SITEFORGE_ADMIN_PASSWORD','test-admin-password-for-cms-reviews')
|
||||
monkeypatch.setenv('SITEFORGE_SESSION_SECRET','test-session-secret-for-cms-reviews-long')
|
||||
with TestClient(app) as c:
|
||||
assert c.post('/api/auth/login',json={'password':'test-admin-password-for-cms-reviews'}).status_code==200
|
||||
c.headers['X-SiteForge-CSRF']=c.get('/api/auth/me').json()['csrf']
|
||||
res=c.post('/api/sites',json={'name':'CMS Review Test','mode':'migration'})
|
||||
assert res.status_code==201,res.text
|
||||
sid=res.json()['id']
|
||||
blob={'items':[{'id':3,'type':'page','title':{'rendered':'About'},'link':'https://old.example/about/',
|
||||
'content':{'rendered':'<p>old</p>'},'acf':{'related':[1,2]}}]}
|
||||
res=c.post(f'/api/migrations/sites/{sid}/import',data={'platform':'wordpress-rest','conflict':'skip'},
|
||||
files={'file':('export.json',json.dumps(blob).encode(),'application/json')})
|
||||
assert res.status_code==200,res.text
|
||||
assert res.json()['pending_review_count']==1
|
||||
get=c.get(f'/api/migrations/sites/{sid}/reviews')
|
||||
assert get.status_code==200 and get.json()['pending']==1
|
||||
issue=get.json()['items'][0]['id']
|
||||
reject=c.post(f'/api/migrations/sites/{sid}/reviews/{issue}/resolve',json={'resolution':'ok'})
|
||||
assert reject.status_code==400
|
||||
ok=c.post(f'/api/migrations/sites/{sid}/reviews/{issue}/resolve',json={'resolution':'已逐项检查关联产品及图片,确认无需迁移旧关联。'})
|
||||
assert ok.status_code==200,ok.text
|
||||
assert ok.json()['pending']==0
|
||||
assert c.get(f'/api/migrations/sites/{sid}/reviews').json()['items']==[]
|
||||
assert c.get(f'/api/migrations/sites/{sid}/reviews?state=resolved').json()['items'][0]['resolution']
|
||||
|
||||
|
||||
def test_signatures_are_bound_to_http_path(monkeypatch,tmp_path):
|
||||
from app import deploy_agent
|
||||
import zipfile,io,time
|
||||
from fastapi.testclient import TestClient
|
||||
monkeypatch.setattr(deploy_agent,'ROOT',tmp_path)
|
||||
secret='release-secret-very-long-at-least-thirtytwo-bytes'
|
||||
monkeypatch.setenv('SITEFORGE_DEPLOY_SECRET',secret)
|
||||
buf=io.BytesIO()
|
||||
with zipfile.ZipFile(buf,'w') as z:z.writestr('index.html','<title>OK</title>')
|
||||
client=TestClient(deploy_agent.app)
|
||||
stamp=str(int(time.time()))
|
||||
headers={'X-SiteForge-Timestamp':stamp,'X-SiteForge-Signature':deploy_agent.signature(secret,'company',stamp,buf.getvalue(),'POST','/deploy/company')}
|
||||
created=client.post('/deploy/company',headers=headers,content=buf.getvalue())
|
||||
assert created.status_code==200
|
||||
version=created.json()['release']
|
||||
# A read-only inventory signature must not authorize state-changing rollback.
|
||||
inventory_headers={'X-SiteForge-Timestamp':stamp,'X-SiteForge-Signature':deploy_agent.signature(secret,'company',stamp,b'','GET','/sites/company/releases')}
|
||||
assert client.get('/sites/company/releases',headers=inventory_headers).status_code==200
|
||||
assert client.post(f'/sites/company/rollback/{version}',headers=inventory_headers).status_code==401
|
||||
rollback_headers={'X-SiteForge-Timestamp':stamp,'X-SiteForge-Signature':deploy_agent.signature(secret,'company',stamp,b'','POST',f'/sites/company/rollback/{version}')}
|
||||
assert client.post(f'/sites/company/rollback/{version}',headers=rollback_headers).status_code==200
|
||||
|
||||
|
||||
def test_art_installer_idempotent_mock_checkout(tmp_path):
|
||||
from scripts.install_into_art_design_pro import install
|
||||
base=tmp_path/'official'/'src'/'router'/'routes'
|
||||
base.mkdir(parents=True)
|
||||
(base/'asyncRoutes.ts').write_text('export const asyncRoutes: MenuListType[] = [];',encoding='utf-8')
|
||||
(tmp_path/'official'/'package.json').write_text('{"name":"art-design-pro"}',encoding='utf-8')
|
||||
install(tmp_path/'official',dry_run=True)
|
||||
assert 'SITEFORGE_FACTORY' not in (base/'asyncRoutes.ts').read_text()
|
||||
install(tmp_path/'official',dry_run=False)
|
||||
before=(base/'asyncRoutes.ts').read_text()
|
||||
assert before.count('// SITEFORGE_FACTORY_V7_REGISTERED')==1
|
||||
assert 'SiteForgeMigration' in before
|
||||
install(tmp_path/'official',dry_run=False)
|
||||
assert (base/'asyncRoutes.ts').read_text()==before
|
||||
|
||||
|
||||
def test_new_site_missing_content_gets_noindex_scaffold(monkeypatch,tmp_path):
|
||||
from app import builder,quality
|
||||
from fastapi.testclient import TestClient
|
||||
monkeypatch.setattr(db,'DATA',tmp_path)
|
||||
monkeypatch.setattr(db,'DB_FILE',tmp_path/'scaffold.sqlite3')
|
||||
monkeypatch.setenv('SITEFORGE_ADMIN_PASSWORD','test-admin-password-for-scaffold')
|
||||
monkeypatch.setenv('SITEFORGE_SESSION_SECRET','test-session-secret-for-scaffold-extended')
|
||||
with TestClient(app) as c:
|
||||
assert c.post('/api/auth/login',json={'password':'test-admin-password-for-scaffold'}).status_code==200
|
||||
c.headers['X-SiteForge-CSRF']=c.get('/api/auth/me').json()['csrf']
|
||||
sid=c.post('/api/sites',json={'name':'New Company','theme':'automotive'}).json()['id']
|
||||
version=builder.build(sid)['version']
|
||||
root=tmp_path/'builds'/sid/version/'dist'
|
||||
assert (root/'products/index.html').is_file()
|
||||
assert (root/'brands/index.html').is_file()
|
||||
assert 'noindex' in (root/'brands/index.html').read_text(encoding='utf-8')
|
||||
assert quality.report(sid,version)['errors']==0
|
||||
Reference in New Issue
Block a user