105 lines
5.8 KiB
Python
105 lines
5.8 KiB
Python
|
|
"""V8.26 global chrome review, version snapshots, and safe restoration."""
|
|||
|
|
import copy
|
|||
|
|
import json
|
|||
|
|
from tests.test_factory import client, create
|
|||
|
|
from app import db, site_design_v87
|
|||
|
|
|
|||
|
|
|
|||
|
|
def config(marker='Global HESU Header'):
|
|||
|
|
data=copy.deepcopy(site_design_v87.DEFAULTS)
|
|||
|
|
data['header'].update({'announcement': marker, 'show_announcement': True})
|
|||
|
|
data['footer']['title']='HESU Footer'
|
|||
|
|
data['nav']=[{'title':'Products','route':'/products/','children':[
|
|||
|
|
{'title':'Vehicles','route':'/vehicles/','children':[]}]}]
|
|||
|
|
return data
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_human_review_and_reviewed_publication_are_build_isolated(client):
|
|||
|
|
site=create(client);url=f'/api/sites/{site}/site-design'
|
|||
|
|
old=db.one('SELECT settings_json FROM sites WHERE id=?',(site,))['settings_json']
|
|||
|
|
assert client.put(url,json={'config':config(), 'expected_revision':0}).status_code==200
|
|||
|
|
proof=client.get(url+'/review');assert proof.status_code==200,proof.text
|
|||
|
|
body=proof.json()
|
|||
|
|
assert {'header','nav','footer'}.issubset(set(body['changed_sections']))
|
|||
|
|
assert body['published_site_unchanged'] is True
|
|||
|
|
assert body['change_count']>=3
|
|||
|
|
assert db.one('SELECT settings_json FROM sites WHERE id=?',(site,))['settings_json']==old
|
|||
|
|
wrong=client.post(url+'/publish-reviewed',json={'expected_revision':1,'plan_token':'1'*64})
|
|||
|
|
assert wrong.status_code==409
|
|||
|
|
good=client.post(url+'/publish-reviewed',json={'expected_revision':1,'plan_token':body['plan_token']})
|
|||
|
|
assert good.status_code==200,good.text
|
|||
|
|
assert 'Global HESU Header' in db.one('SELECT settings_json FROM sites WHERE id=?',(site,))['settings_json']
|
|||
|
|
assert client.get(url+'/review').status_code==409
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_old_review_token_invalid_after_draft_edit(client):
|
|||
|
|
site=create(client);url=f'/api/sites/{site}/site-design'
|
|||
|
|
assert client.put(url,json={'config':config('First'), 'expected_revision':0}).status_code==200
|
|||
|
|
stale=client.get(url+'/review').json()
|
|||
|
|
assert client.put(url,json={'config':config('Second'), 'expected_revision':1}).status_code==200
|
|||
|
|
assert client.post(url+'/publish-reviewed',json={
|
|||
|
|
'expected_revision':2,'plan_token':stale['plan_token']}).status_code==409
|
|||
|
|
fresh=client.get(url+'/review').json()
|
|||
|
|
assert fresh['plan_token']!=stale['plan_token']
|
|||
|
|
assert client.post(url+'/publish-reviewed',json={
|
|||
|
|
'expected_revision':2,'plan_token':fresh['plan_token']}).status_code==200
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_restore_history_as_new_draft_does_not_mutate_live(client):
|
|||
|
|
site=create(client);url=f'/api/sites/{site}/site-design'
|
|||
|
|
assert client.put(url,json={'config':config('Release A'),'expected_revision':0}).status_code==200
|
|||
|
|
reviewed=client.get(url+'/review').json()
|
|||
|
|
pub=client.post(url+'/publish-reviewed',json={'expected_revision':1,'plan_token':reviewed['plan_token']}).json()
|
|||
|
|
pid=pub['publication_id']
|
|||
|
|
old_live=db.one('SELECT settings_json FROM sites WHERE id=?',(site,))['settings_json']
|
|||
|
|
history=client.get(url+f'/publications/{pid}');assert history.status_code==200
|
|||
|
|
assert history.json()['after']['header']['announcement']=='Release A'
|
|||
|
|
assert any(x['field']=='header.announcement' for x in history.json()['changes'])
|
|||
|
|
live_hash=client.get(url+'/live-hash').json()['live_hash']
|
|||
|
|
recovered=client.post(url+f'/publications/{pid}/restore',json={
|
|||
|
|
'snapshot':'before','expected_revision':0,'expected_live_hash':live_hash})
|
|||
|
|
assert recovered.status_code==200,recovered.text
|
|||
|
|
assert recovered.json()['published_unchanged']
|
|||
|
|
assert client.get(url).json()['revision']==1
|
|||
|
|
assert db.one('SELECT settings_json FROM sites WHERE id=?',(site,))['settings_json']==old_live
|
|||
|
|
assert client.post(url+f'/publications/{pid}/restore',json={
|
|||
|
|
'snapshot':'before','expected_revision':0,'expected_live_hash':live_hash}).status_code==409
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_restore_rejects_stale_live_site_and_foreign_history(client):
|
|||
|
|
a=create(client);b=create(client)
|
|||
|
|
ua=f'/api/sites/{a}/site-design';ub=f'/api/sites/{b}/site-design'
|
|||
|
|
assert client.put(ua,json={'config':config(), 'expected_revision':0}).status_code==200
|
|||
|
|
preview=client.get(ua+'/review').json()
|
|||
|
|
pid=client.post(ua+'/publish-reviewed',json={
|
|||
|
|
'expected_revision':1,'plan_token':preview['plan_token']}).json()['publication_id']
|
|||
|
|
old_hash=client.get(ua+'/live-hash').json()['live_hash']
|
|||
|
|
assert client.get(ub+f'/publications/{pid}').status_code==404
|
|||
|
|
assert client.post(ub+f'/publications/{pid}/restore',json={
|
|||
|
|
'snapshot':'after','expected_revision':0,'expected_live_hash':old_hash}).status_code==404
|
|||
|
|
current=db.one('SELECT settings_json FROM sites WHERE id=?',(a,))
|
|||
|
|
changed=json.loads(current['settings_json']);changed['external_setting']='changed'
|
|||
|
|
db.execute('UPDATE sites SET settings_json=? WHERE id=?',(json.dumps(changed),a))
|
|||
|
|
assert client.post(ua+f'/publications/{pid}/restore',json={
|
|||
|
|
'snapshot':'after','expected_revision':0,'expected_live_hash':old_hash}).status_code==409
|
|||
|
|
assert client.post(ua+f'/publications/{pid}/restore',json={
|
|||
|
|
'snapshot':'bad','expected_revision':0,'expected_live_hash':'a'*64}).status_code==400
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_nav_editor_is_visual_and_review_ui_is_wired(client):
|
|||
|
|
sid=create(client)
|
|||
|
|
entry=next(x for x in client.get(f'/api/sites/{sid}/entries').json() if x['route']=='/')
|
|||
|
|
page=client.get(f'/editor/{sid}/{entry["id"]}').text
|
|||
|
|
assert 'siteNavRows' in page and '+ 添加一级菜单' in page
|
|||
|
|
assert 'siteDesignReviewModal' in page and 'siteDesignHistoryModal' in page
|
|||
|
|
code=client.get('/ui/editor.js').text
|
|||
|
|
assert 'drawSiteNav()' in code and 'publish-reviewed' in code
|
|||
|
|
assert 'showSiteDesignHistory' in code
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_invalid_navigation_remains_rejected(client):
|
|||
|
|
sid=create(client);url=f'/api/sites/{sid}/site-design'
|
|||
|
|
bad=config();bad['nav'][0]['children'][0]['route']='javascript:alert(1)'
|
|||
|
|
assert client.put(url,json={'config':bad,'expected_revision':0}).status_code==400
|
|||
|
|
assert client.get(url).json()['revision']==0
|