"""V8.20 offline CMS compatibility tests, independent of the public network."""
import io
import json
import pytest
from app import wordpress_compat
from app.wp_complex_v820 import inspect_wxr, extract_acf
from app.wp_migration_v82 import _media_urls, _rewrite_media_html
from test_factory_v2 import factory,site
def wxr(records):
return (''''''+records+'').encode()
def entry(wp_id,link,custom='',body='',kind='page'):
return f'''Page {wp_id}{link.replace('&','&')}
{kind}{wp_id}publish{custom}'''
def meta(key,value):
return f'{key}'
def test_wxr_duplicate_route_and_dynamic_query_blocked():
data=wxr(entry(1,'https://old.example/products/a/')+entry(2,'https://old.example/products/a/')+entry(3,'https://old.example/?p=5'))
out=inspect_wxr(data)
assert out['unique_published_routes']==1
assert out['blocking_issues']==2
assert {x['code'] for x in out['issues']}=={'duplicate_route','invalid_permalink'}
assert out['ready_for_automatic_publish'] is False
def test_wxr_counts_attachments_and_custom_types():
data=wxr(entry(1,'https://old.example/a/',kind='attachment')+entry(2,'https://old.example/b/',kind='portfolio'))
out=inspect_wxr(data)
assert out['counts']['attachments']==1
assert out['features']['custom_post_types']==1
assert out['sample_routes'][0]['route']=='/b/'
def test_wxr_dynamic_shortcode_not_claimed_as_static():
data=wxr(entry(1,'https://old.example/contact/',body='[contact-form-7 id="4"]'))
out=inspect_wxr(data)
assert out['blocking_issues']==1
assert out['issues'][0]['code']=='dynamic_shortcode'
def test_acf_simple_repeater_explicit_field_references():
values={'_features':'field_parent','features':'2','features_0_title':'First','_features_0_title':'field_child',
'features_1_title':'Second','_features_1_title':'field_child','headline':'About','_headline':'field_header'}
parsed=extract_acf(values)
assert parsed['repeaters']['features']==[{'title':'First'},{'title':'Second'}]
assert parsed['fields']['headline']=='About'
assert any('核对' in s for s in parsed['warnings'])
def test_acf_serialized_does_not_execute_or_decode():
parsed=extract_acf({'_links':'field_test','links':'a:1:{i:0;s:4:"evil";}'})
assert 'links' not in parsed['fields']
assert any('序列化' in s for s in parsed['warnings'])
def test_acf_incomplete_repeater_kept_raw_only():
meta_values={'_list':'field_list','list':'2','_list_0_title':'field_title','list_0_title':'First'}
parsed=extract_acf(meta_values)
assert 'list' not in parsed['repeaters']
assert any('不连续' in x for x in parsed['warnings'])
def test_elementor_unknown_widget_reported():
data=json.dumps([{'elType':'widget','widgetType':'unknown_gallery','settings':{}},
{'elType':'widget','widgetType':'heading','settings':{'title':'A & B'}}])
out=wordpress_compat.elementor_to_html(data)
assert out['widgets']==2
assert 'A & B' in out['html']
assert any('unknown_gallery' in x for x in out['warnings'])
def test_css_remote_image_inventory_and_rewrite():
source={'body':'''
'''}
assert _media_urls(source)==['https://old.example/bg.webp','https://old.example/lazy.png']
converted={'https://old.example/bg.webp':'/media/a.webp','https://old.example/lazy.png':'/media/b.png'}
result,changed=_rewrite_media_html(source['body'],converted)
assert changed and '/media/a.webp' in result and '/media/b.png' in result
assert not _media_urls({'body':result})
def test_xxe_and_invalid_input_are_rejected():
with pytest.raises(ValueError):inspect_wxr(b'')
with pytest.raises(Exception):inspect_wxr(b']>&x;')
with pytest.raises(ValueError):inspect_wxr(b'')
def test_wxr_api_enforces_auth_and_csrf(factory):
sid=site(factory,mode='migration')
payload=wxr(entry(1,'https://old.example/about/'))
response=factory.post(f'/api/migrations/sites/{sid}/wordpress/wxr-preflight',files={'file':('site.xml',payload)})
assert response.status_code==200,response.text
assert response.json()['unique_published_routes']==1
assert response.json()['ready_for_automatic_publish'] is False
bad=factory.post(f'/api/migrations/sites/{sid}/wordpress/wxr-preflight',files={'file':('site.xml',b'')})
assert bad.status_code==400
def test_import_wxr_rejects_dynamic_path_before_writing(factory):
sid=site(factory,mode='migration')
payload=wxr(entry(1,'https://old.example/?p=13'))
response=factory.post(f'/api/sites/{sid}/import',files={'file':('site.xml',payload)})
assert response.status_code in (400,422)
assert not any(x['route']=='/' and x['title']=='Page 1' for x in factory.get(f'/api/sites/{sid}/entries').json())
def test_wxr_acf_repeater_survives_import(factory):
sid=site(factory,mode='migration')
custom=''.join(meta(k,v) for k,v in {'_features':'field_parent','features':'2',
'_features_0_name':'field_child','features_0_name':'SUV',
'_features_1_name':'field_child','features_1_name':'Pickup'}.items())
payload=wxr(entry(1,'https://old.example/products/a/',custom=custom))
response=factory.post(f'/api/sites/{sid}/import',files={'file':('site.xml',payload)})
assert response.status_code==200,response.text
item=next(x for x in factory.get(f'/api/sites/{sid}/entries').json() if x['route']=='/products/a/')
assert item['meta']['fields']['features']==[{'name':'SUV'},{'name':'Pickup'}]
assert item['meta']['acf_repeaters']=={'features':[{'name':'SUV'},{'name':'Pickup'}]}