wcs/tests/admin_pages/test_deprecations.py

649 lines
23 KiB
Python

import io
import json
import os
import zipfile
import pytest
from quixote.http_request import Upload as QuixoteUpload
from wcs import fields
from wcs.backoffice.deprecations import DeprecatedElementsDetected, DeprecationsScan
from wcs.blocks import BlockDef, BlockdefImportError
from wcs.carddef import CardDef
from wcs.data_sources import NamedDataSource, NamedDataSourceImportError
from wcs.formdef import FormDef, FormdefImportError
from wcs.mail_templates import MailTemplate
from wcs.qommon.form import UploadedFile
from wcs.qommon.http_request import HTTPRequest
from wcs.wf.create_formdata import Mapping
from wcs.wf.export_to_model import ExportToModel
from wcs.wf.external_workflow import ExternalWorkflowGlobalAction
from wcs.wf.form import WorkflowFormFieldsFormDef
from wcs.wf.geolocate import GeolocateWorkflowStatusItem
from wcs.wf.jump import JumpWorkflowStatusItem
from wcs.wf.notification import SendNotificationWorkflowStatusItem
from wcs.wf.redirect_to_url import RedirectToUrlWorkflowStatusItem
from wcs.workflows import Workflow, WorkflowBackofficeFieldsFormDef, WorkflowImportError
from wcs.wscalls import NamedWsCall, NamedWsCallImportError
from ..utilities import clean_temporary_pub, create_temporary_pub, get_app, login
from .test_all import create_superuser
@pytest.fixture
def pub(request):
pub = create_temporary_pub()
req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'})
pub.set_app_dir(req)
pub.cfg['identification'] = {'methods': ['password']}
pub.cfg['language'] = {'language': 'en'}
pub.write_cfg()
if os.path.exists(os.path.join(pub.app_dir, 'deprecations.json')):
os.remove(os.path.join(pub.app_dir, 'deprecations.json'))
BlockDef.wipe()
CardDef.wipe()
FormDef.wipe()
MailTemplate.wipe()
NamedDataSource.wipe()
NamedWsCall.wipe()
Workflow.wipe()
return pub
def teardown_module(module):
clean_temporary_pub()
def test_no_deprecations(pub):
create_superuser(pub)
app = login(get_app(pub))
# first time, it's a redirect to the scanning job
resp = app.get('/backoffice/studio/deprecations/', status=302)
resp = resp.follow()
resp = resp.click('Go to deprecation report')
# second time, the page stays on
resp = app.get('/backoffice/studio/deprecations/', status=200)
assert 'No deprecated items were found on this site.' in resp.text
def test_deprecations(pub):
create_superuser(pub)
formdef = FormDef()
formdef.name = 'foobar'
formdef.fields = [
fields.PageField(id='1', label='page1', condition={'type': 'python', 'value': 'True'}),
fields.StringField(id='2', label='python_prefill', prefill={'type': 'formula', 'value': '1 + 2'}),
fields.StringField(
id='3', label='ezt_prefill', prefill={'type': 'string', 'value': '[form_var_test]'}
),
fields.StringField(id='4', label='jsonp_data', data_source={'type': 'jsonp', 'value': 'xxx'}),
fields.StringField(id='5', label='ezt_in_datasource', data_source={'type': 'json', 'value': '[xxx]'}),
fields.CommentField(id='6', label='[ezt] in label'),
fields.CommentField(id='7', label='{{script.usage}} in template'),
fields.PageField(
id='10',
label='page2',
post_conditions=[
{'condition': {'type': 'python', 'value': 'False'}, 'error_message': 'You shall not pass.'}
],
),
fields.TableField(id='8', label='table field'),
fields.RankedItemsField(id='9', label='ranked field'),
]
formdef.store()
workflow = Workflow(name='test')
workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow)
workflow.backoffice_fields_formdef.fields = [
fields.TableField(id='bo1', label='table field'),
]
st0 = workflow.add_status('Status0', 'st0')
display = st0.add_action('displaymsg')
display.message = 'message with [ezt] info'
wscall = st0.add_action('webservice_call', id='_wscall')
wscall.varname = 'xxx'
wscall.url = 'http://remote.example.net/xml'
wscall.post_data = {'str': 'abcd', 'evalme': '=form_number'}
sendsms = st0.add_action('sendsms', id='_sendsms')
sendsms.to = 'xxx'
sendsms.condition = {'type': 'python', 'value': 'True'}
sendsms.parent = st0
st0.items.append(sendsms)
item = st0.add_action('set-backoffice-fields', id='_item')
item.fields = [{'field_id': 'bo1', 'value': '=form_var_foo'}]
create_formdata = st0.add_action('create_formdata', id='_create_formdata')
create_formdata.varname = 'resubmitted'
create_formdata.mappings = [
Mapping(field_id='0', expression='=form_var_toto_string'),
]
item = st0.add_action('update_user_profile', id='_item2')
item.fields = [{'field_id': '__email', 'value': '=form_var_foo'}]
sendmail = st0.add_action('sendmail', id='_sendmail')
sendmail.to = ['=plop']
sendmail = st0.add_action('sendmail', id='_sendmail2')
sendmail.attachments = ['python']
display_form = st0.add_action('form', id='_x')
display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
display_form.formdef.fields.append(
fields.StringField(id='0', label='Test', prefill={'type': 'formula', 'value': '1 + 2'})
)
export_to = st0.add_action('export_to_model', id='_export_to')
export_to.convert_to_pdf = False
export_to.label = 'create doc'
upload = QuixoteUpload('/foo/test.rtf', content_type='application/rtf')
upload.fp = io.BytesIO()
upload.fp.write(b'HELLO WORLD')
upload.fp.seek(0)
export_to.model_file = UploadedFile(pub.app_dir, None, upload)
export_to.by = ['_submitter']
timeout_jump = st0.add_action('jump')
timeout_jump.timeout = '213'
timeout_jump.condition = {'type': 'python', 'value': 'True'}
for klass in (
ExportToModel,
ExternalWorkflowGlobalAction,
GeolocateWorkflowStatusItem,
JumpWorkflowStatusItem,
SendNotificationWorkflowStatusItem,
RedirectToUrlWorkflowStatusItem,
):
action = klass()
action.parent = st0
st0.items.append(action)
st0.add_action('aggregationemail')
global_action = workflow.add_global_action('global')
trigger = global_action.append_trigger('timeout')
trigger.anchor = 'python'
trigger.anchor_expression = 'form_var_date'
workflow.store()
data_source = NamedDataSource(name='ds_python')
data_source.data_source = {'type': 'formula', 'value': repr([('1', 'un'), ('2', 'deux')])}
data_source.store()
data_source = NamedDataSource(name='ds_jsonp')
data_source.data_source = {'type': 'jsonp', 'value': 'xxx'}
data_source.store()
data_source = NamedDataSource(name='ds_csv')
data_source.data_source = {'type': 'json', 'value': 'http://example.net/csvdatasource/plop/test'}
data_source.store()
NamedWsCall.wipe()
wscall = NamedWsCall()
wscall.name = 'Hello'
wscall.request = {'url': 'http://example.net', 'qs_data': {'a': '=1+2'}}
wscall.store()
wscall = NamedWsCall()
wscall.name = 'Hello CSV'
wscall.request = {'url': 'http://example.net/csvdatasource/plop/test'}
wscall.store()
wscall = NamedWsCall()
wscall.name = 'Hello json data store'
wscall.request = {'url': 'http://example.net/jsondatastore/plop'}
wscall.store()
MailTemplate.wipe()
mail_template1 = MailTemplate()
mail_template1.name = 'Hello1'
mail_template1.subject = 'plop'
mail_template1.body = 'plop'
mail_template1.attachments = ['form_attachments.plop']
mail_template1.store()
mail_template2 = MailTemplate()
mail_template2.name = 'Hello2'
mail_template2.subject = 'plop'
mail_template2.body = 'plop [ezt] plop'
mail_template2.store()
app = login(get_app(pub))
resp = app.get('/backoffice/studio/deprecations/', status=302)
resp = resp.follow()
resp = resp.click('Go to deprecation report')
assert [x.text for x in resp.pyquery('.section--ezt li a')] == [
'foobar / Field "ezt_prefill"',
'foobar / Field "ezt_in_datasource"',
'foobar / Field "[ezt] in label"',
'test / Alert',
'Mail Template "Hello2"',
]
assert [x.text for x in resp.pyquery('.section--jsonp li a')] == [
'foobar / Field "jsonp_data"',
'Data source "ds_jsonp"',
]
assert [x.text for x in resp.pyquery('.section--python-data-source li a')] == ['Data source "ds_python"']
assert [x.text for x in resp.pyquery('.section--python-condition li a')] == [
'foobar / Field "page1"',
'foobar / Field "page2"',
'test / SMS',
'test / Automatic Jump',
]
assert [x.text for x in resp.pyquery('.section--python-condition li.important a')] == [
'test / Automatic Jump',
]
assert [x.text for x in resp.pyquery('.section--python-prefill li a')] == [
'foobar / Field "python_prefill"',
'Form action in workflow "test" / Field "Test"',
]
assert [x.text for x in resp.pyquery('.section--python-expression li a')] == [
'test / Webservice',
'test / Backoffice Data',
'test / New Form Creation',
'test / User Profile Update',
'test / Email',
'test / Email',
'test / trigger in global',
'Webservice "Hello"',
'Mail Template "Hello1"',
]
assert [x.text for x in resp.pyquery('.section--script li a')] == [
'foobar / Field "{{script.usage}} in template"'
]
assert [x.text for x in resp.pyquery('.section--rtf li a')] == [
'test / Document Creation',
]
assert [x.text for x in resp.pyquery('.section--fields li a')] == [
'foobar / Field "table field"',
'foobar / Field "ranked field"',
'Backoffice fields of workflow "test" / Field "table field"',
]
assert [x.text for x in resp.pyquery('.section--actions li a')] == [
'test / Daily Summary Email',
]
assert [x.text for x in resp.pyquery('.section--csv-connector li a')] == [
'Data source "ds_csv"',
'Webservice "Hello CSV"',
]
assert [x.text for x in resp.pyquery('.section--json-data-store li a')] == [
'Webservice "Hello json data store"',
]
# check all links are ok
for link in resp.pyquery('.section li a'):
resp.click(href=link.attrib['href'], index=0)
def test_deprecations_choice_label(pub):
MailTemplate.wipe()
# check choice labels are not considered as EZT
workflow = Workflow(name='test')
st0 = workflow.add_status('Status0', 'st0')
accept = st0.add_action('choice', id='_choice')
accept.label = '[test] action'
job = DeprecationsScan()
job.execute()
assert not job.report_lines
def test_deprecations_skip_invalid_ezt(pub):
workflow = Workflow(name='test')
st0 = workflow.add_status('Status0', 'st0')
display = st0.add_action('displaymsg')
display.message = 'message with invalid [if-any] ezt'
job = DeprecationsScan()
job.execute()
assert not job.report_lines
def test_deprecations_ignore_ezt_looking_tag(pub):
workflow = Workflow(name='test')
st0 = workflow.add_status('Status0', 'st0')
sendmail = st0.add_action('sendmail')
sendmail.subject = '[REMINDER] your appointment'
workflow.store()
job = DeprecationsScan()
job.execute()
assert not job.report_lines
sendmail.subject = '[reminder]'
workflow.store()
job = DeprecationsScan()
job.execute()
assert job.report_lines
sendmail.subject = '[if-any plop]test[end]'
workflow.store()
job = DeprecationsScan()
job.execute()
assert job.report_lines
def test_deprecations_cronjob(pub):
assert not os.path.exists(os.path.join(pub.app_dir, 'deprecations.json'))
pub.update_deprecations_report()
assert os.path.exists(os.path.join(pub.app_dir, 'deprecations.json'))
def test_deprecations_document_models(pub):
create_superuser(pub)
workflow = Workflow(name='test')
st0 = workflow.add_status('Status0', 'st0')
export_to = st0.add_action('export_to_model')
export_to.convert_to_pdf = False
export_to.label = 'create doc'
upload = QuixoteUpload('test.rtf', content_type='text/rtf')
upload.fp = io.BytesIO()
upload.fp.write(b'{\\rtf foo [form_var_plop] bar')
upload.fp.seek(0)
export_to.model_file = UploadedFile(pub.app_dir, None, upload)
export_to.by = ['_submitter']
export_to2 = st0.add_action('export_to_model')
export_to2.convert_to_pdf = False
export_to2.label = 'create doc2'
upload = QuixoteUpload('test.odt', content_type='application/vnd.oasis.opendocument.text')
upload.fp = io.BytesIO()
with zipfile.ZipFile(upload.fp, mode='w') as zout:
content = '''<?xml version="1.0" encoding="UTF-8"?>
<office:document-content
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"
xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"
xmlns:xlink="http://www.w3.org/1999/xlink"
office:version="1.2">
<office:body>
<office:text>
<text:sequence-decls>
<text:sequence-decl text:display-outline-level="0" text:name="Illustration"/>
<text:sequence-decl text:display-outline-level="0" text:name="Table"/>
<text:sequence-decl text:display-outline-level="0" text:name="Text"/>
<text:sequence-decl text:display-outline-level="0" text:name="Drawing"/>
</text:sequence-decls>
<text:user-field-decls>
<text:user-field-decl office:value-type="string" office:string-value="{{ form_name }}"/>
</text:user-field-decls>
<text:p text:style-name="P1">Hello.</text:p>
<text:p text:style-name="P2">
<draw:frame draw:style-name="fr1" draw:name="=form_var_image_raw"
text:anchor-type="paragraph" svg:width="1.764cm" svg:height="1.764cm" draw:z-index="0">
<draw:image xlink:href="Pictures/10000000000000320000003276E9D46581B55C88.jpg"
xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad"/>
</draw:frame>
</text:p>
</office:text>
</office:body>
</office:document-content>
'''
zout.writestr('content.xml', content)
upload.fp.seek(0)
export_to2.model_file = UploadedFile(pub.app_dir, None, upload)
export_to2.by = ['_submitter']
workflow.store()
job = DeprecationsScan()
job.execute()
assert job.report_lines == [
{
'category': 'ezt',
'location_label': 'test / Document Creation',
'source': 'workflow:1',
'url': 'http://example.net/backoffice/workflows/1/status/st0/items/1/',
},
{
'category': 'rtf',
'location_label': 'test / Document Creation',
'source': 'workflow:1',
'url': 'http://example.net/backoffice/workflows/1/status/st0/items/1/',
},
{
'category': 'python-expression',
'location_label': 'test / Document Creation',
'source': 'workflow:1',
'url': 'http://example.net/backoffice/workflows/1/status/st0/items/2/',
},
]
def test_deprecations_inspect_pages(pub):
formdef = FormDef()
formdef.name = 'foobar'
formdef.fields = [
fields.PageField(id='1', label='page1', condition={'type': 'python', 'value': 'True'}),
]
formdef.store()
block = BlockDef()
block.name = 'foobar'
block.fields = [
fields.StringField(id='2', label='python_prefill', prefill={'type': 'formula', 'value': '1 + 2'}),
]
block.store()
workflow = Workflow(name='test')
workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow)
workflow.backoffice_fields_formdef.fields = [
fields.TableField(id='bo1', label='table field'),
]
st0 = workflow.add_status('Status0', 'st0')
display = st0.add_action('displaymsg')
display.message = 'message with [ezt] info'
workflow.store()
job = DeprecationsScan()
job.execute()
create_superuser(pub)
app = login(get_app(pub))
resp = app.get(formdef.get_admin_url() + 'inspect')
assert 'Deprecations' in resp.text
resp = app.get(block.get_admin_url() + 'inspect')
assert 'Deprecations' in resp.text
resp = app.get(workflow.get_admin_url() + 'inspect')
assert 'Deprecations' in resp.text
# check there's no deprecation tab in snapshots
snapshot = pub.snapshot_class.get_latest('formdef', formdef.id)
resp = app.get(formdef.get_admin_url() + f'history/{snapshot.id}/inspect')
assert 'Deprecations' not in resp.text
snapshot = pub.snapshot_class.get_latest('block', block.id)
resp = app.get(block.get_admin_url() + f'history/{snapshot.id}/inspect')
assert 'Deprecations' not in resp.text
snapshot = pub.snapshot_class.get_latest('workflow', workflow.id)
resp = app.get(workflow.get_admin_url() + f'history/{snapshot.id}/inspect')
assert 'Deprecations' not in resp.text
# check there's no deprecation tab if there's nothing deprecated
formdef.fields[0].condition = None
formdef.store()
block.fields[0].prefill = None
block.store()
workflow.backoffice_fields_formdef = None
display.message = 'message with {{django}} info'
workflow.store()
job = DeprecationsScan()
job.execute()
resp = app.get(formdef.get_admin_url() + 'inspect')
assert 'Deprecations' not in resp.text
resp = app.get(block.get_admin_url() + 'inspect')
assert 'Deprecations' not in resp.text
resp = app.get(workflow.get_admin_url() + 'inspect')
assert 'Deprecations' not in resp.text
def test_deprecations_inspect_pages_old_format(pub):
formdef = FormDef()
formdef.name = 'foobar'
formdef.fields = [
fields.PageField(id='1', label='page1', condition={'type': 'python', 'value': 'True'}),
]
formdef.store()
job = DeprecationsScan()
job.execute()
with open(os.path.join(pub.app_dir, 'deprecations.json')) as f:
deprecations_json = json.loads(f.read())
del deprecations_json['report_lines'][0]['source']
with open(os.path.join(pub.app_dir, 'deprecations.json'), 'w') as f:
f.write(json.dumps(deprecations_json))
create_superuser(pub)
app = login(get_app(pub))
resp = app.get(formdef.get_admin_url() + 'inspect')
assert 'Deprecations' not in resp.text
resp = app.get('/backoffice/studio/deprecations/')
assert resp.pyquery('.section--python-condition li a')
def test_deprecations_on_import(pub):
formdef = FormDef()
formdef.name = 'foobar'
formdef.fields = [
fields.PageField(id='1', label='page1', condition={'type': 'python', 'value': 'True'}),
]
formdef.store()
blockdef = BlockDef()
blockdef.name = 'foobar'
blockdef.fields = [
fields.StringField(id='2', label='python_prefill', prefill={'type': 'formula', 'value': '1 + 2'}),
]
blockdef.store()
workflow = Workflow(name='test')
st0 = workflow.add_status('Status0', 'st0')
sendsms = st0.add_action('sendsms', id='_sendsms')
sendsms.to = 'xxx'
sendsms.condition = {'type': 'python', 'value': 'True'}
sendsms.parent = st0
st0.items.append(sendsms)
workflow.store()
data_source = NamedDataSource(name='ds_python')
data_source.data_source = {'type': 'formula', 'value': repr([('1', 'un'), ('2', 'deux')])}
data_source.store()
wscall = NamedWsCall()
wscall.name = 'Hello'
wscall.request = {'url': 'http://example.net', 'qs_data': {'a': '=1+2'}}
wscall.store()
mail_template = MailTemplate() # no python expression in mail templates
mail_template.name = 'Hello2'
mail_template.subject = 'plop'
mail_template.body = 'plop [ezt] plop'
mail_template.store()
job = DeprecationsScan()
job.check_deprecated_elements_in_object(formdef)
formdef_xml = formdef.export_to_xml()
FormDef.import_from_xml_tree(formdef_xml)
job = DeprecationsScan()
job.check_deprecated_elements_in_object(blockdef)
blockdef_xml = blockdef.export_to_xml()
BlockDef.import_from_xml_tree(blockdef_xml)
job = DeprecationsScan()
job.check_deprecated_elements_in_object(workflow)
workflow_xml = workflow.export_to_xml()
Workflow.import_from_xml_tree(workflow_xml)
job = DeprecationsScan()
job.check_deprecated_elements_in_object(data_source)
data_source_xml = data_source.export_to_xml()
NamedDataSource.import_from_xml_tree(data_source_xml)
job = DeprecationsScan()
job.check_deprecated_elements_in_object(wscall)
wscall_xml = wscall.export_to_xml()
NamedWsCall.import_from_xml_tree(wscall_xml)
job = DeprecationsScan()
job.check_deprecated_elements_in_object(mail_template)
mail_template_xml = mail_template.export_to_xml()
MailTemplate.import_from_xml_tree(mail_template_xml)
if not pub.site_options.has_section('options'):
pub.site_options.add_section('options')
pub.site_options.set('options', 'forbid-new-python-expressions', 'true')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
job = DeprecationsScan()
with pytest.raises(DeprecatedElementsDetected) as excinfo:
job.check_deprecated_elements_in_object(formdef)
assert str(excinfo.value) == 'Python expression detected'
with pytest.raises(FormdefImportError) as excinfo:
FormDef.import_from_xml_tree(formdef_xml)
assert str(excinfo.value) == 'Python expression detected'
job = DeprecationsScan()
with pytest.raises(DeprecatedElementsDetected) as excinfo:
job.check_deprecated_elements_in_object(blockdef)
assert str(excinfo.value) == 'Python expression detected'
with pytest.raises(BlockdefImportError) as excinfo:
BlockDef.import_from_xml_tree(blockdef_xml)
assert str(excinfo.value) == 'Python expression detected'
job = DeprecationsScan()
with pytest.raises(DeprecatedElementsDetected) as excinfo:
job.check_deprecated_elements_in_object(workflow)
assert str(excinfo.value) == 'Python expression detected'
with pytest.raises(WorkflowImportError) as excinfo:
Workflow.import_from_xml_tree(workflow_xml)
assert str(excinfo.value) == 'Python expression detected'
job = DeprecationsScan()
with pytest.raises(DeprecatedElementsDetected) as excinfo:
job.check_deprecated_elements_in_object(data_source)
assert str(excinfo.value) == 'Python expression detected'
with pytest.raises(NamedDataSourceImportError) as excinfo:
NamedDataSource.import_from_xml_tree(data_source_xml)
assert str(excinfo.value) == 'Python expression detected'
job = DeprecationsScan()
with pytest.raises(DeprecatedElementsDetected) as excinfo:
job.check_deprecated_elements_in_object(wscall)
assert str(excinfo.value) == 'Python expression detected'
with pytest.raises(NamedWsCallImportError) as excinfo:
NamedWsCall.import_from_xml_tree(wscall_xml)
assert str(excinfo.value) == 'Python expression detected'
# no python expressions
job = DeprecationsScan()
job.check_deprecated_elements_in_object(mail_template)
MailTemplate.import_from_xml_tree(mail_template_xml)