From d81959f81f8b88037019566878004bcf06f5c217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Sat, 18 Jan 2020 20:33:44 +0100 Subject: [PATCH] trivial: insure two blank lines before top functions and classes --- doc/scripts/rst2latex.py | 1 + setup.py | 1 + tests/conftest.py | 1 + tests/robot/WcsRobotFrameworkLibrary.py | 2 + tests/test_admin_pages.py | 158 ++++++++++++++++ tests/test_api.py | 63 +++++++ tests/test_auth_pages.py | 6 + tests/test_backoffice_pages.py | 112 +++++++++++ tests/test_carddef.py | 4 + tests/test_categories.py | 2 + tests/test_ctl.py | 13 ++ tests/test_datasource.py | 14 ++ tests/test_ezt.py | 20 ++ tests/test_fields.py | 26 +++ tests/test_form_pages.py | 175 ++++++++++++++++++ tests/test_formdata.py | 49 +++++ tests/test_formdef.py | 13 ++ tests/test_formdef_import.py | 32 ++++ tests/test_hobo.py | 9 + tests/test_hobo_notify.py | 7 + tests/test_misc.py | 34 ++++ tests/test_prefill.py | 12 ++ tests/test_publisher.py | 11 ++ tests/test_register.py | 15 ++ tests/test_role.py | 6 + tests/test_rootdirectory.py | 9 + tests/test_saml2utils.py | 3 + tests/test_saml_auth.py | 18 ++ tests/test_sessions.py | 8 + tests/test_sql.py | 74 ++++++++ tests/test_storage.py | 19 ++ tests/test_strftime.py | 2 + tests/test_templates.py | 15 ++ tests/test_texts.py | 6 + tests/test_tracking_code.py | 5 + tests/test_users.py | 1 + tests/test_variadic_url.py | 3 + tests/test_widgets.py | 20 ++ tests/test_workflow_import.py | 26 +++ tests/test_workflows.py | 65 +++++++ tests/test_wscall.py | 3 + tests/test_x509utils.py | 1 + tests/utilities.py | 4 + wcs/admin/categories.py | 1 + wcs/admin/data_sources.py | 1 + wcs/admin/fields.py | 1 + wcs/admin/forms.py | 1 + wcs/admin/logged_errors.py | 1 + wcs/admin/roles.py | 1 + wcs/admin/settings.py | 1 + wcs/admin/users.py | 2 + wcs/admin/workflows.py | 7 + wcs/admin/wscalls.py | 1 + wcs/api.py | 3 + wcs/api_utils.py | 1 + wcs/backoffice/management.py | 2 + wcs/cache.py | 1 + wcs/categories.py | 1 + wcs/compat.py | 1 + wcs/context_processors.py | 2 + wcs/ctl/check_hobos.py | 2 + wcs/ctl/export_settings.py | 1 + wcs/ctl/management/commands/trigger_jumps.py | 5 + wcs/ctl/rebuild_indexes.py | 1 + wcs/ctl/runscript.py | 1 + wcs/ctl/shell.py | 1 + wcs/ctl/wipe_data.py | 1 + wcs/data_sources.py | 1 + wcs/fields.py | 3 + wcs/formdata.py | 1 + wcs/forms/backoffice.py | 1 + wcs/forms/root.py | 4 + wcs/logged_errors.py | 1 + wcs/monkeypatch.py | 12 ++ wcs/myspace.py | 1 + wcs/publisher.py | 1 + wcs/qommon/__init__.py | 2 + wcs/qommon/admin/cfg.py | 1 + wcs/qommon/admin/emails.py | 1 + wcs/qommon/admin/logger.py | 1 + wcs/qommon/admin/menu.py | 4 + wcs/qommon/admin/settings.py | 1 + wcs/qommon/afterjobs.py | 1 + wcs/qommon/backoffice/listing.py | 1 + wcs/qommon/backoffice/menu.py | 1 + wcs/qommon/backoffice/root.py | 1 + wcs/qommon/cron.py | 2 + wcs/qommon/ctl.py | 2 + wcs/qommon/emails.py | 7 + wcs/qommon/errors.py | 8 + wcs/qommon/evalutils.py | 4 + wcs/qommon/ezt.py | 16 ++ wcs/qommon/form.py | 18 ++ wcs/qommon/http_request.py | 1 + wcs/qommon/http_response.py | 1 + wcs/qommon/humantime.py | 4 + wcs/qommon/ident/__init__.py | 6 + wcs/qommon/ident/base.py | 1 + wcs/qommon/ident/idp.py | 4 + wcs/qommon/ident/password.py | 2 + wcs/qommon/logger.py | 1 + .../management/commands/collectstatic.py | 1 + .../management/commands/makemessages.py | 1 + wcs/qommon/management/commands/migrate.py | 1 + .../management/commands/migrate_schemas.py | 1 + wcs/qommon/misc.py | 29 +++ wcs/qommon/pages.py | 1 + wcs/qommon/publisher.py | 4 + wcs/qommon/saml2.py | 4 + wcs/qommon/saml2utils.py | 1 + wcs/qommon/sessions.py | 3 + wcs/qommon/sms.py | 2 + wcs/qommon/storage.py | 19 ++ wcs/qommon/template.py | 7 + wcs/qommon/templatetags/qommon.py | 33 ++++ wcs/qommon/tokens.py | 1 + wcs/qommon/vendor/locket.py | 3 + wcs/qommon/x509utils.py | 8 + wcs/qommon/xml_storage.py | 1 + wcs/roles.py | 1 + wcs/root.py | 1 + wcs/scripts.py | 1 + wcs/sessions.py | 1 + wcs/sql.py | 42 +++++ wcs/tracking_code.py | 1 + wcs/users.py | 1 + wcs/utils.py | 1 + wcs/wf/aggregation_email.py | 1 + wcs/wf/anonymise.py | 1 + wcs/wf/attachment.py | 1 + wcs/wf/criticality.py | 1 + wcs/wf/form.py | 1 + wcs/wf/geolocate.py | 1 + wcs/wf/jump.py | 2 + wcs/wf/profile.py | 1 + wcs/wf/register_comment.py | 1 + wcs/wf/remove.py | 1 + wcs/wf/timeout_jump.py | 1 + wcs/wf/wscall.py | 1 + wcs/workflows.py | 5 + wcs/wscalls.py | 2 + 141 files changed, 1413 insertions(+) diff --git a/doc/scripts/rst2latex.py b/doc/scripts/rst2latex.py index 4036fbfce..468580576 100755 --- a/doc/scripts/rst2latex.py +++ b/doc/scripts/rst2latex.py @@ -10,6 +10,7 @@ except: from docutils.core import publish_cmdline, Publisher + def set_io(self, source_path=None, destination_path=None): Publisher.set_io_orig(self, source_path, destination_path='/dev/null') diff --git a/setup.py b/setup.py index e4a8ff60b..9af47e08f 100644 --- a/setup.py +++ b/setup.py @@ -75,6 +75,7 @@ def data_tree(destdir, sourcedir): dirs.remove(vcs_dirname) return r + def get_version(): '''Use the VERSION, if absent generates a version with git describe, if not tag exists, take 0.0- and add the length of the commit log. diff --git a/tests/conftest.py b/tests/conftest.py index 9e076a26d..aa6ff50a3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,6 +5,7 @@ import pytest from utilities import EmailsMocking, SMSMocking, HttpRequestsMocking + def pytest_addoption(parser): parser.addoption('--without-postgresql-tests', action='store_true', help='disable tests requiring postgresql') diff --git a/tests/robot/WcsRobotFrameworkLibrary.py b/tests/robot/WcsRobotFrameworkLibrary.py index 86ffa0ad0..ec280e16f 100644 --- a/tests/robot/WcsRobotFrameworkLibrary.py +++ b/tests/robot/WcsRobotFrameworkLibrary.py @@ -31,6 +31,7 @@ from robot.libraries.BuiltIn import BuiltIn from Selenium2Library.locators import ElementFinder as BaseElementFinder from Selenium2Library import utils + class ElementFinder(BaseElementFinder): def __init__(self): BaseElementFinder.__init__(self) @@ -48,6 +49,7 @@ class ElementFinder(BaseElementFinder): return self._find_by_xpath(browser, criteria, tag, constraints) return [] + class WcsRobotFrameworkLibrary: def __init__(self, port=10003): self.port = port diff --git a/tests/test_admin_pages.py b/tests/test_admin_pages.py index 163e77791..85f60c414 100644 --- a/tests/test_admin_pages.py +++ b/tests/test_admin_pages.py @@ -51,10 +51,12 @@ from wcs import fields from utilities import get_app, login, create_temporary_pub, clean_temporary_pub, HttpRequestsMocking + def pytest_generate_tests(metafunc): if 'pub' in metafunc.fixturenames: metafunc.parametrize('pub', ['pickle', 'sql', 'pickle-templates'], indirect=True) + @pytest.fixture def pub(request): pub = create_temporary_pub( @@ -70,6 +72,7 @@ def pub(request): return pub + def create_superuser(pub): if pub.user_class.select(lambda x: x.name == 'admin'): user1 = pub.user_class.select(lambda x: x.name == 'admin')[0] @@ -88,15 +91,18 @@ def create_superuser(pub): return user1 + def create_role(): Role.wipe() role = Role(name='foobar') role.store() return role + def teardown_module(module): clean_temporary_pub() + def test_empty_site(pub): resp = get_app(pub).get('/backoffice/') resp = resp.click('Users', index=0) @@ -104,27 +110,32 @@ def test_empty_site(pub): resp = get_app(pub).get('/backoffice/') resp = resp.click('Settings', index=0) + def test_empty_site_but_idp_settings(pub): pub.cfg['idp'] = {'xxx': {}} pub.write_cfg() resp = get_app(pub).get('/backoffice/') assert resp.location == 'http://example.net/login/?next=http%3A%2F%2Fexample.net%2Fbackoffice%2F' + def test_with_user(pub): create_superuser(pub) resp = get_app(pub).get('/backoffice/', status=302) assert resp.location == 'http://example.net/login/?next=http%3A%2F%2Fexample.net%2Fbackoffice%2F' + def test_with_superuser(pub): create_superuser(pub) app = login(get_app(pub)) app.get('/backoffice/') + def test_admin_redirect(pub): create_superuser(pub) app = login(get_app(pub)) assert app.get('/admin/whatever', status=302).location == 'http://example.net/backoffice/whatever' + def test_admin_for_all(pub): user = create_superuser(pub) role = create_role() @@ -177,6 +188,7 @@ def test_admin_for_all(pub): user.is_admin = True user.store() + def test_forms(pub): user = create_superuser(pub) app = login(get_app(pub)) @@ -184,6 +196,7 @@ def test_forms(pub): assert 'You first have to define roles.' in resp.text assert not 'New Form' in resp.text + def test_forms_new(pub): user = create_superuser(pub) app = login(get_app(pub)) @@ -207,6 +220,7 @@ def test_forms_new(pub): assert formdef.disabled == True assert formdef.last_modification_user_id == str(user.id) + def test_forms_new_popup(pub): FormDef.wipe() user = create_superuser(pub) @@ -232,11 +246,13 @@ def test_forms_new_popup(pub): assert formdef.disabled == True assert formdef.last_modification_user_id == str(user.id) + def assert_option_display(resp, label, value): option_line = re.findall('%s.*%s' % (label, value), resp.text, re.DOTALL) assert option_line assert not '' in option_line + def test_forms_edit(pub): create_superuser(pub) create_role() @@ -364,6 +380,7 @@ def test_forms_edit(pub): assert_option_display(resp, 'Geolocation', 'Disabled') assert FormDef.get(formdef.id).geolocations is None + def test_form_title_change(pub): create_superuser(pub) create_role() @@ -416,6 +433,7 @@ def test_form_title_change(pub): resp = resp.form.submit().follow() assert FormDef.get(formdef2.id).url_name == 'foobar' + def test_form_category(pub): create_superuser(pub) create_role() @@ -439,6 +457,7 @@ def test_form_category(pub): assert 'Category' in resp.text assert_option_display(resp, 'Category', 'None') + def test_form_category_select(pub): create_superuser(pub) create_role() @@ -467,6 +486,7 @@ def test_form_category_select(pub): resp = resp.forms[0].submit('submit') assert FormDef.get(formdef.id).category_id == cat.id + def test_form_workflow(pub): create_superuser(pub) create_role() @@ -490,6 +510,7 @@ def test_form_workflow(pub): resp = app.get('/backoffice/forms/1/') assert_option_display(resp, 'Workflow', 'Default') + def test_form_workflow_change(pub): create_superuser(pub) create_role() @@ -520,6 +541,7 @@ def test_form_workflow_change(pub): resp = resp.forms[0].submit('submit') assert FormDef.get(formdef.id).workflow_id == workflow.id + def test_form_workflow_link(pub): create_superuser(pub) create_role() @@ -551,6 +573,7 @@ def test_form_workflow_link(pub): resp = app.get('/backoffice/forms/%s/' % formdef.id) assert '../workflows/%s/' % workflow.id not in resp.text + def test_form_workflow_remapping(pub): create_superuser(pub) create_role() @@ -616,6 +639,7 @@ def test_form_workflow_remapping(pub): assert data_class.get(formdata1.id).status == 'wf-finished' assert data_class.get(formdata2.id).status == 'draft' + def test_form_submitter_roles(pub): create_superuser(pub) role = create_role() @@ -661,6 +685,7 @@ def test_form_submitter_roles(pub): with pytest.raises(ValueError): resp.form['roles$element0'] = str(role2.id) + def test_form_workflow_role(pub): create_superuser(pub) role = create_role() @@ -688,6 +713,7 @@ def test_form_workflow_role(pub): resp = app.get('/backoffice/forms/1/') resp = resp.click(href='role/_receiver') + def test_form_workflow_options(pub): create_superuser(pub) create_role() @@ -708,6 +734,7 @@ def test_form_workflow_options(pub): resp = app.get('/backoffice/forms/1/') assert '"workflow-options"' in resp.text + def test_form_workflow_variables(pub): create_superuser(pub) create_role() @@ -761,6 +788,7 @@ def test_form_workflow_variables(pub): resp = resp.form.submit() assert time.strftime('%d %m %y', FormDef.get(formdef.id).workflow_options.get('test2')) == '17 06 16' + def test_form_workflow_table_variables(pub): create_superuser(pub) create_role() @@ -807,6 +835,7 @@ def test_form_workflow_table_variables(pub): assert resp.form['f1$element0$col0'].value == 'foobar' assert resp.form['f2'].value == 'foobar' + def test_form_roles(pub): create_superuser(pub) role = create_role() @@ -829,6 +858,7 @@ def test_form_roles(pub): resp = resp.forms[0].submit('submit') assert FormDef.get(1).roles == [role.id] + def test_form_always_advertise(pub): create_superuser(pub) role = create_role() @@ -856,6 +886,7 @@ def test_form_always_advertise(pub): assert_option_display(resp, 'Display to unlogged users', 'Enabled') assert FormDef.get(1).always_advertise is True + def test_form_digest_template(pub): create_superuser(pub) role = create_role() @@ -890,6 +921,7 @@ def test_form_digest_template(pub): # to wait to check the digest has been updated: assert formdef.data_class().get(formdata.id).digest == 'XhelloY' + def test_form_delete(pub): create_superuser(pub) create_role() @@ -909,6 +941,7 @@ def test_form_delete(pub): resp = resp.follow() assert FormDef.count() == 0 + def test_form_duplicate(pub): create_superuser(pub) create_role() @@ -935,6 +968,7 @@ def test_form_duplicate(pub): assert FormDef.count() == 3 assert FormDef.get(3).name == 'form title (copy 2)' + def test_form_export(pub): create_superuser(pub) create_role() @@ -955,6 +989,7 @@ def test_form_export(pub): formdef2 = FormDef.import_from_xml(fd) assert formdef2.name == 'form title' + def test_form_import(pub): user = create_superuser(pub) role = create_role() @@ -1026,6 +1061,7 @@ def test_form_import(pub): assert FormDef.get(1).fields[0].id == '1' assert FormDef.get(1).fields[1].id == '2' + def test_form_qrcode(pub): create_superuser(pub) create_role() @@ -1042,6 +1078,7 @@ def test_form_qrcode(pub): resp = resp.click(href='qrcode') assert '
' in resp.text + def test_form_description(pub): create_superuser(pub) create_role() @@ -1063,6 +1100,7 @@ def test_form_description(pub): resp = resp.follow() assert_option_display(resp, 'Description', 'On') + def test_form_new_field(pub): create_superuser(pub) create_role() @@ -1101,6 +1139,7 @@ def test_form_new_field(pub): resp = app.get('/backoffice/forms/1/') assert '

baz

' in resp.text + def test_form_delete_field(pub): create_superuser(pub) create_role() @@ -1126,6 +1165,7 @@ def test_form_delete_field(pub): resp = resp.follow() assert len(FormDef.get(1).fields) == 0 + def test_form_delete_field_existing_data(pub): create_superuser(pub) create_role() @@ -1188,6 +1228,7 @@ def test_form_duplicate_field(pub): assert FormDef.get(1).fields[0].label == '1st field' assert FormDef.get(1).fields[1].label == '1st field' + def test_form_duplicate_file_field(pub): create_superuser(pub) create_role() @@ -1214,6 +1255,7 @@ def test_form_duplicate_file_field(pub): assert resp.location == 'http://example.net/backoffice/forms/1/fields/#itemId_2' resp = resp.follow() + def test_form_edit_field(pub): create_superuser(pub) create_role() @@ -1239,6 +1281,7 @@ def test_form_edit_field(pub): assert FormDef.get(1).fields[0].label == 'changed field' assert FormDef.get(1).fields[0].required == False + def test_form_edit_field_advanced(pub): create_superuser(pub) create_role() @@ -1291,6 +1334,7 @@ def test_form_edit_field_advanced(pub): assert resp.text.index('Additional parameters') > \ resp.text.index('') + def test_form_prefill_field(pub): create_superuser(pub) create_role() @@ -1334,6 +1378,7 @@ def test_form_prefill_field(pub): resp = resp.form.submit('submit') assert 'syntax error in Django template: Unexpected end of expression' in resp.text + def test_form_edit_string_field_validation(pub): create_superuser(pub) create_role() @@ -1372,6 +1417,7 @@ def test_form_edit_string_field_validation(pub): resp = resp.form.submit('submit') assert 'syntax error' in resp.text + def test_form_edit_item_field(pub): create_superuser(pub) create_role() @@ -1412,6 +1458,7 @@ def test_form_edit_item_field(pub): assert resp.location == 'http://example.net/backoffice/forms/1/fields/#itemId_1' assert FormDef.get(1).fields[0].items == ['XXX'] + def test_form_edit_item_field_data_source(pub): create_superuser(pub) create_role() @@ -1490,6 +1537,7 @@ def test_form_edit_item_field_data_source(pub): resp = resp.form.submit('submit').follow() resp = app.get('/backoffice/forms/1/') + def test_form_edit_items_field(pub): create_superuser(pub) create_role() @@ -1536,6 +1584,7 @@ def test_form_edit_items_field(pub): assert resp.forms[0]['prefill$type'].options == [ (u'None', True, u'None'), (u'Python Expression', False, u'Python Expression')] + def test_form_edit_page_field(pub): create_superuser(pub) create_role() @@ -1590,6 +1639,7 @@ def test_form_edit_page_field(pub): {'condition': {'type': 'django', 'value': 'foo3'}, 'error_message': 'bar2'}, ] + def test_form_edit_comment_field(pub): create_superuser(pub) create_role() @@ -1644,6 +1694,7 @@ def test_form_edit_comment_field(pub): assert resp.location == 'http://example.net/backoffice/forms/1/fields/' assert FormDef.get(formdef.id).fields[-1].label == '
blah
' + def test_form_edit_map_field(pub): create_superuser(pub) create_role() @@ -1708,6 +1759,7 @@ def test_form_edit_map_field(pub): assert FormDef.get(formdef.id).fields[0].prefill == { 'type': 'geolocation', 'value': 'position'} + def test_form_edit_field_warnings(pub): create_superuser(pub) create_role() @@ -1742,6 +1794,7 @@ def test_form_edit_field_warnings(pub): FormDef.wipe() + def test_form_limit_display_to_page(pub): create_superuser(pub) create_role() @@ -1765,6 +1818,7 @@ def test_form_limit_display_to_page(pub): assert '{{form_var_foobar}}' not in hidden_fields assert '{{form_var_baz}}' in hidden_fields + def test_form_legacy_int_id(pub): create_superuser(pub) create_role() @@ -1816,6 +1870,7 @@ def test_form_legacy_int_id(pub): resp = resp.click('Recipient') assert resp.forms[0]['role_id'].value == role.id + def test_form_anonymise(pub): create_superuser(pub) create_role() @@ -1880,6 +1935,7 @@ def test_form_anonymise(pub): assert 'completed' in resp.text assert len([x for x in formdef.data_class().select() if x.anonymised]) == 3 + def test_form_public_url(pub): create_superuser(pub) create_role() @@ -1895,6 +1951,7 @@ def test_form_public_url(pub): resp = resp.click('Display public URL') assert 'http://example.net/form-title/' in resp.text + def test_form_archive(pub): create_superuser(pub) create_role() @@ -1945,6 +2002,7 @@ def test_form_archive(pub): assert 'formdef' in [x.name for x in tf.getmembers()] assert len(tf.getmembers()) == 1 # 0 formdata + 1 formdef + def test_form_overwrite(pub): user = create_superuser(pub) role = create_role() @@ -2113,6 +2171,7 @@ def test_form_export_import_export_overwrite(pub): field_ow = formdef_overwrited.fields[i] assert (field.id, field.label, field.type) == (field_ow.id, field_ow.label, field_ow.type) + def test_form_comment_with_error_in_wscall(http_requests, pub): create_superuser(pub) NamedWsCall.wipe() @@ -2139,11 +2198,13 @@ def test_form_comment_with_error_in_wscall(http_requests, pub): resp = app.get('/backoffice/forms/%s/' % formdef.id) assert 'x [webservice.xxx.foobar] x' in resp.text + def test_workflows(pub): create_superuser(pub) app = login(get_app(pub)) app.get('/backoffice/workflows/') + def test_workflows_default(pub): create_superuser(pub) app = login(get_app(pub)) @@ -2161,6 +2222,7 @@ def test_workflows_default(pub): assert 'Change Status Name' not in resp.text assert 'Delete' not in resp.text + def test_workflows_new(pub): user = create_superuser(pub) role = create_role() @@ -2206,6 +2268,7 @@ def test_workflows_new(pub): assert wf.possible_status[0].name == 'new status' assert wf.possible_status[0].items[0].message == 'bla bla bla' + def test_workflows_svg(pub): user = create_superuser(pub) role = create_role() @@ -2226,6 +2289,7 @@ def test_workflows_svg(pub): assert resp.content_type == 'image/svg+xml' assert '/static/css/dc2/admin.css' in resp.text + def test_workflows_edit(pub): Workflow.wipe() workflow = Workflow(name='foo') @@ -2241,6 +2305,7 @@ def test_workflows_edit(pub): resp = resp.follow() assert 'baz' in resp.text + def test_workflows_edit_status(pub): create_superuser(pub) Workflow.wipe() @@ -2301,6 +2366,7 @@ def test_workflows_edit_status(pub): resp = resp.follow() assert 'Hello' in Workflow.get(1).possible_status[0].backoffice_info_text + def test_workflows_delete_status(pub): create_superuser(pub) Workflow.wipe() @@ -2322,6 +2388,7 @@ def test_workflows_delete_status(pub): assert resp.location == 'http://example.net/backoffice/workflows/1/' resp = resp.follow() + def test_workflows_delete(pub): Workflow.wipe() workflow = Workflow(name='foo') @@ -2336,6 +2403,7 @@ def test_workflows_delete(pub): resp = resp.follow() assert Workflow.count() == 0 + def test_workflows_export_import(pub): create_superuser(pub) @@ -2366,6 +2434,7 @@ def test_workflows_export_import(pub): assert 'Invalid File' in resp.text assert Workflow.count() == 2 + def test_workflows_export_import_create_role(pub): create_superuser(pub) @@ -2426,6 +2495,7 @@ def test_workflows_export_import_create_role(pub): resp = resp.form.submit('submit') assert 'Invalid File (Unknown referenced role (PLOP))' in resp.text + def test_workflows_duplicate(pub): create_superuser(pub) @@ -2447,6 +2517,7 @@ def test_workflows_duplicate(pub): resp = resp.follow() assert Workflow.get(3).name == 'foo (copy 2)' + def test_workflows_add_all_actions(pub): create_superuser(pub) @@ -2474,6 +2545,7 @@ def test_workflows_add_all_actions(pub): resp = resp.follow() # redirect to ./ i += 1 + def test_workflows_check_available_actions(pub): create_superuser(pub) @@ -2553,6 +2625,7 @@ def test_workflows_edit_dispatch_action(pub): assert workflow.possible_status[0].items[0].rules == [ {'value': 'FOOBAR', 'role_id': '1'}, {'value': 'BARFOO', 'role_id': '1'}] + def test_workflows_edit_email_action(pub): create_superuser(pub) role = create_role() @@ -2688,6 +2761,7 @@ def test_workflows_edit_email_action(pub): sendmail = Workflow.get(workflow.id).get_status(st1.id).items[0] assert sendmail.condition == {'type': 'python', 'value': 'True'} + def test_workflows_edit_jump_previous(pub): create_superuser(pub) role = create_role() @@ -2738,6 +2812,7 @@ def test_workflows_edit_jump_previous(pub): resp = app.get('/backoffice/workflows/1/global-actions/ac1/items/_jump/') assert not 'Previously Marked Status' in [x[2] for x in resp.form['status'].options] + def test_workflows_edit_jump_timeout(pub): create_superuser(pub) role = create_role() @@ -2777,6 +2852,7 @@ def test_workflows_edit_jump_timeout(pub): resp = app.get('/backoffice/workflows/1/status/1/items/_jump/') assert resp.text.count('ComputedExpressionWidget') == 1 + def test_workflows_edit_sms_action(pub): create_superuser(pub) role = create_role() @@ -2803,6 +2879,7 @@ def test_workflows_edit_sms_action(pub): resp = resp.form.submit('submit') assert Workflow.get(workflow.id).possible_status[0].items[0].to == ['12345'] + def test_workflows_edit_display_form_action(pub): create_superuser(pub) role = create_role() @@ -2837,6 +2914,7 @@ def test_workflows_edit_display_form_action(pub): assert 'You are about to remove the "foobar" field.' in resp.text assert not 'Warning:' in resp.text + def test_workflows_edit_choice_action(pub): create_superuser(pub) role = create_role() @@ -2869,6 +2947,7 @@ def test_workflows_edit_choice_action(pub): resp = resp.click(href='items/1/', index=0) assert 'Previously Marked Status' in [x[2] for x in resp.form['status'].options] + def test_workflows_edit_choice_action_functions_only(pub): create_superuser(pub) role = create_role() @@ -2899,6 +2978,7 @@ def test_workflows_edit_choice_action_functions_only(pub): assert 'foobar' not in [x[2] for x in resp.form['by$element0'].options] assert '_receiver' in [x[0] for x in resp.form['by$element0'].options] + def test_workflows_action_subpath(pub): create_superuser(pub) role = create_role() @@ -2917,6 +2997,7 @@ def test_workflows_action_subpath(pub): resp = app.get('/backoffice/workflows/%s/status/%s/items/1/crash' % ( workflow.id, baz_status.id), status=404) + def test_workflows_display_action_ezt_validation(pub): create_superuser(pub) role = create_role() @@ -2959,6 +3040,7 @@ def test_workflows_display_action_ezt_validation(pub): resp = resp.form.submit('submit') assert 'syntax error in ezt template' in resp.text + def test_workflows_delete_action(pub): create_superuser(pub) role = create_role() @@ -2986,6 +3068,7 @@ def test_workflows_delete_action(pub): resp = resp.follow() assert Workflow.get(workflow.id).possible_status[0].items == [] + def test_workflows_variables(pub): create_superuser(pub) create_role() @@ -3016,6 +3099,7 @@ def test_workflows_variables(pub): assert Workflow.get(1).variables_formdef.fields[0].key == 'string' assert Workflow.get(1).variables_formdef.fields[0].label == 'foobar' + def test_workflows_variables_edit(pub): test_workflows_variables(pub) @@ -3044,6 +3128,7 @@ def test_workflows_variables_edit(pub): assert Workflow.get(1).variables_formdef.fields[0].key == 'string' assert Workflow.get(1).variables_formdef.fields[0].varname == '1*1*message' + def test_workflows_variables_with_export_to_model_action(pub): test_workflows_variables(pub) @@ -3058,6 +3143,7 @@ def test_workflows_variables_with_export_to_model_action(pub): resp = app.get('/backoffice/workflows/1/variables/fields/') resp = resp.click('Edit', href='1/') + def test_workflows_backoffice_fields(pub): create_superuser(pub) create_role() @@ -3162,6 +3248,7 @@ def test_workflows_backoffice_fields(pub): workflow = Workflow.get(workflow.id) assert workflow.possible_status[0].items[0].fields == [{'field_id': first_field_id, 'value': 'Hello'}] + def test_workflows_functions(pub): create_superuser(pub) create_role() @@ -3216,6 +3303,7 @@ def test_workflows_functions(pub): resp = resp.click('Recipient') assert not 'delete' in resp.forms[0].fields + def test_workflows_functions_vs_visibility(pub): create_superuser(pub) create_role() @@ -3254,6 +3342,7 @@ def test_workflows_functions_vs_visibility(pub): assert set(Workflow.get(workflow.id).possible_status[2].visibility) == set( ['_receiver', '_other-function']) + def test_workflows_global_actions(pub): create_superuser(pub) create_role() @@ -3301,6 +3390,7 @@ def test_workflows_global_actions(pub): resp = resp.form.submit('delete') assert not Workflow.get(workflow.id).global_actions + def test_workflows_global_actions_edit(pub): create_superuser(pub) create_role() @@ -3358,6 +3448,7 @@ def test_workflows_global_actions_edit(pub): resp = resp.form.submit('submit') assert Workflow.get(workflow.id).global_actions[0].triggers[0].roles == ['_receiver'] + def test_workflows_global_actions_timeout_triggers(pub): create_superuser(pub) create_role() @@ -3405,6 +3496,7 @@ def test_workflows_global_actions_timeout_triggers(pub): resp = resp.form.submit('submit').follow() assert Workflow.get(workflow.id).global_actions[0].triggers[0].timeout == '-2' + def test_workflows_criticality_levels(pub): create_superuser(pub) create_role() @@ -3465,6 +3557,7 @@ def test_workflows_criticality_levels(pub): resp = resp.forms[0].submit('delete-level') assert len(Workflow.get(workflow.id).criticality_levels) == 1 + def test_workflows_wscall_label(pub): create_superuser(pub) create_role() @@ -3487,6 +3580,7 @@ def test_workflows_wscall_label(pub): resp = app.get('/backoffice/workflows/%s/status/%s/' % (workflow.id, baz_status.id)) assert 'Webservice (foowscallbar)' in resp.text + def test_workflows_inspect_view(pub): from wcs.workflows import WorkflowVariablesFieldsFormDef from wcs.wf.form import FormWorkflowStatusItem, WorkflowFormFieldsFormDef @@ -3558,6 +3652,7 @@ def test_workflows_inspect_view(pub): app = login(get_app(pub)) resp = app.get('/backoffice/workflows/%s/inspect' % workflow.id) + def test_workflows_unused(pub): create_superuser(pub) FormDef.wipe() @@ -3588,6 +3683,7 @@ def test_workflows_unused(pub): resp = app.get('/backoffice/workflows/') assert 'Unused workflows' in resp.text + def test_users_roles_menu_entries(pub): create_superuser(pub) app = login(get_app(pub)) @@ -3610,11 +3706,13 @@ def test_users_roles_menu_entries(pub): assert 'Users' not in [x['label'] for x in resp.json] assert 'Roles' not in [x['label'] for x in resp.json] + def test_users(pub): create_superuser(pub) app = login(get_app(pub)) app.get('/backoffice/users/') + def test_users_new(pub): pub.user_class.wipe() create_superuser(pub) @@ -3633,6 +3731,7 @@ def test_users_new(pub): assert pub.user_class.count() == user_count + 1 assert PasswordAccount.count() == account_count + def test_users_new_with_account(pub): pub.user_class.wipe() create_superuser(pub) @@ -3653,6 +3752,7 @@ def test_users_new_with_account(pub): assert pub.user_class.count() == user_count + 1 assert PasswordAccount.count() == account_count + 1 + def test_users_edit(pub): pub.user_class.wipe() create_superuser(pub) @@ -3667,6 +3767,7 @@ def test_users_edit(pub): assert resp.location == 'http://example.net/backoffice/users/%s/' % user.id resp = resp.follow() + def test_users_edit_new_account(pub): pub.user_class.wipe() PasswordAccount.wipe() @@ -3687,6 +3788,7 @@ def test_users_edit_new_account(pub): assert PasswordAccount.count() == account_count + 1 + def test_users_edit_edit_account(pub): pub.user_class.wipe() PasswordAccount.wipe() @@ -3713,6 +3815,7 @@ def test_users_edit_edit_account(pub): assert PasswordAccount.has_key('foo') assert PasswordAccount.get('foo').user_id == user.id + def test_users_edit_with_managing_idp(pub): create_role() pub.user_class.wipe() @@ -3743,6 +3846,7 @@ def test_users_edit_with_managing_idp(pub): resp = app.get('/backoffice/users/%s/' % user.id) assert not '/edit' in resp.text + def test_users_delete(pub): pub.user_class.wipe() PasswordAccount.wipe() @@ -3767,6 +3871,7 @@ def test_users_delete(pub): assert pub.user_class.count() == user_count - 1 assert PasswordAccount.count() == account_count - 1 + def test_users_pagination(pub): pub.user_class.wipe() PasswordAccount.wipe() @@ -3792,6 +3897,7 @@ def test_users_pagination(pub): resp = resp.click('Next Page') assert 'foo bar 50' in resp.text + def test_users_filter(pub): pub.user_class.wipe() PasswordAccount.wipe() @@ -3827,6 +3933,7 @@ def test_users_filter(pub): assert 'foo bar 10' not in resp.text # simple user assert 'baz bar 1' in resp.text # user with role + def test_users_search(pub): pub.user_class.wipe() PasswordAccount.wipe() @@ -3848,6 +3955,7 @@ def test_users_search(pub): assert 'bar 10' in resp.text assert 'Number of filtered users: 10' in resp.text + def test_users_new_with_custom_formdef(pub): pub.user_class.wipe() formdef = UserFieldsFormDef(pub) @@ -3874,6 +3982,7 @@ def test_users_new_with_custom_formdef(pub): assert pub.user_class.count() == user_count + 1 assert PasswordAccount.count() == account_count + def test_users_display_roles(pub): pub.user_class.wipe() @@ -3887,10 +3996,12 @@ def test_users_display_roles(pub): assert role.name in resp.text assert 'Unknown role (XXX)' in resp.text + def test_roles(pub): app = login(get_app(pub)) app.get('/backoffice/roles/') + def test_roles_new(pub): Role.wipe() app = login(get_app(pub)) @@ -3908,6 +4019,7 @@ def test_roles_new(pub): assert Role.get(1).name == 'a new role' assert Role.get(1).details == 'bla bla bla' + def test_roles_edit(pub): Role.wipe() role = Role(name='foobar') @@ -3931,6 +4043,7 @@ def test_roles_edit(pub): assert Role.get(1).details == 'bla bla bla' assert Role.get(1).emails_to_members == True + def test_roles_matching_formdefs(pub): Role.wipe() role = Role(name='foo') @@ -3962,6 +4075,7 @@ def test_roles_matching_formdefs(pub): assert 'form baz' in resp.text assert 'form bar' not in resp.text + def test_roles_delete(pub): Role.wipe() role = Role(name='foobar') @@ -3976,11 +4090,13 @@ def test_roles_delete(pub): resp = resp.follow() assert Role.count() == 0 + def test_categories(pub): create_superuser(pub) app = login(get_app(pub)) resp = app.get('/backoffice/forms/categories/') + def test_categories_legacy_urls(pub): create_superuser(pub) app = login(get_app(pub)) @@ -3991,6 +4107,7 @@ def test_categories_legacy_urls(pub): resp = app.get('/backoffice/categories/1/') assert resp.location.endswith('/backoffice/forms/categories/1/') + def test_categories_new(pub): create_superuser(pub) Category.wipe() @@ -4017,6 +4134,7 @@ def test_categories_new(pub): assert Category.get(1).name == 'a new category' assert Category.get(1).description == 'description of the category' + def test_categories_edit(pub): create_superuser(pub) Category.wipe() @@ -4038,6 +4156,7 @@ def test_categories_edit(pub): assert Category.get(1).description == 'category description' + def test_categories_edit_duplicate_name(pub): Category.wipe() category = Category(name='foobar') @@ -4057,6 +4176,7 @@ def test_categories_edit_duplicate_name(pub): resp = resp.forms[0].submit('cancel') assert resp.location == 'http://example.net/backoffice/forms/categories/' + def test_categories_with_formdefs(pub): Category.wipe() category = Category(name='foobar') @@ -4077,6 +4197,7 @@ def test_categories_with_formdefs(pub): assert 'form bar' in resp.text assert 'no form associated to this category' not in resp.text + def test_categories_delete(pub): Category.wipe() category = Category(name='foobar') @@ -4123,6 +4244,7 @@ def test_categories_edit_description(pub): resp2 = resp2.follow() assert Category.get(1).description == 'updated description' + def test_categories_new_duplicate_name(pub): Category.wipe() category = Category(name='foobar') @@ -4135,6 +4257,7 @@ def test_categories_new_duplicate_name(pub): resp = resp.forms[0].submit('submit') assert 'This name is already used' in resp.text + def test_categories_reorder(pub): Category.wipe() category = Category(name='foo') @@ -4155,6 +4278,7 @@ def test_categories_reorder(pub): Category.sort_by_position(categories) assert [x.id for x in categories] == ['3', '1', '2'] + def test_settings(pub): create_superuser(pub) app = login(get_app(pub)) @@ -4171,6 +4295,7 @@ def test_settings(pub): app.get('/backoffice/settings/session') app.get('/backoffice/settings/admin-permissions') + def test_settings_disabled_screens(pub): create_superuser(pub) app = login(get_app(pub)) @@ -4187,6 +4312,7 @@ def test_settings_disabled_screens(pub): assert 'Identification' not in resp.text assert 'Theme' not in resp.text + def test_settings_export_import(pub, studio): def wipe(): FormDef.wipe() @@ -4361,6 +4487,7 @@ def test_settings_export_import(pub, studio): filelist = zipf.namelist() assert len([x for x in filelist if 'roles/' in x]) == 0 + def test_settings_themes(pub): create_superuser(pub) app = login(get_app(pub)) @@ -4393,6 +4520,7 @@ def test_settings_themes(pub): assert 'checked' in resp.text assert get_current_theme()['name'] == 'test' + def test_settings_template(pub): create_superuser(pub) app = login(get_app(pub)) @@ -4413,6 +4541,7 @@ def test_settings_template(pub): resp = app.get('/backoffice/settings/template') assert resp.forms[0]['template'].value == orig_value + def test_settings_user(pub): user = create_superuser(pub) app = login(get_app(pub)) @@ -4467,6 +4596,7 @@ def test_settings_user(pub): pub.cfg['users']['field_email'] = None pub.write_cfg() + def test_settings_emails(pub): create_superuser(pub) app = login(get_app(pub)) @@ -4498,6 +4628,7 @@ def test_settings_emails(pub): resp = resp.forms[0].submit() assert pub.cfg['emails']['email-new-account-approved_subject'] is None + def test_settings_texts(pub): create_superuser(pub) app = login(get_app(pub)) @@ -4515,6 +4646,7 @@ def test_settings_texts(pub): assert resp.location == 'http://example.net/backoffice/settings/texts/' assert pub.cfg['texts']['text-top-of-login'] == None + @pytest.mark.skipif('lasso is None') def test_settings_auth(pub): pub.user_class.wipe() # makes sure there are no users @@ -4545,6 +4677,7 @@ def test_settings_auth(pub): assert 'identification/password/' in resp.text assert pub.cfg['identification']['methods'] == ['password'] + @pytest.mark.skipif('lasso is None') def test_settings_idp(pub): # create admin session @@ -4605,6 +4738,7 @@ def test_settings_idp(pub): resp = resp.click('Update from remote URL') assert urlopen.call_count == 2 + def test_settings_auth_password(pub): Role.wipe() @@ -4633,6 +4767,7 @@ def test_settings_auth_password(pub): resp = resp.click('Bulk Import') resp = resp.forms[0].submit() + def test_settings_filetypes(pub): create_superuser(pub) app = login(get_app(pub)) @@ -4671,6 +4806,7 @@ def test_settings_filetypes(pub): resp = resp.follow() assert 'HTML files' not in resp.text + def test_settings_filetypes_update(pub): create_superuser(pub) app = login(get_app(pub)) @@ -4713,6 +4849,7 @@ def test_settings_filetypes_update(pub): 'label': 'Text files', } + def test_settings_logs(pub): # reset logging state logging.shutdown() @@ -4733,6 +4870,7 @@ def test_settings_logs(pub): resp = resp.follow() assert 'Logs' not in resp.text + def test_settings_geolocation(pub): create_superuser(pub) app = login(get_app(pub)) @@ -4749,6 +4887,7 @@ def test_settings_geolocation(pub): pub.reload_cfg() assert pub.cfg['misc']['default-position'] == '1.234;-1.234' + def test_data_sources(pub): create_superuser(pub) app = login(get_app(pub)) @@ -4762,6 +4901,7 @@ def test_data_sources(pub): app.get('/backoffice/forms/data-sources/42/', status=404) app.get('/backoffice/workflows/data-sources/42/', status=404) + def test_data_sources_new(pub): create_superuser(pub) NamedDataSource.wipe() @@ -4807,6 +4947,7 @@ def test_data_sources_new(pub): assert NamedDataSource.count() == 2 + def test_data_sources_view(pub): create_superuser(pub) NamedDataSource.wipe() @@ -4952,6 +5093,7 @@ def test_data_sources_edit(pub): assert NamedDataSource.get(1).description == 'data source description' + def test_data_sources_edit_duplicate_name(pub): create_superuser(pub) NamedDataSource.wipe() @@ -4973,6 +5115,7 @@ def test_data_sources_edit_duplicate_name(pub): resp = resp.forms[0].submit('cancel') assert resp.location == 'http://example.net/backoffice/settings/data-sources/1/' + def test_data_sources_delete(pub): create_superuser(pub) NamedDataSource.wipe() @@ -4995,6 +5138,7 @@ def test_data_sources_delete(pub): resp = resp.follow() assert NamedDataSource.count() == 0 + def test_data_sources_in_use_delete(pub): create_superuser(pub) NamedDataSource.wipe() @@ -5022,6 +5166,7 @@ def test_data_sources_in_use_delete(pub): resp = resp.click(href='delete') assert 'delete-button' in resp.text + def test_data_sources_edit_slug(pub): create_superuser(pub) NamedDataSource.wipe() @@ -5056,6 +5201,7 @@ def test_data_sources_edit_slug(pub): resp = resp.forms[0].submit('submit') assert resp.location == 'http://example.net/backoffice/settings/data-sources/1/' + def test_data_sources_in_use_edit_slug(pub): create_superuser(pub) NamedDataSource.wipe() @@ -5086,6 +5232,7 @@ def test_data_sources_in_use_edit_slug(pub): assert 'form_slug' in resp.text resp = resp.form.submit('submit') + def test_wscalls_new(pub): create_superuser(pub) NamedWsCall.wipe() @@ -5114,6 +5261,7 @@ def test_wscalls_new(pub): assert NamedWsCall.get('a_new_webservice_call').name == 'a new webservice call' + def test_wscalls_view(pub): create_superuser(pub) NamedWsCall.wipe() @@ -5133,6 +5281,7 @@ def test_wscalls_view(pub): resp = app.get('/backoffice/settings/wscalls/%s/' % wscall.id) assert 'http://remote.example.net/json' in resp.text + def test_wscalls_edit(pub): test_wscalls_view(pub) @@ -5175,6 +5324,7 @@ def test_wscalls_delete(pub): assert resp.location == 'http://example.net/backoffice/settings/wscalls/' assert NamedWsCall.count() == 0 + def test_settings_permissions(pub): create_superuser(pub) role1 = create_role() @@ -5226,6 +5376,7 @@ def test_settings_permissions(pub): assert pub.cfg['admin-permissions']['forms'] == [] assert pub.cfg['admin-permissions']['workflows'] == [] + def test_settings_theme_preview(pub): create_superuser(pub) @@ -5252,6 +5403,7 @@ def test_settings_theme_preview(pub): resp = resp.form.submit('submit') assert "The theme preview doesn't support this." in resp.text + def test_settings_theme_download_upload(pub): create_superuser(pub) @@ -5286,6 +5438,7 @@ def test_settings_theme_download_upload(pub): assert app.get('/themes/alto/../', status=404) assert app.get('/themes/xxx/../', status=404) + def test_postgresql_settings(pub): if not pub.is_using_postgresql(): pytest.skip('this requires SQL') @@ -5322,6 +5475,7 @@ def test_postgresql_settings(pub): resp = resp.form.submit() assert pub.cfg['postgresql']['port'] == 5432 + def test_studio_home(pub, studio): create_superuser(pub) app = login(get_app(pub)) @@ -5346,6 +5500,7 @@ def test_studio_home(pub, studio): resp = app.get('/backoffice/') assert 'studio' not in resp.text + def test_studio_workflows(pub, studio): create_superuser(pub) app = login(get_app(pub)) @@ -5355,6 +5510,7 @@ def test_studio_workflows(pub, studio): assert 'status/deleted/' in resp.text assert 'This is the default workflow,' in resp.text + def test_cards_new(pub, studio): CardDef.wipe() create_superuser(pub) @@ -5369,6 +5525,7 @@ def test_cards_new(pub, studio): assert CardDef.get(1).workflow_id is None assert CardDef.get(1).disabled is False + def test_cards_duplicate(pub, studio): test_cards_new(pub, studio) app = login(get_app(pub)) @@ -5377,6 +5534,7 @@ def test_cards_duplicate(pub, studio): assert CardDef.get(2).name == 'card title (copy)' assert CardDef.get(2).disabled is False + def test_card_workflow_change(pub, studio): Workflow.wipe() workflow = Workflow(name='Workflow Two') diff --git a/tests/test_api.py b/tests/test_api.py index 8067d7074..70be68247 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -38,10 +38,12 @@ from wcs.qommon.errors import AccessForbiddenError from utilities import get_app, create_temporary_pub, clean_temporary_pub + def pytest_generate_tests(metafunc): if 'pub' in metafunc.fixturenames: metafunc.parametrize('pub', ['pickle', 'sql'], indirect=True) + @pytest.fixture def pub(request, emails): pub = create_temporary_pub(sql_mode=(request.param == 'sql')) @@ -59,6 +61,7 @@ coucou = 1234 return pub + def teardown_module(module): clean_temporary_pub() @@ -103,39 +106,48 @@ def sign_uri(uri, user=None, format='json'): hashlib.sha256).digest())) return urlparse.urlunparse((scheme, netloc, path, params, query, fragment)) + def test_user_page_redirect(pub): output = get_app(pub).get('/user') assert output.headers.get('location') == 'http://example.net/myspace/' + def test_user_page_error(pub): # check we get json as output for errors output = get_app(pub).get('/api/user/', status=403) assert output.json['err_desc'] == 'no user specified' + def test_user_page_error_when_json_and_no_user(pub): output = get_app(pub).get('/api/user/?format=json', status=403) assert output.json['err_desc'] == 'no user specified' + def test_get_user_from_api_query_string_error_missing_orig(pub): output = get_app(pub).get('/api/user/?format=json&signature=xxx', status=403) assert output.json['err_desc'] == 'missing/multiple orig field' + def test_get_user_from_api_query_string_error_invalid_orig(pub): output = get_app(pub).get('/api/user/?format=json&orig=coin&signature=xxx', status=403) assert output.json['err_desc'] == 'invalid orig' + def test_get_user_from_api_query_string_error_missing_algo(pub): output = get_app(pub).get('/api/user/?format=json&orig=coucou&signature=xxx', status=403) assert output.json['err_desc'] == 'missing/multiple algo field' + def test_get_user_from_api_query_string_error_invalid_algo(pub): output = get_app(pub).get('/api/user/?format=json&orig=coucou&signature=xxx&algo=coin', status=403) assert output.json['err_desc'] == 'invalid algo' + def test_get_user_from_api_query_string_error_invalid_signature(pub): output = get_app(pub).get('/api/user/?format=json&orig=coucou&signature=xxx&algo=sha1', status=403) assert output.json['err_desc'] == 'invalid signature' + def test_get_user_from_api_query_string_error_missing_timestamp(pub): signature = urllib.quote( base64.b64encode( @@ -145,6 +157,7 @@ def test_get_user_from_api_query_string_error_missing_timestamp(pub): output = get_app(pub).get('/api/user/?format=json&orig=coucou&algo=sha1&signature=%s' % signature, status=403) assert output.json['err_desc'] == 'missing/multiple timestamp field' + def test_get_user_from_api_query_string_error_missing_email(pub): timestamp = datetime.datetime.utcnow().isoformat()[:19] + 'Z' query = 'format=json&orig=coucou&algo=sha1×tamp=' + timestamp @@ -156,6 +169,7 @@ def test_get_user_from_api_query_string_error_missing_email(pub): output = get_app(pub).get('/api/user/?%s&signature=%s' % (query, signature), status=403) assert output.json['err_desc'] == 'no user specified' + def test_get_user_from_api_query_string_error_unknown_nameid(pub): timestamp = datetime.datetime.utcnow().isoformat()[:19] + 'Z' query = 'format=json&orig=coucou&algo=sha1&NameID=xxx×tamp=' + timestamp @@ -167,6 +181,7 @@ def test_get_user_from_api_query_string_error_unknown_nameid(pub): output = get_app(pub).get('/api/user/?%s&signature=%s' % (query, signature), status=403) assert output.json['err_desc'] == 'unknown NameID' + def test_get_user_from_api_query_string_error_missing_email_valid_endpoint(pub): # check it's ok to sign an URL without specifiying an user if the endpoint # works fine without user. @@ -182,6 +197,7 @@ def test_get_user_from_api_query_string_error_missing_email_valid_endpoint(pub): output = get_app(pub).get('/json?%s&signature=%s' % (query, signature)) assert output.json == {'err': 0, 'data': []} + def test_get_user_from_api_query_string_error_unknown_nameid_valid_endpoint(pub): # check the categories and forms endpoints accept an unknown NameID timestamp = datetime.datetime.utcnow().isoformat()[:19] + 'Z' @@ -196,6 +212,7 @@ def test_get_user_from_api_query_string_error_unknown_nameid_valid_endpoint(pub) output = get_app(pub).get('/json?%s&signature=%s' % (query, signature)) assert output.json == {'err': 0, 'data': []} + def test_get_user_from_api_query_string_error_success_sha1(pub, local_user): timestamp = datetime.datetime.utcnow().isoformat()[:19] + 'Z' query = 'format=json&orig=coucou&algo=sha1&email=' + urllib.quote(local_user.email) + '×tamp=' + timestamp @@ -207,6 +224,7 @@ def test_get_user_from_api_query_string_error_success_sha1(pub, local_user): output = get_app(pub).get('/api/user/?%s&signature=%s' % (query, signature)) assert output.json['user_display_name'] == u'Jean Darmette' + def test_get_user_from_api_query_string_error_invalid_signature_algo_mismatch(pub, local_user): timestamp = datetime.datetime.utcnow().isoformat()[:19] + 'Z' query = 'format=json&orig=coucou&algo=sha256&email=' + urllib.quote(local_user.email) + '×tamp=' + timestamp @@ -218,6 +236,7 @@ def test_get_user_from_api_query_string_error_invalid_signature_algo_mismatch(pu output = get_app(pub).get('/api/user/?%s&signature=%s' % (query, signature), status=403) assert output.json['err_desc'] == 'invalid signature' + def test_get_user_from_api_query_string_error_success_sha256(pub, local_user): timestamp = datetime.datetime.utcnow().isoformat()[:19] + 'Z' query = 'format=json&orig=coucou&algo=sha256&email=' + urllib.quote(local_user.email) + '×tamp=' + timestamp @@ -229,6 +248,7 @@ def test_get_user_from_api_query_string_error_success_sha256(pub, local_user): output = get_app(pub).get('/api/user/?%s&signature=%s' % (query, signature)) assert output.json['user_display_name'] == u'Jean Darmette' + def test_sign_url(pub, local_user): signed_url = sign_url( 'http://example.net/api/user/?format=json&orig=coucou&email=%s' % urllib.quote(local_user.email), @@ -248,6 +268,7 @@ def test_sign_url(pub, local_user): url = signed_url[len('http://example.net'):] output = get_app(pub).get(url, status=403) + def test_get_user(pub, local_user): Role.wipe() role = Role(name='Foo bar') @@ -307,6 +328,7 @@ def test_get_user_compat_endpoint(pub, local_user): output = get_app(pub).get(url) assert output.json['user_display_name'] == u'Jean Darmette' + def test_formdef_list(pub): Role.wipe() role = Role(name='Foo bar') @@ -342,6 +364,7 @@ def test_formdef_list(pub): assert resp1.json['err'] == 0 assert len(resp1.json['data']) == 0 + def test_limited_formdef_list(pub, local_user): Role.wipe() role = Role(name='Foo bar') @@ -413,6 +436,7 @@ def test_limited_formdef_list(pub, local_user): resp = get_app(pub).get('/api/formdefs/') assert resp.json['data'][0]['required_authentication_contexts'] == ['fedict'] + def test_formdef_list_redirection(pub): FormDef.wipe() formdef = FormDef() @@ -429,6 +453,7 @@ def test_formdef_list_redirection(pub): assert resp1.json['data'][0]['redirection'] == True assert 'count' not in resp1.json['data'][0] + def test_backoffice_submission_formdef_list(pub, local_user): Role.wipe() role = Role(name='Foo bar') @@ -493,6 +518,7 @@ def test_backoffice_submission_formdef_list(pub, local_user): assert resp.json['err'] == 0 assert len(resp.json['data']) == 0 + def test_formdef_schema(pub): Workflow.wipe() workflow = Workflow(name='test') @@ -601,6 +627,7 @@ def test_formdef_schema(pub): get_app(pub).get('/api/formdefs/xxx/schema', status=404) + def test_post_invalid_json(pub, local_user): resp = get_app(pub).post('/api/formdefs/test/submit', params='not a json payload', @@ -609,6 +636,7 @@ def test_post_invalid_json(pub, local_user): assert resp.json['err'] == 1 assert resp.json['err_class'] == 'Invalid request' + def test_formdef_submit(pub, local_user): Role.wipe() role = Role(name='test') @@ -689,6 +717,7 @@ def test_formdef_submit(pub, local_user): data_class.wipe() + def test_formdef_submit_only_one(pub, local_user): Role.wipe() role = Role(name='test') @@ -726,6 +755,7 @@ def test_formdef_submit_only_one(pub, local_user): assert data_class.get(resp.json['data']['id']).user_id == str(local_user.id) assert data_class.count() == 2 + def test_formdef_submit_with_varname(pub, local_user): NamedDataSource.wipe() data_source = NamedDataSource(name='foobar') @@ -920,6 +950,7 @@ def test_formdef_submit_from_wscall(pub, local_user): new_formdata = formdef.data_class().get(resp.json['data']['id']) assert new_formdata.data.get('5') is None + def test_categories(pub): FormDef.wipe() Category.wipe() @@ -962,6 +993,7 @@ def test_categories(pub): resp = get_app(pub).get('/api/categories/') assert resp.json['data'][0]['description'] == category.description + def test_categories_private(pub, local_user): FormDef.wipe() Category.wipe() @@ -1001,6 +1033,7 @@ def test_categories_private(pub, local_user): resp = get_app(pub).get(sign_uri('http://example.net/api/categories/', local_user)) assert len(resp.json['data']) == 1 + def test_categories_formdefs(pub, local_user): FormDef.wipe() Category.wipe() @@ -1080,6 +1113,7 @@ def test_categories_formdefs(pub, local_user): assert resp.json['err'] == 0 assert len(resp.json['data']) == 1 + def test_categories_full(pub): test_categories(pub) resp = get_app(pub).get('/api/categories/?full=on') @@ -1087,6 +1121,7 @@ def test_categories_full(pub): assert resp.json['data'][0]['forms'][0]['title'] == 'test' assert resp.json['data'][0]['forms'][1]['title'] == 'test 2' + def test_formdata(pub, local_user): NamedDataSource.wipe() data_source = NamedDataSource(name='foobar') @@ -1187,6 +1222,7 @@ def test_formdata(pub, local_user): resp2 = get_app(pub).get(sign_uri('/test/%s/' % formdata.id, user=local_user), status=403) + def test_formdata_backoffice_fields(pub, local_user): test_formdata(pub, local_user) workflow = Workflow.get(2) @@ -1205,6 +1241,7 @@ def test_formdata_backoffice_fields(pub, local_user): resp = get_app(pub).get(sign_uri('/api/forms/test/%s/' % formdata.id, user=local_user)) assert resp.json['workflow']['fields']['backoffice_blah'] == 'Hello world' + def test_formdata_edit(pub, local_user): test_formdata(pub, local_user) formdef = FormDef.select()[0] @@ -1256,6 +1293,7 @@ def test_formdata_edit(pub, local_user): assert formdef.data_class().select()[0].data['0'] == 'bar2@localhost' assert formdef.data_class().select()[0].status == 'wf-rejected' + def test_formdata_with_workflow_data(pub, local_user): Role.wipe() role = Role(name='test') @@ -1298,6 +1336,7 @@ def test_formdata_with_workflow_data(pub, local_user): assert base64.decodestring(force_bytes(resp.json['workflow']['data']['blah']['content'])) == b'test' assert base64.decodestring(force_bytes(resp.json['workflow']['data']['blah2']['content'])) == b'test' + def test_user_by_nameid(pub, local_user): resp = get_app(pub).get(sign_uri('/api/users/xyz/', user=local_user), status=404) @@ -1306,6 +1345,7 @@ def test_user_by_nameid(pub, local_user): resp = get_app(pub).get(sign_uri('/api/users/xyz/', user=local_user)) assert str(resp.json['id']) == str(local_user.id) + def test_user_roles(pub, local_user): local_user.name_identifiers = ['xyz'] local_user.store() @@ -1317,6 +1357,7 @@ def test_user_roles(pub, local_user): assert len(resp.json['user_roles']) == 1 assert resp.json['user_roles'][0]['name'] == 'Foo bar' + def test_user_forms(pub, local_user): Workflow.wipe() workflow = Workflow.get_default_workflow() @@ -1435,6 +1476,7 @@ def test_user_forms(pub, local_user): assert resp2.json['data'][0] == resp.json['data'][1] assert resp2.json['data'][1] == resp.json['data'][0] + def test_user_forms_limit_offset(pub, local_user): if not pub.is_using_postgresql(): pytest.skip('this requires SQL') @@ -1480,6 +1522,7 @@ def test_user_forms_limit_offset(pub, local_user): assert len(resp.json['data']) == 10 assert [x['form_number_raw'] for x in resp.json['data']] == [str(x) for x in range(50, 40, -1)] + def test_user_forms_from_agent(pub, local_user): Role.wipe() role = Role(name='Foo bar') @@ -1532,6 +1575,7 @@ def test_user_forms_from_agent(pub, local_user): agent_user.store() get_app(pub).get(sign_uri('/api/users/%s/forms' % local_user.id, user=agent_user), status=403) + def test_user_drafts(pub, local_user): FormDef.wipe() formdef = FormDef() @@ -1716,6 +1760,7 @@ def test_api_list_formdata(pub, local_user): get_app(pub).get(sign_uri('/api/forms/test/list?filter=all&offset=plop', user=local_user), status=400) get_app(pub).get(sign_uri('/api/forms/test/list?filter=all&limit=plop', user=local_user), status=400) + def test_api_anonymized_formdata(pub, local_user, admin_user): Role.wipe() role = Role(name='test') @@ -1820,6 +1865,7 @@ def test_api_anonymized_formdata(pub, local_user, admin_user): assert 'NameID' in resp.json['evolution'][1]['who'] assert 'name' in resp.json['evolution'][1]['who'] + def test_api_geojson_formdata(pub, local_user): Role.wipe() role = Role(name='test') @@ -1905,6 +1951,7 @@ def test_api_geojson_formdata(pub, local_user): formdef.store() resp = get_app(pub).get(sign_uri('/api/forms/test/geojson', user=local_user), status=404) + def test_api_global_geojson(pub, local_user): Role.wipe() role = Role(name='test') @@ -1959,6 +2006,7 @@ def test_api_global_geojson(pub, local_user): assert 'features' in resp.json assert len(resp.json['features']) == 20 + def test_api_global_listing(pub, local_user): if not pub.is_using_postgresql(): resp = get_app(pub).get(sign_uri('/api/forms/geojson', user=local_user), status=404) @@ -2027,6 +2075,7 @@ def test_api_global_listing(pub, local_user): get_app(pub).get(sign_uri('/api/forms/?status=done&limit=plop', user=local_user), status=400) get_app(pub).get(sign_uri('/api/forms/?status=done&offset=plop', user=local_user), status=400) + def test_api_global_listing_ignored_roles(pub, local_user): test_api_global_listing(pub, local_user) @@ -2148,6 +2197,7 @@ def ics_data(local_user): formdata.jump_status('finished') formdata.store() + def test_api_ics_formdata(pub, local_user, ics_data): role = Role.select()[0] @@ -2203,6 +2253,7 @@ def test_api_ics_formdata(pub, local_user, ics_data): assert resp.text.count('DTSTART') == 10 assert resp.text.count('DTEND') == 10 + def test_api_ics_formdata_http_auth(pub, local_user, ics_data): role = Role.select()[0] @@ -2233,6 +2284,7 @@ def test_api_ics_formdata_http_auth(pub, local_user, ics_data): app.authorization = ('Basic', ('user', 'password2')) resp = app.get('/api/forms/test/ics/foobar?email=%s' % local_user.email, status=403) + def test_roles(pub, local_user): Role.wipe() role = Role(name='Hello World') @@ -2257,6 +2309,7 @@ def test_roles(pub, local_user): assert resp.json['data'][0]['emails_to_members'] == False assert resp.json['data'][0]['details'] == 'kouign amann' + def test_users(pub, local_user): resp = get_app(pub).get('/api/users/', status=403) @@ -2292,6 +2345,7 @@ def test_users(pub, local_user): resp = get_app(pub).get(sign_uri('/api/users/?q=foobar')) assert len(resp.json['data']) == 0 + def test_users_unaccent(pub, local_user): local_user.name = 'Jean Sénisme' local_user.store() @@ -2307,6 +2361,7 @@ def test_users_unaccent(pub, local_user): resp = get_app(pub).get(sign_uri('/api/users/?q=blah')) assert len(resp.json['data']) == 0 + def test_workflow_trigger(pub, local_user): workflow = Workflow(name='test') st1 = workflow.add_status('Status1', 'st1') @@ -2393,6 +2448,7 @@ def test_workflow_trigger_with_condition(pub, local_user): resp = get_app(pub).post(sign_uri(formdata.get_url() + 'jump/trigger/XXX')) assert resp.json == {'err': 0, 'url': None} + def test_workflow_trigger_jump_once(pub, local_user): workflow = Workflow(name='test') st1 = workflow.add_status('Status1', 'st1') @@ -2514,6 +2570,7 @@ def test_workflow_global_webservice_trigger(pub, local_user): {'test': 'BAR'}, status=200) assert formdef.data_class().get(formdata.id).workflow_data == {'plop': {'test': 'BAR'}} + def test_tracking_code(pub): FormDef.wipe() formdef = FormDef() @@ -2550,6 +2607,7 @@ def test_tracking_code(pub): formdata.remove_self() resp = get_app(pub).get(sign_url('/api/code/%s?orig=coucou' % code.id, '1234'), status=404) + def test_validate_expression(pub): resp = get_app(pub).get('/api/validate-expression?expression=hello') assert resp.json == {'klass': None, 'msg': ''} @@ -2570,6 +2628,7 @@ def test_validate_expression(pub): assert resp.json['klass'] == 'error' assert resp.json['msg'].startswith('syntax error in Django template: Could not parse the remainder') + def test_validate_condition(pub): resp = get_app(pub).get('/api/validate-condition?type=python&value_python=hello') assert resp.json == {'klass': None, 'msg': ''} @@ -2593,6 +2652,7 @@ def test_validate_condition(pub): assert resp.json['klass'] == 'error' assert resp.json['msg'] == 'unknown condition type' + @pytest.fixture(params=['sql', 'pickle']) def no_request_pub(request): pub = create_temporary_pub(sql_mode=bool(request.param == 'sql')) @@ -2605,11 +2665,13 @@ api.example.com = 1234 ''') return pub + def test_get_secret_and_orig(no_request_pub): secret, orig = get_secret_and_orig('https://api.example.com/endpoint/') assert secret == '1234' assert orig == 'example.net' + def test_reverse_geocoding(pub): with mock.patch('wcs.qommon.misc.urlopen') as urlopen: urlopen.side_effect = lambda *args: StringIO(json.dumps({'address': 'xxx'})) @@ -2717,6 +2779,7 @@ def test_geocoding(pub): resp = get_app(pub).get('/api/geocoding?q=test') assert urlopen.call_args[0][0] == 'http://reverse.example.net/?param=value&format=json&q=test&accept-language=en' + def test_cards(pub, local_user): Role.wipe() role = Role(name='test') diff --git a/tests/test_auth_pages.py b/tests/test_auth_pages.py index a232d1bdd..f64b060ee 100644 --- a/tests/test_auth_pages.py +++ b/tests/test_auth_pages.py @@ -24,15 +24,18 @@ def pub(): return pub + @pytest.fixture def pub_2auth(pub): pub.cfg['identification'] = {'methods': ['password', 'idp']} pub.write_cfg() return pub + def teardown_module(module): clean_temporary_pub() + def test_login_logout(pub): resp_initial = get_app(pub).get('/') resp = login(get_app(pub), username='foo', password='foo').get('/') @@ -40,6 +43,7 @@ def test_login_logout(pub): resp = resp.follow() assert resp.text == resp_initial.text + def test_register_account(pub): resp = get_app(pub).get('/').click('Login').follow() assert not 'register' in resp.text @@ -54,6 +58,7 @@ def test_register_account(pub): assert PasswordAccount.count() == 2 assert pub.user_class.count() == 2 + def test_login_2auth(pub_2auth): resp = get_app(pub_2auth).get('/').click('Login').follow() resp.form['method'] = 'Username / password' @@ -68,6 +73,7 @@ def test_login_2auth(pub_2auth): resp = resp.form.submit().follow() assert 'SSO support is not yet configured' in resp.text + def test_register_2auth(pub_2auth): pub_2auth.cfg['identities'] = {'creation': 'self'} pub_2auth.write_cfg() diff --git a/tests/test_backoffice_pages.py b/tests/test_backoffice_pages.py index f9e55011e..67f5c1edf 100644 --- a/tests/test_backoffice_pages.py +++ b/tests/test_backoffice_pages.py @@ -59,6 +59,7 @@ def pytest_generate_tests(metafunc): if 'pub' in metafunc.fixturenames: metafunc.parametrize('pub', ['pickle', 'sql', 'pickle-templates'], indirect=True) + @pytest.fixture def pub(request, emails): pub = create_temporary_pub( @@ -80,6 +81,7 @@ coucou = 1234 return pub + def create_user(pub, is_admin=False): if pub.user_class.select(lambda x: x.name == 'admin'): user1 = pub.user_class.select(lambda x: x.name == 'admin')[0] @@ -106,8 +108,10 @@ def create_user(pub, is_admin=False): return user1 + def create_superuser(pub): return create_user(pub, is_admin=True) + def create_environment(pub, set_receiver=True): pub.session_manager.session_class.wipe() Workflow.wipe() @@ -178,14 +182,17 @@ def create_environment(pub, set_receiver=True): formdata.jump_status('new') formdata.store() + def teardown_module(module): clean_temporary_pub() + def test_backoffice_unlogged(pub): create_superuser(pub) resp = get_app(pub).get('/backoffice/', status=302) assert resp.location == 'http://example.net/login/?next=http%3A%2F%2Fexample.net%2Fbackoffice%2F' + def test_backoffice_home(pub): create_superuser(pub) app = login(get_app(pub)) @@ -194,6 +201,7 @@ def test_backoffice_home(pub): assert 'Forms Workshop' in resp.text assert 'Workflows Workshop' in resp.text + def test_backoffice_role_user(pub): create_user(pub) app = login(get_app(pub)) @@ -224,6 +232,7 @@ def test_backoffice_role_user(pub): assert not 'Forms Workshop' in resp.text assert 'Workflows Workshop' in resp.text + def test_backoffice_forms(pub): create_superuser(pub) create_environment(pub, set_receiver=False) @@ -284,6 +293,7 @@ def test_backoffice_forms(pub): assert 'Forms in your care' in resp.text assert '9 open on 50' in resp.text + def test_backoffice_listing(pub): create_superuser(pub) create_environment(pub) @@ -383,6 +393,7 @@ def test_backoffice_listing(pub): resp = resp.forms[0].submit() assert resp.text.count('data-link') == 9 + def test_backoffice_listing_pagination(pub): if not pub.is_using_postgresql(): pytest.skip('this requires SQL') @@ -423,6 +434,7 @@ def test_backoffice_listing_pagination(pub): resp = resp.follow() assert resp.form['offset'].value == '0' + def test_backoffice_listing_order(pub): if not pub.is_using_postgresql(): pytest.skip('this requires SQL') @@ -475,6 +487,7 @@ def test_backoffice_listing_order(pub): ids = [x.strip('/') for x in re.findall(r'data-link="(.*?)"', resp.text)] assert ids == list(reversed(last_update_time_order)) + def test_backoffice_listing_anonymised(pub): if not pub.is_using_postgresql(): pytest.skip('this requires SQL') @@ -493,6 +506,7 @@ def test_backoffice_listing_anonymised(pub): resp = app.get('/backoffice/management/form-title/?limit=500') assert resp.text.count('data-link') == 9 + def test_backoffice_legacy_urls(pub): create_superuser(pub) create_environment(pub) @@ -507,6 +521,7 @@ def test_backoffice_legacy_urls(pub): assert resp.location == 'http://example.net/backoffice/management/form-title/listing/foo?bla' resp = app.get('/backoffice/not-form-title/', status=404) + def test_backoffice_columns(pub): create_superuser(pub) create_environment(pub) @@ -521,6 +536,7 @@ def test_backoffice_columns(pub): assert resp.text.count('data-link') == 17 # 17 rows assert resp.text.count('FOO BAR') == 0 # no field 1 column + def test_backoffice_channel_column(pub): if not pub.site_options.has_section('variables'): pub.site_options.add_section('variables') @@ -540,6 +556,7 @@ def test_backoffice_channel_column(pub): assert resp.text.count('data-link') == 17 # 17 rows assert resp.text.count('Web') == 17 + def test_backoffice_submission_agent_column(pub): user = create_user(pub) create_environment(pub) @@ -578,6 +595,7 @@ def test_backoffice_submission_agent_column(pub): assert len(resp.text.splitlines()) == 18 # 17 + header line assert resp.text.count(',agent,') == 17 + def test_backoffice_image_column(pub): create_superuser(pub) create_environment(pub) @@ -596,6 +614,7 @@ def test_backoffice_image_column(pub): resp = app.get('/backoffice/management/form-title/') assert 'download?f=4&thumbnail=1' not in resp.text + def test_backoffice_filter(pub): create_superuser(pub) create_environment(pub) @@ -639,6 +658,7 @@ def test_backoffice_filter(pub): resp = resp.forms[0].submit() assert resp.text.count('baz') == 0 + def test_backoffice_default_filter(pub): create_superuser(pub) create_environment(pub) @@ -664,6 +684,7 @@ def test_backoffice_default_filter(pub): resp = app.get('/backoffice/management/form-title/') assert 'filter-4-value' in resp.form.fields + def test_backoffice_bool_filter(pub): create_superuser(pub) create_environment(pub) @@ -694,6 +715,7 @@ def test_backoffice_bool_filter(pub): assert resp.text.count('Yes') == 0 assert resp.text.count('No') > 0 + def test_backoffice_item_filter(pub): create_superuser(pub) create_environment(pub) @@ -766,6 +788,7 @@ def test_backoffice_item_filter(pub): else: assert [x['id'] for x in resp2.json['data']] == [u'â', u'b', u'd'] + def test_backoffice_item_double_filter(pub): if not pub.is_using_postgresql(): pytest.skip('this requires SQL') @@ -835,6 +858,7 @@ def test_backoffice_item_double_filter(pub): assert [x[0] for x in resp.form['filter-4-value'].options] == ['', 'a', 'b'] assert [x[0] for x in resp.form['filter-5-value'].options] == ['', 'A', 'B', 'C'] + def test_backoffice_bofield_item_filter(pub): create_superuser(pub) create_environment(pub) @@ -966,6 +990,7 @@ def test_backoffice_items_filter(pub): assert resp.text.count(u'b, d') == 0 assert resp.text.count('data-link') == 0 # no rows + def test_backoffice_csv(pub): create_superuser(pub) create_environment(pub) @@ -1026,6 +1051,7 @@ def test_backoffice_csv(pub): resp_csv = resp.click('Export as CSV File') assert 'Created' not in resp_csv.text.splitlines()[0] + def test_backoffice_export_long_listings(pub): create_superuser(pub) create_environment(pub) @@ -1069,6 +1095,7 @@ def test_backoffice_export_long_listings(pub): # check error handling app.get('/afterjobs/whatever', status=404) + def test_backoffice_csv_export_channel(pub): if not pub.site_options.has_section('variables'): pub.site_options.add_section('variables') @@ -1091,6 +1118,7 @@ def test_backoffice_csv_export_channel(pub): assert resp_csv.text.splitlines()[0].split(',')[1] == 'Channel' assert resp_csv.text.splitlines()[1].split(',')[1] == 'Web' + def test_backoffice_csv_export_anonymised(pub): fd = open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') pub.site_options.write(fd) @@ -1110,6 +1138,7 @@ def test_backoffice_csv_export_anonymised(pub): assert resp_csv.text.splitlines()[0].split(',')[-1] == 'Anonymised' assert resp_csv.text.splitlines()[1].split(',')[-1] == 'No' + def test_backoffice_ods(pub): create_superuser(pub) create_environment(pub) @@ -1195,6 +1224,7 @@ def test_backoffice_ods(pub): assert row.findall('.//{%s}table-cell' % ods.NS['table'])[ string_column].find('{%s}p' % ods.NS['text']).text == 'plop\nplop' + @pytest.mark.skipif('xlwt is None') def test_backoffice_xls(pub): create_superuser(pub) @@ -1214,6 +1244,7 @@ def test_backoffice_xls(pub): resp = resp.click('Excel Export') assert resp.headers['content-type'].startswith('application/vnd.ms-excel') + def test_backoffice_statistics(pub): create_superuser(pub) create_environment(pub) @@ -1235,6 +1266,7 @@ def test_backoffice_statistics(pub): assert '

Filters

' in resp.text assert 'End: 2013-01-01' in resp.text + def test_backoffice_multi_actions(pub): create_superuser(pub) create_environment(pub) @@ -1344,6 +1376,7 @@ def test_backoffice_multi_actions(pub): else: assert formdata.status != 'wf-accepted' + def test_backoffice_statistics_status_filter(pub): create_superuser(pub) create_environment(pub) @@ -1382,6 +1415,7 @@ def test_backoffice_statistics_status_filter(pub): resp = resp.forms[0].submit() assert 'Total number of records: 50' in resp.text + def test_backoffice_statistics_status_select(pub): create_superuser(pub) create_environment(pub) @@ -1430,6 +1464,7 @@ def test_backoffice_statistics_status_select(pub): resp = resp.click('Statistics') assert 'filter-2-value' in resp.form.fields + def test_backoffice_map(pub): user = create_user(pub) create_environment(pub) @@ -1466,6 +1501,7 @@ def test_backoffice_map(pub): resp = resp.click('Plot on a Map') assert 'tile.example.net/' in resp.text + def test_backoffice_geojson(pub): user = create_user(pub) create_environment(pub) @@ -1527,6 +1563,7 @@ def test_backoffice_handling(pub): assert FormDef.get_by_urlname('form-title').data_class().get(number31.id).status == 'wf-accepted' assert 'HELLO WORLD' in resp.text + def test_backoffice_handling_global_action(pub): create_user(pub) create_environment(pub) @@ -1563,6 +1600,7 @@ def test_backoffice_handling_global_action(pub): assert 'HELLO WORLD GLOBAL ACTION' in resp.text assert formdef.data_class().get(formdata.id).status == 'wf-finished' + def test_backoffice_global_remove_action(pub): create_user(pub) create_environment(pub) @@ -1596,6 +1634,7 @@ def test_backoffice_global_remove_action(pub): assert resp.request.url == 'http://example.net/backoffice/management/test-global-remove/' assert 'The form has been deleted.' in resp.text + def test_backoffice_submission_context(pub): user = create_user(pub) create_environment(pub) @@ -1632,6 +1671,7 @@ def test_backoffice_submission_context(pub): assert 'http://www.example.com/summary' in resp.text assert 'by %s' % user.get_display_name() in resp.text + def test_backoffice_geolocation_info(pub): user = create_user(pub) create_environment(pub) @@ -1660,6 +1700,7 @@ def test_backoffice_geolocation_info(pub): assert 'data-init-lng="2.32"' in resp.text assert 'data-init-lat="48.83' in resp.text + def test_backoffice_info_text(pub): create_user(pub) create_environment(pub) @@ -1752,6 +1793,7 @@ def test_backoffice_info_text(pub): resp = app.get('/backoffice/management/form-title/%s/' % number31.id) assert not 'backoffice-description' in resp.text + def test_backoffice_handling_post_dispatch(pub): # check a formdata that has been dispatched to another role is accessible # by an user with that role. @@ -1801,6 +1843,7 @@ def test_backoffice_handling_post_dispatch(pub): assert FormDef.get_by_urlname('form-title').data_class().get(number31.id).status == 'wf-accepted' assert 'HELLO WORLD' in resp.text + def test_global_statisticspub(pub): create_superuser(pub) create_environment(pub) @@ -1814,6 +1857,7 @@ def test_global_statisticspub(pub): resp = resp.forms[0].submit() assert 'Total count: 20' in resp.text + def test_backoffice_submission(pub): user = create_user(pub) create_environment(pub) @@ -1958,6 +2002,7 @@ def test_backoffice_submission_with_tracking_code(pub): resp = app.get(formdata_location) assert not formdata.tracking_code in resp.text + def test_backoffice_submission_welco(pub, welco_url): user = create_user(pub) create_environment(pub) @@ -1992,6 +2037,7 @@ def test_backoffice_submission_welco(pub, welco_url): # check agent name is displayed next to pending submission assert '(%s)' % user.display_name in resp.text + def test_backoffice_submission_initiated_from_welco(pub, welco_url): user = create_user(pub) create_environment(pub) @@ -2046,6 +2092,7 @@ def test_backoffice_submission_initiated_from_welco(pub, welco_url): assert pq('#steps li.current .label').text() == '1st PAGE' assert 'Field on 1st page' in resp.text # and in fields + def test_backoffice_submission_with_return_url(pub): user = create_user(pub) create_environment(pub) @@ -2082,6 +2129,7 @@ def test_backoffice_submission_with_return_url(pub): resp = resp.form.submit('submit') # -> to submit assert resp.location == 'https://example.org' + def test_backoffice_parallel_submission(pub): user = create_user(pub) create_environment(pub) @@ -2152,6 +2200,7 @@ def test_backoffice_parallel_submission(pub): resp4 = resp4.follow() assert 'This form has already been submitted.' in resp4.text + def test_backoffice_submission_dispatch(pub): user = create_user(pub) create_environment(pub) @@ -2215,6 +2264,7 @@ def test_backoffice_submission_dispatch(pub): # right role assert resp.location.startswith('http://example.net/backoffice/management/form-title/') + def test_backoffice_submission_tracking_code(pub): user = create_user(pub) create_environment(pub) @@ -2254,6 +2304,7 @@ def test_backoffice_submission_tracking_code(pub): assert 'Check values then click submit.' in resp.text assert 'test submission' in resp.text + def test_backoffice_submission_drafts(pub): user = create_user(pub) create_environment(pub) @@ -2307,6 +2358,7 @@ def test_backoffice_submission_drafts(pub): # check it kept the same id assert resp.location == 'http://example.net/backoffice/management/form-title/%s/' % formdata_no + def test_backoffice_submission_remove_drafts(pub): user = create_user(pub) create_environment(pub) @@ -2363,6 +2415,7 @@ def test_backoffice_submission_remove_drafts(pub): resp = app.get('/backoffice/submission/form-title/remove/%s' % formdata.id, status=403) + def test_backoffice_submission_live_condition(pub): user = create_user(pub) create_environment(pub) @@ -2405,6 +2458,7 @@ def test_backoffice_submission_live_condition(pub): assert 'Bar' in resp.text assert 'Foo' not in resp.text + def test_backoffice_submission_conditional_jump_based_on_bo_field(pub): user = create_superuser(pub) Workflow.wipe() @@ -2468,6 +2522,7 @@ def test_backoffice_submission_conditional_jump_based_on_bo_field(pub): assert formdef.data_class().count() == 1 assert formdef.data_class().select()[0].status == 'wf-st1' + def test_backoffice_submission_sections(pub): user = create_user(pub) create_environment(pub) @@ -2504,6 +2559,7 @@ def test_backoffice_submission_sections(pub): assert 'Running submission' in resp.text assert '>#%s' % formdata.id in resp.text + def test_backoffice_submission_prefill_user(pub): user = create_user(pub) create_environment(pub) @@ -2550,6 +2606,7 @@ def test_backoffice_submission_prefill_user(pub): # and check it was not prefilled assert resp.form['f1'].value == '' + def test_backoffice_submission_prefill_user_via_formula(pub): user = create_user(pub) create_environment(pub) @@ -2596,6 +2653,7 @@ def test_backoffice_submission_prefill_user_via_formula(pub): # and check it was not prefilled assert resp.form['f1'].value == '' + def test_backoffice_submission_prefill_user_multiple_pages(pub): user = create_user(pub) create_environment(pub) @@ -2685,6 +2743,7 @@ def test_backoffice_submission_prefill_user_multiple_pages(pub): assert formdef.data_class().count() == 1 assert formdef.data_class().select()[0].user_id is None + def test_backoffice_submission_multiple_page_restore_on_validation(pub): user = create_user(pub) create_environment(pub) @@ -2723,6 +2782,7 @@ def test_backoffice_submission_multiple_page_restore_on_validation(pub): resp = resp.follow() assert 'Check values then click submit.' in resp.text + def test_backoffice_submission_substitution_vars(pub): user = create_user(pub) create_environment(pub) @@ -2774,6 +2834,7 @@ def test_backoffice_submission_substitution_vars(pub): resp = resp.form.submit('submit') assert 'dj-PLOP-an-bar-go' in resp.text + def test_backoffice_submission_manual_channel(pub): user = create_user(pub) create_environment(pub) @@ -2812,6 +2873,7 @@ def test_backoffice_submission_manual_channel(pub): assert data_class.get(formdata_no).backoffice_submission is True assert data_class.get(formdata_no).submission_channel == 'mail' + def test_backoffice_submission_no_manual_channel_with_welco(pub, welco_url): user = create_user(pub) create_environment(pub) @@ -2824,6 +2886,7 @@ def test_backoffice_submission_no_manual_channel_with_welco(pub, welco_url): resp = app.get('/backoffice/submission/%s/' % formdef.url_name) assert 'submission_channel' not in resp.form.fields + def test_backoffice_submission_with_nameid_and_channel(pub, local_user): user = create_user(pub) create_environment(pub) @@ -2862,6 +2925,7 @@ def test_backoffice_submission_with_nameid_and_channel(pub, local_user): assert formdata.submission_channel == 'mail' assert formdata.status == 'wf-new' + def test_backoffice_wscall_failure_display(http_requests, pub): user = create_user(pub) create_environment(pub) @@ -2912,6 +2976,7 @@ def test_backoffice_wscall_failure_display(http_requests, pub): assert (' with the number %s.' % number31.get_display_id()) in resp.text assert not 'Error during webservice call' in resp.text + def test_backoffice_wscall_error_email(http_requests, pub, emails): pub.cfg['debug'] = {'error_email': 'errors@localhost.invalid'} pub.write_cfg() @@ -2966,6 +3031,7 @@ def test_backoffice_wscall_error_email(http_requests, pub, emails): assert '/form-title/%s/' % number31.id in error_email['payload'] assert error_email['msg']['References'] + def test_backoffice_wscall_attachment(http_requests, pub): create_user(pub) create_environment(pub) @@ -3033,6 +3099,7 @@ def test_backoffice_wscall_attachment(http_requests, pub): resp = resp.follow() assert resp.text == '' + def test_backoffice_wfedit(pub): user = create_user(pub) create_environment(pub) @@ -3077,6 +3144,7 @@ def test_backoffice_wfedit(pub): assert form_class().get(number31.id).data['2'] == 'bar' number31.store() + def test_backoffice_wfedit_submission(pub): user = create_user(pub) create_environment(pub) @@ -3127,6 +3195,7 @@ def test_backoffice_wfedit_submission(pub): number31.store() assert formdata_count == form_class.count() + def test_backoffice_wfedit_and_backoffice_fields(pub): user = create_user(pub) create_environment(pub) @@ -3169,6 +3238,7 @@ def test_backoffice_wfedit_and_backoffice_fields(pub): resp = resp.follow() assert form_class().get(number31.id).data['bo1'] == 'plop' + def test_backoffice_wfedit_and_data_source_with_user_info(pub): user = create_user(pub) create_environment(pub) @@ -3220,6 +3290,7 @@ def test_backoffice_wfedit_and_data_source_with_user_info(pub): assert urlopen.call_count == 2 resp = resp.follow() + def test_global_listing(pub): if not pub.is_using_postgresql(): pytest.skip('this requires SQL') @@ -3338,6 +3409,7 @@ def test_global_listing(pub): assert resp.text[resp.text.index('blah xxx' in resp.text + def test_management_views_with_no_formdefs(pub): if not pub.is_using_postgresql(): pytest.skip('this requires SQL') @@ -3398,6 +3471,7 @@ def test_management_views_with_no_formdefs(pub): resp = app.get('/backoffice/management/listing') assert 'This site is currently empty.' in resp.text + def test_category_in_global_listing(pub): if not pub.is_using_postgresql(): pytest.skip('this requires SQL') @@ -3437,6 +3511,7 @@ def test_category_in_global_listing(pub): assert 'management/other-form/' in resp.text assert not 'management/form-title/' in resp.text + def test_datetime_in_global_listing(pub): if not pub.is_using_postgresql(): pytest.skip('this requires SQL') @@ -3477,6 +3552,7 @@ def test_datetime_in_global_listing(pub): resp = resp.forms['listing-settings'].submit() assert resp.text[resp.text.index('Misc<' in partial_resp.text assert partial_resp.text.index('>Misc<') < partial_resp.text.index('>cat1<') + def test_count_open(pub): if not pub.is_using_postgresql(): pytest.skip('this requires SQL') @@ -3735,6 +3816,7 @@ def test_count_open(pub): resp = login(get_app(pub)).get('/backoffice/management/count?callback=toto') assert "20" in resp.text + def test_count_backoffice_drafts(pub): user = create_user(pub) create_environment(pub) @@ -3769,6 +3851,7 @@ def test_count_backoffice_drafts(pub): resp = login(get_app(pub)).get('/backoffice/submission/count?mode=existing') assert resp.json['count'] == 2 + def test_menu_json(pub): user = create_user(pub) create_environment(pub) @@ -3782,6 +3865,7 @@ def test_menu_json(pub): assert resp.text == 'foo(%s);' % menu_json_str assert resp.headers['content-type'] == 'application/javascript' + def test_per_user_view(pub): if not pub.is_using_postgresql(): pytest.skip('this requires SQL') @@ -3842,6 +3926,7 @@ def test_per_user_view(pub): resp = app.get('/backoffice/management/users/%s/' % user.id) assert resp.text.count('' in resp.text + def test_backoffice_backoffice_submission_in_listings(pub): create_superuser(pub) create_environment(pub) @@ -3918,6 +4005,7 @@ def test_backoffice_backoffice_submission_in_listings(pub): resp = app.get('/backoffice/management/form-title/') assert 'backoffice-submission' in resp.text + def test_backoffice_backoffice_submission_in_global_listing(pub): if not pub.is_using_postgresql(): pytest.skip('this requires SQL') @@ -3938,6 +4026,7 @@ def test_backoffice_backoffice_submission_in_global_listing(pub): resp = app.get('/backoffice/management/listing?limit=100') assert 'backoffice-submission' in resp.text + def test_backoffice_advisory_lock(pub): create_superuser(pub) create_environment(pub) @@ -4008,6 +4097,7 @@ def test_backoffice_advisory_lock(pub): assert not 'advisory-lock' in app2.get('/backoffice/management/form-title/') assert 'advisory-lock' in app.get('/backoffice/management/form-title/') + def test_backoffice_advisory_lock_related_formdatas(pub): if not pub.is_using_postgresql(): pytest.skip('this requires SQL') @@ -4078,6 +4168,7 @@ def test_backoffice_advisory_lock_related_formdatas(pub): session = pub.session_manager.session_class.select(lambda x: x.user == user.id)[0] assert 'formdata-other-form-%d' % other_formdata.id in session.visiting_objects.keys() + def test_backoffice_resubmit(pub): user = create_user(pub) create_environment(pub) @@ -4140,6 +4231,7 @@ def test_backoffice_resubmit(pub): assert 'Original form' in resp.text assert formdata.get_url(backoffice=True) in resp.text + def test_backoffice_workflow_display_form(pub): user = create_user(pub) create_environment(pub) @@ -4199,6 +4291,7 @@ def test_backoffice_workflow_display_form(pub): assert formdef.data_class().get(formdata.id).workflow_data == { 'blah_var_str': 'blah', 'blah_var_radio': 'c', 'blah_var_radio_raw': 'c'} + def test_backoffice_workflow_form_with_conditions(pub): user = create_user(pub) create_environment(pub) @@ -4313,6 +4406,7 @@ def test_backoffice_workflow_form_with_conditions(pub): assert formdef.data_class().get(formdata.id).workflow_data == {'blah_var_str': 'xxx2', 'blah_var_str2': None} + def test_backoffice_workflow_form_with_live_data_source(pub): user = create_user(pub) create_environment(pub) @@ -4368,6 +4462,7 @@ def test_backoffice_workflow_form_with_live_data_source(pub): live_resp = app.post(live_url + '?modified_field_id=1', params=resp.form.submit_fields()) assert live_resp.json['result']['2']['items'] == [{u'text': u'hello', u'id': u'C'}, {u'text': u'world', u'id': u'D'}] + def test_backoffice_criticality_in_formdef_listing(pub): if not pub.is_using_postgresql(): pytest.skip('this requires SQL') @@ -4412,6 +4507,7 @@ def test_backoffice_criticality_in_formdef_listing(pub): assert resp.text.index(formdata1_str) < resp.text.index(formdata3_str) assert resp.text.index(formdata1_str) > resp.text.index(formdata4_str) + def test_backoffice_criticality_in_global_listing(pub): if not pub.is_using_postgresql(): pytest.skip('this requires SQL') @@ -4457,6 +4553,7 @@ def test_backoffice_criticality_in_global_listing(pub): assert resp.text.index(formdata1_str) < resp.text.index(formdata3_str) assert resp.text.index(formdata1_str) > resp.text.index(formdata4_str) + def test_backoffice_criticality_formdata_view(pub): user = create_user(pub) create_environment(pub) @@ -4496,6 +4593,7 @@ class IHateUnicode(object): def __repr__(self): return 'ok' + @pytest.fixture def local_user(): get_publisher().user_class.wipe() @@ -4506,6 +4604,7 @@ def local_user(): user.store() return user + def test_inspect_page(pub, local_user): create_user(pub) create_environment(pub) @@ -4667,6 +4766,7 @@ def test_inspect_page(pub, local_user): assert 'syntax error' in resp.text assert 'Invalid block tag' in resp.text + def test_workflow_jump_previous(pub): user = create_user(pub) create_environment(pub) @@ -4776,6 +4876,7 @@ def test_workflow_jump_previous(pub): formdata.jump_status('_previous') # pop () assert formdata.status == 'wf-%s' % st1.id + def test_workflow_jump_previous_on_submit(pub): user = create_user(pub) create_environment(pub) @@ -4835,6 +4936,7 @@ def test_workflow_jump_previous_on_submit(pub): assert formdef.data_class().get(formdata.id).status == 'wf-north' assert not formdef.data_class().get(formdata.id).workflow_data['_markers_stack'] + def test_workflow_jump_previous_auto(pub): user = create_user(pub) create_environment(pub) @@ -4888,6 +4990,7 @@ def test_workflow_jump_previous_auto(pub): assert formdata.status == 'wf-south' assert formdata.workflow_data['_markers_stack'] == [{'status_id': 'north'}] + def test_backoffice_fields(pub): user = create_user(pub) create_environment(pub) @@ -4938,6 +5041,7 @@ def test_backoffice_fields(pub): assert 'Backoffice Data' in resp.text assert 'Not set' in resp.text + def test_backoffice_logged_errors(pub): Workflow.wipe() workflow = Workflow.get_default_workflow() @@ -5027,6 +5131,7 @@ def test_backoffice_logged_errors(pub): resp = resp2.click('Failed to evaluate condition') assert not 'href="http://example.net/backoffice/management/test/' in resp.text + def test_backoffice_formdata_named_wscall(http_requests, pub): user = create_user(pub) create_environment(pub) @@ -5083,6 +5188,7 @@ def test_backoffice_formdata_named_wscall(http_requests, pub): resp = app.get('/backoffice/submission/test/') assert resp.html.find('div', {'data-field-id': '7'}).text.strip() == 'dja-bar-ngo' + def test_backoffice_session_var(pub): open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w').write('''[options] query_string_allowed_vars = foo,bar @@ -5118,6 +5224,7 @@ query_string_allowed_vars = foo,bar resp = resp.follow() assert resp.html.find('div', {'data-field-id': '7'}).text.strip() == 'django' + def test_backoffice_display_message(pub): user = create_user(pub) create_environment(pub) @@ -5186,6 +5293,7 @@ def test_backoffice_display_message(pub): assert 'message-to-all' in resp.text assert resp.text.index('message-to-all') > resp.text.index('message-to-receiver') + def test_backoffice_forms_condition_on_button(pub): create_superuser(pub) create_environment(pub, set_receiver=True) @@ -5248,6 +5356,7 @@ def test_backoffice_forms_condition_on_button(pub): resp = resp.follow() assert '8 open on 50' in resp.text # only the accepted ones + def test_workflow_inspect_page(pub): create_superuser(pub) create_environment(pub, set_receiver=True) @@ -5273,6 +5382,7 @@ def test_workflow_inspect_page(pub): resp = app.get('/backoffice/workflows/%s/inspect' % workflow.id) assert '23 hours' in resp.text + def test_workflow_comment_required(pub): user = create_user(pub) create_environment(pub) @@ -5322,6 +5432,7 @@ def test_workflow_comment_required(pub): assert 'widget-with-error' not in resp.text assert 'HELLO WORLD 2' in resp.text + def test_carddata_management(pub, studio): CardDef.wipe() user = create_user(pub) @@ -5373,6 +5484,7 @@ def test_carddata_management(pub, studio): resp = resp.click('foo') assert resp.text.count('[foo]

') @@ -30,6 +34,7 @@ def test_simple_qualifier(): template.generate(output, {'foo': 'bar'}) assert output.getvalue() == '

bar

' + def test_simple_qualifier_missing_variable(): template = Template() template.parse('

[foo]

') @@ -37,6 +42,7 @@ def test_simple_qualifier_missing_variable(): template.generate(output, {}) assert output.getvalue() == '

[foo]

' + def test_if_any(): template = Template() template.parse('

[if-any foo]bar[end]

') @@ -56,6 +62,7 @@ def test_if_any(): template.generate(output, {'foo': False}) assert output.getvalue() == '

bar

' + def test_if_any_else(): template = Template() template.parse('

[if-any foo]bar[else]baz[end]

') @@ -68,6 +75,7 @@ def test_if_any_else(): template.generate(output, {}) assert output.getvalue() == '

baz

' + def test_is(): template = Template() template.parse('

[is foo "bar"]bar[end]

') @@ -87,6 +95,7 @@ def test_is(): template.generate(output, {'foo': 'bar'}) assert output.getvalue() == '

bar

' + def test_callable_qualifier(): template = Template() template.parse('

[foo]

') @@ -94,6 +103,7 @@ def test_callable_qualifier(): template.generate(output, {'foo': lambda x: x.write('bar')}) assert output.getvalue() == '

bar

' + def test_date_qualifier(pub): template = Template() template.parse('

[foo]

') @@ -107,6 +117,7 @@ def test_date_qualifier(pub): template.generate(output, {'foo': datetime.date(2019, 1, 2)}) assert output.getvalue() == '

02/01/2019

' + def test_datetime_qualifier(pub): template = Template() template.parse('

[foo]

') @@ -120,6 +131,7 @@ def test_datetime_qualifier(pub): template.generate(output, {'foo': datetime.datetime(2019, 1, 2, 14, 4)}) assert output.getvalue() == '

02/01/2019 14:04

' + def test_unclosed_block(): template = Template() with pytest.raises(UnclosedBlocksError): @@ -129,6 +141,7 @@ def test_unclosed_block(): except UnclosedBlocksError as e: assert e.column == 19 and e.line == 0 + def test_unmatched_end(): template = Template() with pytest.raises(UnmatchedEndError): @@ -138,6 +151,7 @@ def test_unmatched_end(): except UnmatchedEndError as e: assert e.column == 15 and e.line == 0 + def test_unmatched_else(): template = Template() with pytest.raises(UnmatchedElseError): @@ -147,6 +161,7 @@ def test_unmatched_else(): except UnmatchedElseError as e: assert e.column == 3 and e.line == 0 + def test_missing_is_arg(): template = Template() with pytest.raises(ArgCountSyntaxError): @@ -156,6 +171,7 @@ def test_missing_is_arg(): except ArgCountSyntaxError as e: assert e.column == 5 and e.line == 1 + def test_array_index(): template = Template() template.parse('

[foo.0]

') @@ -169,6 +185,7 @@ def test_array_index(): template.generate(output, {'foo': ['bar']}) assert output.getvalue() == '

[foo.bar]

' + def test_array_subindex(): template = Template() template.parse('

[foo.0.1]

') @@ -176,6 +193,7 @@ def test_array_subindex(): template.generate(output, {'foo': [['bar', 'baz']]}) assert output.getvalue() == '

baz

' + def test_dict_index(): template = Template() template.parse('

[foo.a]

') @@ -189,6 +207,7 @@ def test_dict_index(): template.generate(output, {'foo': {'a': 'bar'}}) assert output.getvalue() == '

[foo.b]

' + def test_ezt_script(pub): os.mkdir(os.path.join(pub.app_dir, 'scripts')) fd = open(os.path.join(pub.app_dir, 'scripts', 'hello_world.py'), 'w') @@ -209,6 +228,7 @@ def test_ezt_script(pub): template.generate(output, vars) assert output.getvalue() == '

Hello fred

' + def test_re_parse(): assert _re_parse.split('[a]') == ['', 'a', None, ''] assert _re_parse.split('[a] [b]') == \ diff --git a/tests/test_fields.py b/tests/test_fields.py index e33bd8474..129b742a7 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -16,6 +16,7 @@ from wcs.formdef import FormDef from utilities import create_temporary_pub, MockSubstitutionVariables + def setup_module(module): cleanup() global pub @@ -24,19 +25,23 @@ def setup_module(module): pub._set_request(req) req.session = sessions.Session(id=1) + def teardown_module(module): global pub shutil.rmtree(pub.APP_DIR) + def test_fill_admin_form(): for klass in fields.field_classes: form = Form(use_tokens=False) klass().fill_admin_form(form) + def test_get_admin_attributes(): for klass in fields.field_classes: klass().get_admin_attributes() + def test_add_to_form(): for klass in fields.field_classes: form = Form(use_tokens=False) @@ -46,6 +51,7 @@ def test_add_to_form(): else: klass(label='foo').add_to_form(form) + def test_string(): # sample string assert fields.StringField().get_view_value('foo') == 'foo' @@ -74,6 +80,7 @@ def test_string(): assert str(fields.StringField().get_rst_view_value(url)) == url assert fields.StringField().get_csv_value(url) == [url] + def test_text(): assert fields.TextField().get_view_short_value('foo'*10) == ('foo'*10)[:25] + ' (...)' assert fields.TextField().get_view_value('foo') == '

foo

' @@ -92,15 +99,18 @@ def test_text(): assert 'cols="12"' in str(form.render()) assert 'rows="12"' in str(form.render()) + def test_email(): assert fields.EmailField().get_view_value('foo@localhost') == \ 'foo@localhost' assert fields.EmailField().get_rst_view_value('foo@localhost') == 'foo@localhost' + def test_bool(): assert fields.BoolField().get_view_value(True) == 'Yes' assert fields.BoolField().get_view_value(False) == 'No' + def test_bool_stats(): formdef = FormDef() formdef.url_name = 'title' @@ -114,6 +124,7 @@ def test_bool_stats(): stats = formdef.fields[0].stats(formdatas) assert re.findall('Yes.*75.*No.*25', str(stats)) + def test_items(): assert fields.ItemsField(items=['a', 'b', 'c']).get_view_value(['a', 'b']) == 'a, b' assert fields.ItemsField(items=['a', 'b', 'c']).get_csv_value(['a', 'b']) == ['a', 'b', ''] @@ -129,13 +140,16 @@ def test_items(): assert field.get_options() == [('1', 'foo', '1'), ('2', 'bar', '2')] assert field.get_options() == [('1', 'foo', '1'), ('2', 'bar', '2')] # twice for cached behaviour + def test_password(): assert fields.PasswordField().get_view_value('xxx') == '●'*8 + def test_file(): upload = Upload('/foo/bar', content_type='text/plain') assert fields.FileField().get_csv_value(upload) == ['/foo/bar'] + def test_page(): formdef = FormDef() formdef.fields = [] @@ -148,9 +162,11 @@ def test_page(): assert page.is_visible({'1': 'bar'}, formdef) is True assert page.is_visible({'1': 'baz'}, formdef) is False + def test_table(): assert 'prefill' not in fields.TableField().get_admin_attributes() + def test_title(): field = fields.TitleField(label='Foobar') form = Form(use_tokens=False) @@ -195,6 +211,7 @@ def test_title(): assert '<i>Foobar&eacute;</i>' in str(form.render()) assert field.unhtmled_label == 'Foobaré' + def test_subtitle(): field = fields.SubtitleField(label='Foobar') form = Form(use_tokens=False) @@ -239,6 +256,7 @@ def test_subtitle(): assert '<i>Foobar&eacute;</i>' in str(form.render()) assert field.unhtmled_label == 'Foobaré' + def test_comment(): field = fields.CommentField(label='Foobar') form = Form(use_tokens=False) @@ -285,6 +303,7 @@ def test_comment(): field = fields.CommentField(label='

Foobaré

') assert field.unhtmled_label == 'Foobaré' + def test_map(): assert fields.MapField().get_json_value('42.2;10.2') == {'lat': 42.2, 'lon': 10.2} assert fields.MapField().get_json_value('-42.2;10.2') == {'lat': -42.2, 'lon': 10.2} @@ -292,6 +311,7 @@ def test_map(): assert fields.MapField().get_json_value('') == None assert fields.MapField().get_json_value('foobar') == None + def test_item_render(): items_kwargs = [] items_kwargs.append({'items': ['aa', 'ab', 'ac']}) @@ -357,6 +377,7 @@ def test_item_render(): assert str(form.render()).count('') == 1 # --- assert str(form.render()).count('ab<' in str(form.render()) assert '>ac<' in str(form.render()) + def test_ranked_items(): field = fields.RankedItemsField(id='1', label='Foobar', items=['aa', 'ab', 'ac']) assert len(field.get_csv_heading()) == 3 assert field.get_csv_value({'aa': 2, 'ab': 1, 'ac': 3}) == ['ab', 'aa', 'ac'] + def test_table_rows(): field = fields.TableRowsField(id='1', label='Foobar', columns=['aa', 'ab', 'ac'], total_row=False) html_table = str(field.get_view_value([['A', 'B', 'C'], ['D', 'E', 'F']])) diff --git a/tests/test_form_pages.py b/tests/test_form_pages.py index 6fd2c4961..5360f8aa2 100644 --- a/tests/test_form_pages.py +++ b/tests/test_form_pages.py @@ -62,10 +62,12 @@ def assert_equal_zip(stream1, stream2): t1, t2 = z1.read(name), z2.read(name) assert t1 == t2, 'file "%s" differs' % name + def pytest_generate_tests(metafunc): if 'pub' in metafunc.fixturenames: metafunc.parametrize('pub', ['pickle', 'sql', 'pickle-templates', 'pickle-lazy'], indirect=True) + @pytest.fixture def pub(request, emails): pub = create_temporary_pub( @@ -96,6 +98,7 @@ def create_formdef(): formdef.store() return formdef + def create_user(pub): pub.user_class.wipe() PasswordAccount.wipe() @@ -110,6 +113,7 @@ def create_user(pub): account.store() return user + def create_user_and_admin(pub): pub.user_class.wipe() PasswordAccount.wipe() @@ -132,12 +136,14 @@ def create_user_and_admin(pub): account.store() return user, admin + def test_home(pub): create_formdef() home = get_app(pub).get('/') assert 'category-misc' in home.text assert 'test' in home.text + def test_home_with_user_forms(pub): user = create_user(pub) formdef = create_formdef() @@ -185,6 +191,7 @@ def test_home_with_user_forms(pub): resp = app.get('/test/%s' % draft.id, status=302) resp = resp.follow(status=403) + def test_home_category(pub): formdef = create_formdef() formdef.category_id = '1' @@ -194,6 +201,7 @@ def test_home_category(pub): assert not 'category-misc' in home.text assert 'test' in home.text + def test_home_two_categories(pub): formdef = create_formdef() formdef.category_id = '1' @@ -219,6 +227,7 @@ def test_home_two_categories(pub): assert 'category-barfoo' in resp.text # 2nd formdef assert not 'category-misc' in resp.text # no more "misc" category + def test_home_keywords(pub): formdef = create_formdef() formdef.category_id = '1' @@ -228,6 +237,7 @@ def test_home_keywords(pub): assert home.html.find('div', {'data-keywords': 'hello world'}) or home.html.find('div', {'data-keywords': 'world hello'}) assert home.html.find('li', {'data-keywords': 'hello world'}) or home.html.find('li', {'data-keywords': 'world hello'}) + def test_home_formdef_description(pub): formdef = create_formdef() formdef.description = 'HELLO WORLD' @@ -236,6 +246,7 @@ def test_home_formdef_description(pub): assert 'HELLO WORLD' in home.text assert 'test' in home.text + def test_home_disabled(pub): formdef = create_formdef() formdef.disabled = True @@ -246,6 +257,7 @@ def test_home_disabled(pub): # check access is denied resp = get_app(pub).get('/test/', status=403) + def test_home_disabled_with_redirect(pub): formdef = create_formdef() formdef.disabled = True @@ -256,6 +268,7 @@ def test_home_disabled_with_redirect(pub): resp = resp.click('test') assert resp.location == 'http://example.org' + def test_home_inaccessible(pub): formdef = create_formdef() formdef.roles = ['xxx'] @@ -264,6 +277,7 @@ def test_home_inaccessible(pub): assert home.status_int == 302 assert home.location == 'http://example.net/login/?next=http%3A%2F%2Fexample.net%2F' + def test_home_always_advertise(pub): formdef = create_formdef() formdef.roles = ['xxx'] @@ -273,6 +287,7 @@ def test_home_always_advertise(pub): assert 'test' in home.text assert 'test (authentication required)' in home.text + def test_home_redirect(pub): pub.cfg['misc']['homepage-redirect-url'] = 'http://www.example.com/' pub.write_cfg() @@ -281,6 +296,7 @@ def test_home_redirect(pub): assert home.status_int == 302 assert home.location == 'http://www.example.com/' + def test_home_redirect_var(pub): pub.cfg['misc']['homepage-redirect-url'] = 'http://www.example.com/[site_lang]/' pub.write_cfg() @@ -289,6 +305,7 @@ def test_home_redirect_var(pub): assert home.status_int == 302 assert home.location == 'http://www.example.com/en/' + def test_category_page(pub): formdef = create_formdef() formdef.category_id = '1' @@ -297,6 +314,7 @@ def test_category_page(pub): assert '

foobar

' in resp.text assert 'test' in resp.text + def test_category_page_redirect(pub): formdef = create_formdef() formdef.category_id = '1' @@ -308,6 +326,7 @@ def test_category_page_redirect(pub): assert resp.status_int == 302 assert resp.location == 'http://www.example.com/' + def test_category_page_redirect_var(pub): formdef = create_formdef() formdef.category_id = '1' @@ -319,6 +338,7 @@ def test_category_page_redirect_var(pub): assert resp.status_int == 302 assert resp.location == 'http://www.example.com/en/foobar/' + def test_form_access(pub): formdef = create_formdef() get_app(pub).get('/test/', status=200) @@ -368,6 +388,7 @@ def test_form_access(pub): user.store() login(get_app(pub), username='foo', password='foo').get('/test/', status=200) + def test_form_access_auth_context(pub): user = create_user(pub) @@ -402,6 +423,7 @@ def test_form_access_auth_context(pub): assert 'You need a stronger authentication level to fill this form.' not in resp.text assert resp.form + def test_form_submit(pub): formdef = create_formdef() formdef.data_class().wipe() @@ -416,6 +438,7 @@ def test_form_submit(pub): assert formdef.data_class().count() == 1 assert '
' in next_page.text + def test_form_submit_no_confirmation(pub): formdef = create_formdef() formdef.confirmation = False @@ -428,6 +451,7 @@ def test_form_submit_no_confirmation(pub): assert 'The form has been recorded' in next_page.text assert formdef.data_class().count() == 1 + def test_form_string_field_submit(pub): formdef = create_formdef() formdef.fields = [fields.StringField(id='0', label='string')] @@ -448,6 +472,7 @@ def test_form_string_field_submit(pub): data = formdef.data_class().get(data_id) assert data.data == {'0': 'foobar'} + def test_form_items_submit(pub): formdef = create_formdef() formdef.fields = [fields.ItemsField(id='0', label='items', type='items', @@ -472,6 +497,7 @@ def test_form_items_submit(pub): assert data.data['0'] == ['Foo', 'Bar'] assert data.data['0_display'] == 'Foo, Bar' + def test_form_string_with_invalid_xml_chars(pub): formdef = create_formdef() formdef.fields = [fields.StringField(id='0', label='string')] @@ -485,11 +511,13 @@ def test_form_string_with_invalid_xml_chars(pub): data = formdef.data_class().select()[0] assert data.data == {'0': 'helloworld'} + def assert_current_page(resp, page_label): for li_tag in resp.html.findAll('li'): if 'current' in li_tag.attrs['class']: assert li_tag.find_all('span')[-1].text == page_label + def test_form_multi_page(pub): for initial_condition in (None, 'True'): formdef = create_formdef() @@ -563,6 +591,7 @@ def test_form_multi_page_condition(pub): resp = resp.forms[0].submit('previous') assert resp.forms[0]['f1'] + def test_form_multi_page_condition_select(pub): formdef = create_formdef() formdef.fields = [fields.PageField(id='0', label='1st page', type='page'), @@ -592,6 +621,7 @@ def test_form_multi_page_condition_select(pub): assert '3rd page' in resp.text assert_current_page(resp, '3rd page') + def test_form_multi_page_condition_select_new_varname(pub): formdef = create_formdef() formdef.fields = [fields.PageField(id='0', label='1st page', type='page'), @@ -621,6 +651,7 @@ def test_form_multi_page_condition_select_new_varname(pub): assert '3rd page' in resp.text assert_current_page(resp, '3rd page') + def test_form_multi_page_condition_checkbox(pub): formdef = create_formdef() formdef.fields = [fields.PageField(id='0', label='1st page', type='page'), @@ -641,6 +672,7 @@ def test_form_multi_page_condition_checkbox(pub): resp = resp.forms[0].submit('submit') # should go to second page assert 'f3' in resp.forms[0].fields + def test_form_multi_page_condition_json_check(pub): # make sure the json export has no value for fields from hidden pages formdef = create_formdef() @@ -682,6 +714,7 @@ def test_form_multi_page_condition_json_check(pub): assert json_dict['fields']['st2'] == 'VALUE F3' assert json_dict['fields']['st3'] is None + def test_form_multi_page_condition_no_confirmation_json_check(pub): # same as above but without the confirmation page. formdef = create_formdef() @@ -727,6 +760,7 @@ def test_form_multi_page_condition_no_confirmation_json_check(pub): assert json_dict['fields']['st2'] == 'VALUE F3' assert json_dict['fields']['st3'] is None + def test_form_multi_page_condition_data_source(pub): formdef = create_formdef() formdef.fields = [fields.PageField(id='0', label='1st page', type='page'), @@ -759,6 +793,7 @@ def test_form_multi_page_condition_data_source(pub): resp = resp.forms[0].submit('submit') # should go to second page assert 'f3' in resp.forms[0].fields + def test_form_multi_page_condition_data_source_with_form_variable(pub): # this tries to recreate #8272 which is about a json datasource being # used in a page condition and taking a value from the given page to @@ -791,6 +826,7 @@ def test_form_multi_page_condition_data_source_with_form_variable(pub): resp = resp.forms[0].submit('submit') # should go to second page assert 'f3' in resp.forms[0].fields + def test_form_multi_page_condition_on_first_page(pub): formdef = create_formdef() formdef.fields = [ @@ -822,6 +858,7 @@ def test_form_multi_page_condition_on_first_page(pub): with pytest.raises(AssertionError): resp.form.get('previous') + def test_form_multi_page_condition_on_first_and_next(pub): formdef = create_formdef() formdef.fields = [ @@ -860,6 +897,7 @@ def test_form_multi_page_condition_on_first_and_next(pub): assert data.data.get('1') == 'xxx' assert data.data.get('3') is None + def test_form_multi_page_condition_no_visible_page(pub): formdef = create_formdef() formdef.fields = [ @@ -872,6 +910,7 @@ def test_form_multi_page_condition_no_visible_page(pub): formdef.store() get_app(pub).get('/test/', status=404) + def test_form_multi_page_many_conditions(pub): formdef = create_formdef() formdef.fields = [ @@ -898,6 +937,7 @@ def test_form_multi_page_many_conditions(pub): resp = get_app(pub).get('/test/') assert invalidate_cache.call_count == call_count + def test_form_multi_page_condition_stored_values(pub): formdef = create_formdef() formdef.fields = [fields.PageField(id='0', label='1st page', type='page'), @@ -952,6 +992,7 @@ def test_form_multi_page_condition_stored_values(pub): assert formdata.data['1'] == 'blah' assert formdata.data.get('3') is None + def test_form_multi_page_post_conditions(pub): formdef = create_formdef() formdef.fields = [fields.PageField(id='0', label='1st page', type='page'), @@ -1045,6 +1086,7 @@ def test_form_multi_page_post_conditions(pub): resp = resp.forms[0].submit('submit') assert 'Check values then click submit.' in resp.text + def test_form_multi_page_conditions_and_post_conditions(pub): formdef = create_formdef() formdef.fields = [ @@ -1086,6 +1128,7 @@ def test_form_multi_page_conditions_and_post_conditions(pub): resp = resp.form.submit('submit') assert 'You shall not pass.' in resp.text + def test_form_multi_page_page_name_as_title(pub): formdef = create_formdef() formdef.fields = [fields.PageField(id='0', label='1st page', type='page'), @@ -1127,6 +1170,7 @@ def test_form_multi_page_page_name_as_title(pub): assert 'Check values then click submit.' in next_page.text assert next_page.text.count('1st page') == 2 # in steps and in main body + def test_form_submit_with_user(pub, emails): create_user(pub) formdef = create_formdef() @@ -1144,6 +1188,7 @@ def test_form_submit_with_user(pub, emails): assert emails.emails.get('New form (test)') assert emails.emails.get('New form (test)')['email_rcpt'] == ['foo@localhost'] + def test_form_titles(pub): formdef = create_formdef() formdef.fields = [ @@ -1174,6 +1219,7 @@ def test_form_titles(pub): assert '

subtitle of 1st page

' in resp.text assert '

title of second page

' in resp.text + def test_form_summary_empty_pages(pub): user = create_user(pub) formdef = create_formdef() @@ -1238,6 +1284,7 @@ def test_form_summary_empty_pages(pub): assert '

3rd page

' in resp.text assert '

4th page

' not in resp.text + def test_form_display_locations(pub): formdef = create_formdef() formdef.fields = [ @@ -1270,6 +1317,7 @@ def test_form_display_locations(pub): assert 'plop3' in resp.text assert 'Bla bla bla' in resp.text + def test_multipage_form_display_locations(pub): formdef = create_formdef() formdef.fields = [ @@ -1292,6 +1340,7 @@ def test_multipage_form_display_locations(pub): assert '

2nd page

' in resp.text # page 2 title assert 'Bla bla bla' in resp.text # and page 2 comment field + def test_form_visit_existing(pub): user = create_user(pub) formdef = create_formdef() @@ -1314,6 +1363,7 @@ def test_form_visit_existing(pub): resp = login(get_app(pub), username='foo', password='foo').get('/test/%s/' % formdata_user.id) assert 'The form has been recorded on' in resp + def test_form_auth(pub): create_user(pub) formdef = create_formdef() @@ -1324,6 +1374,7 @@ def test_form_auth(pub): resp = login(get_app(pub), username='foo', password='foo').get('/test/auth') assert resp.location == 'http://example.net/test/' + def test_form_tryauth(pub): create_user(pub) formdef = create_formdef() @@ -1345,6 +1396,7 @@ def test_form_tryauth(pub): pub.cfg['identification'] = {'methods': ['password']} pub.write_cfg() + def test_form_forceauth(pub): create_user(pub) formdef = create_formdef() @@ -1352,6 +1404,7 @@ def test_form_forceauth(pub): resp = get_app(pub).get('/test/forceauth') assert resp.location == 'http://example.net/login/?ReturnUrl=http%3A//example.net/test/&forceAuthn=true' + def test_form_no_tracking_code(pub): formdef = create_formdef() formdef.data_class().wipe() @@ -1361,6 +1414,7 @@ def test_form_no_tracking_code(pub): resp = get_app(pub).get('/test/') assert not '

Tracking code

' in resp.text + def test_form_no_tracking_code_variable(pub): create_user(pub) FormDef.wipe() @@ -1398,6 +1452,7 @@ def get_displayed_tracking_code(resp): break return tracking_code + def test_form_tracking_code(pub, nocache): formdef = create_formdef() formdef.fields = [fields.StringField(id='0', label='string')] @@ -1499,6 +1554,7 @@ def test_form_tracking_code(pub, nocache): assert resp.location == 'http://example.net/test/%s' % formdata_id resp = resp.follow() + def test_form_tracking_code_rate_limit(pub, freezer): pub.load_site_options() if not pub.site_options.has_section('options'): @@ -1519,6 +1575,7 @@ def test_form_tracking_code_rate_limit(pub, freezer): # and ok again get_app(pub).get('/code/ABC/load', status=404) + def test_form_tracking_code_as_user(pub, nocache): user = create_user(pub) formdef = create_formdef() @@ -1599,6 +1656,7 @@ def test_form_tracking_code_as_user(pub, nocache): resp = app.get('/code/%s/load' % tracking_code, headers={'User-agent': 'Googlebot'}, status=403) + def test_form_empty_tracking_code(pub, nocache): formdef = create_formdef() formdef.fields = [fields.StringField(id='0', label='string')] @@ -1618,6 +1676,7 @@ def test_form_empty_tracking_code(pub, nocache): assert resp.location == 'http://example.net/code/%s/load' % tracking_code resp = resp.follow(status=404) + def test_form_tracking_code_email(pub, emails, nocache): formdef = create_formdef() formdef.data_class().wipe() @@ -1647,6 +1706,7 @@ def test_form_tracking_code_email(pub, emails, nocache): resp = resp.follow() assert resp.forms[1]['f0'].value == 'barfoo' + def test_form_tracking_code_remove_draft(pub, nocache): formdef = create_formdef() formdef.fields = [fields.StringField(id='0', label='string')] @@ -1693,6 +1753,7 @@ def test_form_tracking_code_remove_draft(pub, nocache): assert resp.location == 'http://example.net/' assert formdef.data_class().count() == 0 + def test_form_tracking_code_remove_empty_draft(pub, nocache): formdef = create_formdef() formdef.fields = [fields.StringField(id='0', label='string')] @@ -1741,6 +1802,7 @@ def test_form_tracking_code_remove_empty_draft(pub, nocache): assert resp.location == 'http://example.net/' assert formdef.data_class().count() == 0 + def test_form_discard_draft(pub, nocache): user = create_user(pub) @@ -1839,6 +1901,7 @@ def test_form_discard_draft(pub, nocache): resp = resp.forms[1].submit('cancel') assert [x.status for x in formdef.data_class().select()] == ['draft'] + def test_form_invalid_tracking_code(pub, nocache): formdef = create_formdef() formdef.fields = [fields.StringField(id='0', label='string')] @@ -1891,6 +1954,7 @@ def test_form_invalid_tracking_code(pub, nocache): assert resp.location == 'http://example.net/code/%s/load' % code.id resp = resp.follow(status=404) + def test_form_tracking_code_as_variable(pub, nocache): formdef = create_formdef() formdef.fields = [fields.PageField(id='0', label='1st page', type='page'), @@ -1908,6 +1972,7 @@ def test_form_tracking_code_as_variable(pub, nocache): assert tracking_code is not None assert '!%s!' % tracking_code in resp.text + def test_form_draft_with_file(pub): user = create_user(pub) formdef = create_formdef() @@ -1945,6 +2010,7 @@ def test_form_draft_with_file(pub): assert 'download="test.txt"' in resp.text assert resp.click('test.txt').follow().text == 'foobar' + def test_form_draft_with_file_direct_validation(pub): user = create_user(pub) formdef = create_formdef() @@ -1970,6 +2036,7 @@ def test_form_draft_with_file_direct_validation(pub): assert 'download="test2.txt"' in resp.text assert resp.click('test2.txt').follow().text == 'foobar2' + def test_form_draft_with_date(pub): user = create_user(pub) formdef = create_formdef() @@ -1994,6 +2061,7 @@ def test_form_draft_with_date(pub): assert '2012-02-12' in resp.text + @pytest.mark.parametrize('tracking_code', [True, False]) def test_form_direct_draft_access(pub, tracking_code): user = create_user(pub) @@ -2022,6 +2090,7 @@ def test_form_direct_draft_access(pub, tracking_code): formdata.store() resp = login(get_app(pub), 'foo', 'foo').get('/test/%s' % formdata.id, status=403) + def form_password_field_submit(app, password): formdef = create_formdef() formdef.enable_tracking_codes = True @@ -2049,12 +2118,14 @@ def form_password_field_submit(app, password): 'cleartext': force_str(password), }} + def test_form_password_field_submit(pub): user = create_user(pub) form_password_field_submit(get_app(pub), 'foobar') form_password_field_submit(get_app(pub), force_str(u'• 83003706')) form_password_field_submit(login(get_app(pub), username='foo', password='foo'), 'foobar\u00eb') + def test_form_multi_page_formdef_count_condition(pub): formdef = create_formdef() formdef.fields = [fields.PageField(id='0', label='1st page', type='page'), @@ -2080,6 +2151,7 @@ def test_form_multi_page_formdef_count_condition(pub): resp = resp.forms[0].submit('submit') # should NOT go straight to validation assert 'Check values then click submit.' not in resp.text + def test_form_multi_page_post_edit(pub): user = create_user(pub) @@ -2201,6 +2273,7 @@ def test_form_multi_page_post_edit(pub): logged_error = LoggedError.select()[0] assert logged_error.occurences_count == 2 + def test_form_count_dispatching(pub): user = create_user(pub) @@ -2241,6 +2314,7 @@ def test_form_count_dispatching(pub): assert len(formdef.data_class().select(clause=lambda x: x.status == 'wf-st2')) == 1 assert len(formdef.data_class().select(clause=lambda x: x.status == 'wf-st1')) == 1 + def test_preview_form(pub): user = create_user(pub) @@ -2266,6 +2340,7 @@ def test_preview_form(pub): assert 'This was only a preview: form was not actually submitted.' in next_page.text assert len([x for x in formdef.data_class().select() if not x.is_draft()]) == 0 + def test_form_item_data_source_field_submit(pub): def submit_item_data_source_field(ds): formdef = create_formdef() @@ -2299,6 +2374,7 @@ def test_form_item_data_source_field_submit(pub): assert submit_item_data_source_field(ds) == { '0': '1', '0_display': 'un', '0_structured': {'id': '1', 'text': 'un', 'more': 'foo'}} + def test_form_items_data_source_field_submit(pub): def submit_items_data_source_field(ds): formdef = create_formdef() @@ -2339,6 +2415,7 @@ def test_form_items_data_source_field_submit(pub): {'id': '1', 'more': 'foo', 'text': 'un'}, {'id': '3', 'more': 'baz', 'text': 'trois'}]} + def test_form_page_string_prefill(pub): user = create_user(pub) formdef = create_formdef() @@ -2351,6 +2428,7 @@ def test_form_page_string_prefill(pub): assert resp.forms[0]['f0'].value == 'HELLO WORLD' assert 'widget-prefilled' in resp.text + def test_form_page_profile_prefill(pub): user = create_user(pub) formdef = create_formdef() @@ -2365,6 +2443,7 @@ def test_form_page_profile_prefill(pub): resp = login(get_app(pub), username='foo', password='foo').get('/test/') assert resp.forms[0]['f0'].value == 'foo@localhost' + def test_form_page_profile_first_name_prefill(pub): user = create_user(pub) @@ -2411,6 +2490,7 @@ def test_form_page_profile_first_name_prefill(pub): assert resp.forms[0]['f0'].value == 'plop' assert resp.forms[0]['f1'].value == 'mytown' + def test_form_page_formula_prefill(pub): user = create_user(pub) formdef = create_formdef() @@ -2423,6 +2503,7 @@ def test_form_page_formula_prefill(pub): assert resp.forms[0]['f0'].value == 'HELLO WORLD' assert 'widget-prefilled' in resp.text + def test_form_page_template_prefill(pub): user = create_user(pub) formdef = create_formdef() @@ -2447,6 +2528,7 @@ def test_form_page_template_prefill(pub): assert resp.form['f0'].value == '' assert 'widget-prefilled' not in resp.text + def test_form_page_session_var_prefill(pub): user = create_user(pub) formdef = create_formdef() @@ -2504,6 +2586,7 @@ query_string_allowed_vars = foo,bar os.unlink(os.path.join(pub.app_dir, 'site-options.cfg')) + def test_form_page_query_string_list_prefill(pub): user = create_user(pub) formdef = create_formdef() @@ -2533,6 +2616,7 @@ def test_form_page_query_string_list_prefill(pub): resp = resp.form.submit('submit').follow() assert 'deux' in resp.text + def test_form_page_profile_prefill_list(pub): user = create_user(pub) formdef = create_formdef() @@ -2557,6 +2641,7 @@ def test_form_page_profile_prefill_list(pub): assert 'invalid value selected' in resp.text assert resp.forms[0]['f0'].value == '' + def test_form_page_formula_prefill_items_field(pub): user = create_user(pub) formdef = create_formdef() @@ -2633,6 +2718,7 @@ def test_form_captcha(pub): assert 'Check values then click submit.' in resp.text assert not 'form_captcha' in resp.text + def test_form_captcha_and_no_validation_page(pub): user = create_user(pub) formdef = create_formdef() @@ -2659,6 +2745,7 @@ def test_form_captcha_and_no_validation_page(pub): assert 'Check values then click submit.' in resp.text assert 'form_captcha' in resp.text + def test_form_file_field_with_fargo(pub, fargo_url): user = create_user(pub) formdef = create_formdef() @@ -2689,6 +2776,7 @@ def test_form_file_field_with_fargo(pub, fargo_url): assert 'fargo.js' not in resp.text assert 'use-file-from-fargo' not in resp.text + def test_form_file_field_without_fargo(pub): user = create_user(pub) formdef = create_formdef() @@ -2710,6 +2798,7 @@ def test_form_file_field_without_fargo(pub): assert 'fargo.js' not in resp.text assert 'use-file-from-fargo' not in resp.text + def test_form_file_field_submit(pub): formdef = create_formdef() formdef.fields = [fields.FileField(id='0', label='file')] @@ -2732,6 +2821,7 @@ def test_form_file_field_submit(pub): assert resp.content_type == 'text/plain' assert resp.text == 'foobar' + def test_form_file_field_image_submit(pub): formdef = create_formdef() formdef.fields = [fields.FileField(id='0', label='file')] @@ -2775,6 +2865,7 @@ def test_form_file_field_image_submit(pub): resp = resp.forms[0].submit('submit') assert not ' 1 + def test_form_autosave(pub): formdef = create_formdef() formdef.fields = [fields.PageField(id='0', label='1st page', type='page'), @@ -4490,6 +4615,7 @@ def test_form_autosave(pub): assert json.loads(ajax_resp.text)['result'] == 'error' assert formdef.data_class().select()[0].data['1'] == 'foobar3' + def test_form_autosave_with_items_field(pub): formdef = create_formdef() formdef.data_class().wipe() @@ -4521,6 +4647,7 @@ def test_form_autosave_with_items_field(pub): assert formdef.data_class().select()[0].data['1'] == 'bar' assert formdef.data_class().select()[0].data['3'] == ['poire', 'abricot'] + def test_form_autosave_with_parameterized_datasource(pub): formdef = create_formdef() formdef.fields = [fields.PageField(id='0', label='1st page', type='page'), @@ -4551,6 +4678,7 @@ def test_form_autosave_with_parameterized_datasource(pub): assert formdef.data_class().select()[0].data['3'] == '1' assert formdef.data_class().select()[0].data['3_display'] == 'barbar' + def test_form_string_field_autocomplete(pub): formdef = create_formdef() formdef.fields = [fields.StringField(id='0', label='string', type='string', required=False)] @@ -4575,6 +4703,7 @@ def test_form_string_field_autocomplete(pub): assert ').autocomplete({' in resp.text assert 'http://example.net' in resp.text + def test_form_workflow_trigger(pub): user = create_user(pub) @@ -4647,6 +4776,7 @@ def test_form_workflow_trigger(pub): formdata = formdef.data_class().get(formdata.id) assert formdata.workflow_data.get('data') == {'foo': 'bar'} + def test_form_worklow_multiple_identical_status(pub): user = create_user(pub) @@ -4730,6 +4860,7 @@ def test_form_worklow_multiple_identical_status(pub): assert resp.text.count('new-evolution-1') == 1 assert resp.text.count('new-evolution-2') == 1 + def test_form_worklow_comments_on_same_status(pub): pub.session_manager.session_class.wipe() user = create_user(pub) @@ -4875,6 +5006,7 @@ def test_display_message(pub): page = app.get(formdata.get_url()) assert 'warningnotice' in page.text + def test_workflow_condition_on_message(pub): user = create_user(pub) @@ -4914,6 +5046,7 @@ def test_workflow_condition_on_message(pub): page = app.get(formdata.get_url()) assert not 'message-to-all' in page.text + def test_session_cookie_flags(pub): formdef = create_formdef() app = get_app(pub) @@ -4928,6 +5061,7 @@ def test_session_cookie_flags(pub): assert 'httponly' in resp.headers['Set-Cookie'] assert 'secure' in resp.headers['Set-Cookie'] + def test_form_page_profile_verified_prefill(pub): user = create_user(pub) formdef = create_formdef() @@ -4976,6 +5110,7 @@ def test_form_page_profile_verified_prefill(pub): assert not 'Check values then click submit.' in resp.text assert resp.form['f0'].value == 'foo@localhost' + def test_form_page_profile_verified_date_prefill(pub): user = create_user(pub) @@ -5023,6 +5158,7 @@ def test_form_page_profile_verified_date_prefill(pub): assert len(formdatas) == 1 assert time.strftime('%Y-%m-%d', formdatas[0].data['0']) == '2018-09-27' + def test_form_page_profile_verified_radio_item_prefill(pub): user = create_user(pub) formdef = create_formdef() @@ -5056,6 +5192,7 @@ def test_form_page_profile_verified_radio_item_prefill(pub): assert 'Check values then click submit.' in resp.text assert resp.form['f0'].value == 'foo@localhost' # it is reverted + def test_item_field_from_cards(pub): user = create_user(pub) formdef = create_formdef() @@ -5096,6 +5233,7 @@ def test_item_field_from_cards(pub): assert formdef.data_class().select()[0].data['0_display'] == 'bar' assert formdef.data_class().select()[0].data['0_structured']['name'] == 'bar' + def test_item_field_with_disabled_items(http_requests, pub): user = create_user(pub) formdef = create_formdef() @@ -5193,6 +5331,7 @@ def test_item_field_with_disabled_items(http_requests, pub): resp = resp.form.submit('submit') # -> validation page assert 'There were errors processing the form' in resp.text + def test_items_field_with_disabled_items(http_requests, pub): user = create_user(pub) formdef = create_formdef() @@ -5243,6 +5382,7 @@ def test_items_field_with_disabled_items(http_requests, pub): assert formdef.data_class().select()[0].data['0'] == ['2'] assert formdef.data_class().select()[0].data['0_display'] == 'world' + def test_item_field_autocomplete_json_source(http_requests, pub): user = create_user(pub) formdef = create_formdef() @@ -5412,6 +5552,7 @@ remote.example.net = 1234 resp = resp.form.submit('submit') # -> submit assert formdef.data_class().select()[0].data['0'] is None + def test_item_field_autocomplete_jsonp_source(http_requests, pub): user = create_user(pub) formdef = create_formdef() @@ -5458,6 +5599,7 @@ def test_item_field_autocomplete_jsonp_source(http_requests, pub): # no _structured data for pure jsonp sources assert '0_structured' not in formdef.data_class().select()[0].data + def test_form_data_keywords(pub): formdef = create_formdef() formdef.keywords = 'hello,world' @@ -5474,6 +5616,7 @@ def test_form_data_keywords(pub): assert 'The form has been recorded' in resp.text assert formdef.data_class().count() == 1 + def test_logged_errors(pub): Workflow.wipe() workflow = Workflow.get_default_workflow() @@ -5520,6 +5663,7 @@ def test_logged_errors(pub): assert len(LoggedError.get_ids_with_indexed_value('workflow_id', '12')) == 1 assert len(LoggedError.get_ids_with_indexed_value('workflow_id', 'X')) == 0 + def test_formdata_named_wscall(http_requests, pub): create_user(pub) NamedWsCall.wipe() @@ -5588,6 +5732,7 @@ def test_formdata_named_wscall(http_requests, pub): formdata = formdef.data_class().select()[0] assert formdata.evolution[0].parts[0].content == 'Hello bar World' + def test_formdata_named_wscall_in_conditions(http_requests, pub): create_user(pub) NamedWsCall.wipe() @@ -5618,6 +5763,7 @@ def test_formdata_named_wscall_in_conditions(http_requests, pub): assert '>4th page<' in resp.text assert len(http_requests.requests) == 1 + def test_resubmit(pub): user = create_user(pub) @@ -5699,6 +5845,7 @@ def test_resubmit(pub): resp = resp.follow() assert resp.forms[1]['f2'].value == 'foo' + def test_form_custom_select_template(pub): formdef = create_formdef() formdef.fields = [ @@ -5727,6 +5874,7 @@ def test_form_custom_select_template(pub): resp = get_app(pub).get('/test/') assert 'substitution variable: http://remote.example.net/' in resp.text + def test_form_status_appearance_keywords(pub): create_user(pub) formdef = create_formdef() @@ -5761,6 +5909,7 @@ def test_form_status_appearance_keywords(pub): resp = get_app(pub).get('/test/') assert 'class="quixote foobar plop"' in resp.text + def test_user_global_action(pub): user = create_user(pub) @@ -5806,6 +5955,7 @@ def test_user_global_action(pub): assert 'HELLO WORLD GLOBAL ACTION' in resp.text assert formdef.data_class().get(formdata.id).status == 'wf-finished' + def test_user_global_action_same_status_store(pub): user = create_user(pub) @@ -5862,6 +6012,7 @@ def test_user_global_action_same_status_store(pub): assert formdef.data_class().get(formdata.id).status == 'wf-new' assert formdef.data_class().get(formdata.id).data['bo1'] == '123' + def test_condition_on_action(pub, emails): user = create_user(pub) @@ -5920,6 +6071,7 @@ def test_condition_on_action(pub, emails): resp = resp.form.submit('submit') assert emails.get('New form2 (test condition on action)') + def test_email_actions(pub, emails): user = create_user(pub) @@ -5992,6 +6144,7 @@ def test_email_actions(pub, emails): email_data = emails.get('New form2 (test email action)') assert len(re.findall(r'http.*? ', email_data['payload'])) == 2 + def test_manager_public_access(pub): user, manager = create_user_and_admin(pub) @@ -6064,6 +6217,7 @@ def test_manager_public_access(pub): resp = app.get(formdata.get_url()) assert 'The form has been recorded' in resp.text + def test_form_and_category_same_slug(pub): FormDef.wipe() formdef = FormDef() @@ -6075,6 +6229,7 @@ def test_form_and_category_same_slug(pub): resp = get_app(pub).get('/foobar/') assert resp.form + def test_field_condition(pub): FormDef.wipe() formdef = FormDef() @@ -6102,6 +6257,7 @@ def test_field_condition(pub): assert 'Bar' in resp.text assert 'Foo' not in resp.text + def test_field_unicode_condition(pub): FormDef.wipe() formdef = FormDef() @@ -6127,6 +6283,7 @@ def test_field_unicode_condition(pub): resp = resp.form.submit('submit') assert 'f4' in resp.form.fields + def test_field_unicode_condition_contains_in_list(pub): FormDef.wipe() formdef = FormDef() @@ -6152,6 +6309,7 @@ def test_field_unicode_condition_contains_in_list(pub): resp = resp.form.submit('submit') assert 'f4' in resp.form.fields + def test_field_unicode_condition_contains_in_string(pub): FormDef.wipe() formdef = FormDef() @@ -6177,6 +6335,7 @@ def test_field_unicode_condition_contains_in_string(pub): resp = resp.form.submit('submit') assert 'f4' in resp.form.fields + def test_field_unicode_condition_in_array(pub): FormDef.wipe() formdef = FormDef() @@ -6224,6 +6383,7 @@ def test_field_unicode_condition_in_array(pub): resp = resp.form.submit('submit').follow() assert '

Message CHECK OK

' in resp.text # check it's ok in workflow template + def test_field_live_condition(pub): FormDef.wipe() formdef = FormDef() @@ -6278,6 +6438,7 @@ def test_field_live_condition(pub): assert 'Bar' in resp.text assert 'Foo' in resp.text + def test_field_live_items_condition(pub): FormDef.wipe() formdef = FormDef() @@ -6315,6 +6476,7 @@ def test_field_live_items_condition(pub): assert live_resp.json['result']['1']['visible'] assert not live_resp.json['result']['2']['visible'] + def test_field_condition_on_required_field(pub): # from https://dev.entrouvert.org/issues/27247 FormDef.wipe() @@ -6358,6 +6520,7 @@ def test_field_condition_on_required_field(pub): assert '
required field
' in resp.text assert 'HELLO' not in resp.text + def test_field_live_condition_multipages(pub): FormDef.wipe() formdef = FormDef() @@ -6406,6 +6569,7 @@ def test_field_live_condition_multipages(pub): assert 'name="f4"' in resp.text resp = resp.form.submit('submit') + def test_field_live_select_content(pub, http_requests): FormDef.wipe() formdef = FormDef() @@ -6460,6 +6624,7 @@ def test_field_live_select_content(pub, http_requests): assert formdata.data['3'] == 'a' assert formdata.data['3_display'] == 'b' + def test_field_live_select_content_on_workflow_form(pub, http_requests): create_user(pub) wf = Workflow(name='wf-title') @@ -6542,6 +6707,7 @@ def test_field_live_select_content_on_workflow_form(pub, http_requests): assert formdata.workflow_data['xxx_var_foo_raw'] == 'a' assert formdata.workflow_data['xxx_var_foo'] == 'b' + def test_field_live_comment_content(pub, http_requests): FormDef.wipe() formdef = FormDef() @@ -6581,6 +6747,7 @@ def test_field_live_comment_content(pub, http_requests): live_resp = app.post('/foo/live', params=resp.form.submit_fields()) assert live_resp.json['result']['7']['content'] == '

bla hello bla

' + def test_form_edit_and_backoffice_field_change(pub): create_user(pub) @@ -6677,6 +6844,7 @@ def test_field_live_condition_unknown_page_id(pub): params = [(key, value if key != 'page_id' else 'eiuiu') for key, value in params] app.post('/foo/live', params=params) + def test_backoffice_fields_just_after_conditional_form_submit(pub): """ simulate selection of a structured list via condition on form, @@ -6746,6 +6914,7 @@ def test_backoffice_fields_just_after_conditional_form_submit(pub): assert formdata.data['bo1'] == 'None vs B2' assert formdata.data['bo2'] == ' vs moreB2' + def test_backoffice_fields_just_after_conditional_form_edit_action(pub): """ test unfeed on ConditionVars within edit context @@ -6838,6 +7007,7 @@ def test_backoffice_fields_just_after_conditional_form_edit_action(pub): assert formdata.data['bo1'] == 'A1 vs None' assert formdata.data['bo2'] == 'moreA1 vs ' + def test_form_recall_draft(pub): user = create_user(pub) @@ -6871,6 +7041,7 @@ def test_form_recall_draft(pub): assert 'href="%s"' % draft.id in resp.text assert 'href="%s"' % draft2.id in resp.text + def test_frontoffice_workflow_form_with_conditions(pub): user = create_user(pub) wf = Workflow.get_default_workflow() @@ -6987,6 +7158,7 @@ def test_frontoffice_workflow_form_with_conditions(pub): assert formdef.data_class().get(formdata.id).workflow_data == {'blah_var_str': 'xxx2', 'blah_var_str2': None} + def test_frontoffice_workflow_form_with_dynamic_comment(pub): user = create_user(pub) wf = Workflow.get_default_workflow() @@ -7031,6 +7203,7 @@ def test_frontoffice_workflow_form_with_dynamic_comment(pub): assert live_resp.json['result']['2']['visible'] assert live_resp.json['result']['2']['content'] == '

value is test

' + def test_frontoffice_workflow_form_with_impossible_condition(pub): user = create_user(pub) wf = Workflow.get_default_workflow() @@ -7071,6 +7244,7 @@ def test_frontoffice_workflow_form_with_impossible_condition(pub): assert 'f1' not in resp.form.fields assert resp.html.find('div', {'data-field-id': '2'}).attrs.get('style') == 'display: none' + def test_choice_button_ignore_form_errors(pub): user = create_user(pub) @@ -7140,6 +7314,7 @@ def test_choice_button_ignore_form_errors(pub): resp = resp.form.submit('button_x2').follow() assert 'Status2' in resp.text + def test_form_comment_is_hidden_attribute(pub): formdef = create_formdef() formdef.fields = [ diff --git a/tests/test_formdata.py b/tests/test_formdata.py index 1d24ca6d6..960f7b991 100644 --- a/tests/test_formdata.py +++ b/tests/test_formdata.py @@ -28,10 +28,12 @@ from utilities import create_temporary_pub, clean_temporary_pub from test_api import local_user + def pytest_generate_tests(metafunc): if 'pub' in metafunc.fixturenames: metafunc.parametrize('pub', ['pickle', 'sql'], indirect=True) + @pytest.fixture def pub(request): pub = create_temporary_pub(sql_mode=(request.param == 'sql')) @@ -72,6 +74,7 @@ def test_basic(pub): assert substvars.get('form_slug') == 'foobar' assert substvars.get('category_name') == 'test category' + def test_saved(pub): formdef.data_class().wipe() formdata = formdef.data_class()() @@ -83,6 +86,7 @@ def test_saved(pub): assert substvars.get('form_url_backoffice').endswith('/backoffice/management/foobar/1/') assert substvars.get('form_status_url').endswith('/foobar/1/status') + def test_auto_display_id(pub): formdef.data_class().wipe() formdata = formdef.data_class()() @@ -91,6 +95,7 @@ def test_auto_display_id(pub): assert substvars.get('form_number') == '%s-%s' % (formdef.id, formdata.id) assert substvars.get('form_number_raw') == str(formdata.id) + def test_manual_display_id(pub): formdef.data_class().wipe() formdata = formdef.data_class()() @@ -100,6 +105,7 @@ def test_manual_display_id(pub): assert substvars.get('form_number') == 'bar' assert substvars.get('form_number_raw') == str(formdata.id) + def test_submission_context(pub): formdef.data_class().wipe() formdata = formdef.data_class()() @@ -120,6 +126,7 @@ def test_submission_context(pub): assert substvars.get('form_submission_channel') is None assert substvars.get('form_submission_channel_label') == 'Web' + def test_just_created(pub): formdef.data_class().wipe() formdata = formdef.data_class()() @@ -133,6 +140,7 @@ def test_just_created(pub): assert substvars.get('form_receipt_datetime') assert substvars.get('form_evolution') + def test_field(pub): formdef.data_class().wipe() formdef.fields = [fields.StringField(id='0', label='string')] @@ -146,6 +154,7 @@ def test_field(pub): assert substvars.get('form_f0') == 'test' assert substvars.get('form_field_string') == 'test' + def test_field_varname(pub): formdef.data_class().wipe() formdef.fields = [fields.StringField(id='0', label='string', varname='foo')] @@ -156,6 +165,7 @@ def test_field_varname(pub): assert substvars.get('form_f0') == 'test' assert substvars.get('form_var_foo') == 'test' + def test_file_field(pub): formdef.data_class().wipe() formdef.fields = [fields.FileField(id='0', label='file', varname='foo')] @@ -182,6 +192,7 @@ def test_file_field(pub): assert substvars['form_var_foo_raw'] is None assert substvars['form_var_foo_url'] is None + def test_get_submitter(pub): formdef.data_class().wipe() formdef.fields = [fields.StringField(id='0', label='email', varname='foo', @@ -203,6 +214,7 @@ def test_get_submitter(pub): formdata.data = {} assert formdef.get_submitter_email(formdata) == 'bar@localhost' + def test_get_last_update_time(pub): formdef.data_class().wipe() formdef.store() @@ -226,6 +238,7 @@ def test_get_last_update_time(pub): formdata.evolution = None assert formdata.last_update_time == formdata.receipt_time + def test_password_field(pub): formdef.data_class().wipe() formdef.fields = [fields.PasswordField(id='0', label='pwd')] @@ -237,6 +250,7 @@ def test_password_field(pub): formdata2 = formdata.get(formdata.id) assert formdata2.data == {'0': {'cleartext': 'foo'}} + def test_date_field(pub): formdef.data_class().wipe() formdef.fields = [fields.DateField(id='0', label='date')] @@ -254,6 +268,7 @@ def test_date_field(pub): assert formdata2.get_substitution_variables()['form_field_date'] == '12/05/2015' pub.cfg['language'] = {'language': 'en'} + def test_clean_drafts(pub): formdef = FormDef() formdef.name = 'foo' @@ -279,6 +294,7 @@ def test_clean_drafts(pub): assert formdef.data_class().count() == 1 assert formdef.data_class().select()[0].id == d_id1 + def test_criticality_levels(pub): workflow = Workflow(name='criticality') workflow.criticality_levels = [ @@ -347,6 +363,7 @@ def test_criticality_levels(pub): assert d.get_static_substitution_variables().get('form_criticality_label') == 'green' assert d.get_substitution_variables().get('form_criticality_label') == 'green' + def test_field_item_substvars(pub): ds = { 'type': 'formula', @@ -366,6 +383,7 @@ def test_field_item_substvars(pub): assert variables.get('form_var_xxx') == 'un' assert variables.get('form_var_xxx_raw') == '1' + def test_get_json_export_dict_evolution(pub, local_user): Workflow.wipe() workflow = Workflow(name='test') @@ -439,6 +457,7 @@ def test_get_json_export_dict_evolution(pub, local_user): assert 'who' not in export['evolution'][0] assert 'parts' not in export['evolution'][1] + def test_field_bool_substvars(pub): formdef = FormDef() formdef.name = 'foobar' @@ -457,6 +476,7 @@ def test_field_bool_substvars(pub): assert variables.get('form_var_xxx') == 'True' assert variables.get('form_var_xxx_raw') is True + def test_backoffice_field_varname(pub): wf = Workflow(name='bo fields') wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(wf) @@ -476,6 +496,7 @@ def test_backoffice_field_varname(pub): substvars = formdata.get_substitution_variables() assert substvars.get('form_var_backoffice_blah') == 'test' + def test_workflow_data_file_url(pub): upload = PicklableUpload('test.txt', 'text/plain', 'ascii') upload.receive([b'first line', b'second line']) @@ -492,6 +513,7 @@ def test_workflow_data_file_url(pub): substvars = formdata.get_substitution_variables() assert substvars['foo_var_file_url'] + def test_evolution_get_status(pub): Workflow.wipe() workflow = Workflow(name='test') @@ -536,6 +558,7 @@ def test_evolution_get_status(pub): assert [x.get_status().id for x in d.evolution] == ['1', '1', '1', '2', '2'] + @pytest.fixture def variable_test_data(pub): pub.user_class.wipe() @@ -602,6 +625,7 @@ def variable_test_data(pub): pub.substitutions.feed(formdata) return LazyFormData(formdata) + def test_lazy_formdata(pub, variable_test_data): formdata = FormDef.select()[0].data_class().select()[0] lazy_formdata = LazyFormData(formdata) @@ -727,6 +751,7 @@ def test_lazy_formdata(pub, variable_test_data): lazy_formdata = LazyFormData(formdata) assert lazy_formdata.tracking_code == tracking_code.id + def test_lazy_formdata_queryset(pub, variable_test_data): lazy_formdata = variable_test_data data_class = lazy_formdata._formdef.data_class() @@ -786,6 +811,7 @@ def test_lazy_formdata_queryset(pub, variable_test_data): manager['drafts'] assert manager._cached_resultset is None + def test_lazy_formdata_queryset_distance(pub, variable_test_data): lazy_formdata = variable_test_data formdef = lazy_formdata._formdef @@ -825,6 +851,7 @@ def test_lazy_formdata_queryset_distance(pub, variable_test_data): assert bool(nearby) is False assert len(nearby) == 0 + def test_lazy_variables(pub, variable_test_data): formdata = FormDef.select()[0].data_class().select()[0] for mode in (None, 'lazy'): @@ -841,6 +868,7 @@ def test_lazy_variables(pub, variable_test_data): assert context['form_var_foo_foo_baz_baz'] == 'other' assert context['form_var_pwd_cleartext'] == 'a' + def test_lazy_variables_missing(pub, variable_test_data): formdef = FormDef.select()[0] formdata = formdef.data_class()() @@ -858,6 +886,7 @@ def test_lazy_variables_missing(pub, variable_test_data): with pytest.raises(KeyError): assert context['form_var_foo_foo_xxx'] == 'bar' + def test_lazy_map_variable(pub, variable_test_data): formdef = FormDef.select()[0] formdata = formdef.data_class().select()[0] @@ -896,6 +925,7 @@ def test_lazy_map_variable(pub, variable_test_data): assert WorkflowStatusItem.compute('{{ form_var_map|distance:"1;2"|floatformat }}', raises=True) == '' assert WorkflowStatusItem.compute('{{ "1;2"|distance:form_var_map|floatformat }}', raises=True) == '' + def test_lazy_conditions(pub, variable_test_data): condition = Condition({'type': 'django', 'value': 'form_var_foo_foo == "bar"'}) assert condition.evaluate() is True @@ -992,6 +1022,7 @@ def test_lazy_conditions(pub, variable_test_data): condition = Condition({'type': 'django', 'value': 'not form_var_datefield == form_var_barbarbar'}) assert condition.evaluate() is True + def test_has_role_templatetag(pub, variable_test_data): condition = Condition({'type': 'django', 'value': 'form_user|has_role:"foobar"'}) assert condition.evaluate() is False @@ -1018,6 +1049,7 @@ def test_has_role_templatetag(pub, variable_test_data): condition = Condition({'type': 'django', 'value': 'xxx|has_role:"foobar"'}) assert condition.evaluate() is False + def test_lazy_now_and_today(pub, variable_test_data): for condition_value in ( 'now > "1970-01-01"', @@ -1040,6 +1072,7 @@ def test_lazy_now_and_today(pub, variable_test_data): condition = Condition({'type': 'django', 'value': condition_value.replace('now', 'today')}) assert condition.evaluate() is True + def test_lazy_date_templatetags(pub, variable_test_data): for condition_value in ( '"2017-10-10"|date == "2017-10-10"', @@ -1112,6 +1145,7 @@ def test_lazy_date_templatetags(pub, variable_test_data): condition = Condition({'type': 'django', 'value': 'today == now|date'}) assert condition.evaluate() is True + def test_lazy_date_with_maths(pub, variable_test_data): # form_var_datefield : 2018-07-31 # form_var_datefield2 : 2018-08-31 @@ -1195,6 +1229,7 @@ def test_lazy_date_with_maths(pub, variable_test_data): condition = Condition({'type': 'django', 'value': condition_value.replace('today', 'now')}) assert condition.evaluate() is True + def test_lazy_templates(pub, variable_test_data): context = pub.substitutions.get_context_variables(mode='lazy') tmpl = Template('{{form_var_foo_foo}}') @@ -1215,6 +1250,7 @@ def test_lazy_templates(pub, variable_test_data): tmpl = Template('{% if form_user_email == "bar@localhost" %}HELLO{% endif %}') assert tmpl.render(context) == 'HELLO' + def test_lazy_ezt_templates(pub, variable_test_data): context = pub.substitutions.get_context_variables(mode='lazy') tmpl = Template('[form_var_foo_foo]') @@ -1223,6 +1259,7 @@ def test_lazy_ezt_templates(pub, variable_test_data): tmpl = Template('[is form_var_foo_foo "bar"]HELLO[else]BYE[end]') assert tmpl.render(context) == 'HELLO' + def test_lazy_formdata_fields(pub): formdef = FormDef() formdef.name = 'foobar' @@ -1250,6 +1287,7 @@ def test_lazy_formdata_fields(pub): tmpl = Template('{% if form_var_item != "Foo" %}HELLO{% endif %}') assert tmpl.render(context) == '' + def test_date_conditions_python(pub, variable_test_data): for pycondition in ( 'utils.age_in_days(form_var_datefield, form_var_datefield2) == 31', @@ -1278,6 +1316,7 @@ def test_date_conditions_python(pub, variable_test_data): condition = Condition({'type': 'python', 'value': pycondition}) assert condition.evaluate() is True + def test_date_conditions_django(pub, variable_test_data): for condition_value in ( # hope date is > 2018 # age_in_days @@ -1391,6 +1430,7 @@ def test_date_conditions_django(pub, variable_test_data): condition = Condition({'type': 'django', 'value': condition_value}) assert condition.evaluate() is True + def test_form_digest_date(pub): formdef = FormDef() formdef.name = 'foobar' @@ -1440,6 +1480,7 @@ def test_form_digest_date(pub): formdata.store() assert formdef.data_class().get(formdata.id).digest == 'plop plop' + def test_lazy_formdata_decimal_filter(pub): formdef = FormDef() formdef.name = 'foobar' @@ -1468,6 +1509,7 @@ def test_lazy_formdata_decimal_filter(pub): tmpl = Template('{{ 4.12|decimal:form_var_arg }}') assert tmpl.render(context) == '4.120' + def test_decimal_conditions_django(pub, variable_test_data): for condition_value in ( 'form_var_foo_foo|decimal == 0', @@ -1486,6 +1528,7 @@ def test_decimal_conditions_django(pub, variable_test_data): condition = Condition({'type': 'django', 'value': condition_value}) assert condition.evaluate() is True + def test_lazy_formdata_mathematics_filters(pub): formdef = FormDef() formdef.name = 'foobar' @@ -1517,6 +1560,7 @@ def test_lazy_formdata_mathematics_filters(pub): tmpl = Template('{{ form_var_term1|divide:form_var_term2 }}') assert tmpl.render(context) == '0.75' + def test_mathematic_conditions_django(pub, variable_test_data): for true_condition_value in ( # reminder @@ -1559,6 +1603,7 @@ def test_mathematic_conditions_django(pub, variable_test_data): condition = Condition({'type': 'django', 'value': false_condition_value}) assert condition.evaluate() is False + def test_lazy_formdata_ceil_filter(pub): formdef = FormDef() formdef.name = 'foobar' @@ -1580,6 +1625,7 @@ def test_lazy_formdata_ceil_filter(pub): tmpl = Template('{{ form_var_value|abs }}') assert tmpl.render(context) == '3.14' + def test_rounding_and_abs_conditions_django(pub, variable_test_data): for true_condition_value in ( # reminder @@ -1611,6 +1657,7 @@ def test_rounding_and_abs_conditions_django(pub, variable_test_data): condition = Condition({'type': 'django', 'value': false_condition_value}) assert condition.evaluate() is False + def test_formdata_user_field(pub, variable_test_data): local_user = variable_test_data._formdata.user @@ -1636,6 +1683,7 @@ def test_formdata_user_field(pub, variable_test_data): condition = Condition({'type': 'django', 'value': 'form_user_var_test'}) assert condition.evaluate() is False + def test_string_filters(pub, variable_test_data): tmpl = Template('{% with form_var_foo_foo|split:"a" as x %}{{x.0}}{% endwith %}', raises=True) for mode in (None, 'lazy'): @@ -1652,6 +1700,7 @@ def test_string_filters(pub, variable_test_data): context = pub.substitutions.get_context_variables(mode=mode) assert tmpl.render(context) == '' + def test_user_label(pub): from wcs.admin.settings import UserFieldsFormDef user_formdef = UserFieldsFormDef(pub) diff --git a/tests/test_formdef.py b/tests/test_formdef.py index 29ba01cc1..8a310357d 100644 --- a/tests/test_formdef.py +++ b/tests/test_formdef.py @@ -23,10 +23,12 @@ from wcs.fields import StringField, FileField, DateField, ItemField, PageField from utilities import create_temporary_pub, clean_temporary_pub + def pytest_generate_tests(metafunc): if 'pub' in metafunc.fixturenames: metafunc.parametrize('pub', ['pickle', 'sql'], indirect=True) + @pytest.fixture def pub(request): pub = create_temporary_pub(sql_mode=(request.param == 'sql')) @@ -37,6 +39,7 @@ def pub(request): return pub + def teardown_module(module): clean_temporary_pub() @@ -92,6 +95,7 @@ def test_is_disabled_expiration_datetime(pub): datetime.datetime.now() + datetime.timedelta(hours=1)).timetuple()[:5] assert not formdef.is_disabled() + def test_title_change(pub): formdef = FormDef() formdef.name = 'foo' @@ -113,6 +117,7 @@ def test_title_change(pub): assert FormDef.get(formdef.id).name == 'baz' assert FormDef.get(formdef.id).internal_identifier == 'bar' # didn't change + def test_substitution_variables(pub): formdef = FormDef() formdef.name = 'foo' @@ -138,6 +143,7 @@ def test_substitution_variables(pub): assert formdef.get_substitution_variables()['form_option_bar'] == 'Bar' assert formdef.get_substitution_variables()['form_option_bar_raw'] == 'bar' + def test_urls(pub): FormDef.wipe() formdef = FormDef() @@ -150,6 +156,7 @@ def test_urls(pub): assert formdef.get_url() == 'https://example.net/foo/' assert formdef.get_url(backoffice=True) == 'https://example.net/backoffice/management/foo/' + def test_schema_with_date_variable(pub): FormDef.wipe() formdef = FormDef() @@ -166,6 +173,7 @@ def test_schema_with_date_variable(pub): formdef.workflow_options = {'foo': datetime.datetime(2016, 4, 2).timetuple()} assert json.loads(formdef.export_to_json())['options']['foo'].startswith('2016-04-02') + def test_substitution_variables_object(pub): formdef = FormDef() formdef.name = 'foo' @@ -188,6 +196,7 @@ def test_substitution_variables_object(pub): assert substs.formdef is formdef + def test_file_field_migration(pub): pub.cfg['filetypes'] = {1: {'mimetypes': [ @@ -217,6 +226,7 @@ def test_file_field_migration(pub): assert formdef.fields[1].document_type['label'] == 'Image files' assert formdef.fields[0].document_type['label'] == 'Image files, Documents' + def test_internal_identifier_migration(pub): FormDef.wipe() formdef = FormDef() @@ -233,6 +243,7 @@ def test_internal_identifier_migration(pub): formdef = FormDef.get(formdef.id) assert formdef.internal_identifier == 'foo' + def test_page_field_migration(pub): FormDef.wipe() formdef = FormDef() @@ -392,6 +403,7 @@ def test_unused_file_removal_job(pub): # 1 attachment assert len(glob.glob(os.path.join(pub.app_dir, 'unused-files/attachments/*/*'))) == 1 + def test_get_formdefs_of_all_kinds(pub): from wcs.admin.settings import UserFieldsFormDef from wcs.carddef import CardDef @@ -442,6 +454,7 @@ def test_get_formdefs_of_all_kinds(pub): ('carddef', CardDef), ] + def test_pickle_2to3_conversion(pub): if six.PY2: pytest.skip('only relevant for Python 3') diff --git a/tests/test_formdef_import.py b/tests/test_formdef_import.py index 32ef9c474..39dd6120c 100644 --- a/tests/test_formdef_import.py +++ b/tests/test_formdef_import.py @@ -27,35 +27,41 @@ def setup_module(module): def teardown_module(module): shutil.rmtree(pub.APP_DIR) + def export_to_indented_xml(formdef, include_id=False): formdef_xml = ET.fromstring(ET.tostring(formdef.export_to_xml(include_id=include_id))) indent(formdef_xml) return formdef_xml + def assert_compare_formdef(formdef1, formdef2, include_id=False): assert ET.tostring(export_to_indented_xml(formdef1, include_id=include_id)) == \ ET.tostring(export_to_indented_xml(formdef2, include_id=include_id)) assert formdef1.export_to_json(include_id=include_id, indent=2) == \ formdef2.export_to_json(include_id=include_id, indent=2) + def assert_xml_import_export_works(formdef, include_id=False): formdef_xml = formdef.export_to_xml(include_id=include_id) formdef2 = FormDef.import_from_xml_tree(formdef_xml, include_id=include_id) assert_compare_formdef(formdef, formdef2, include_id=include_id) return formdef2 + def assert_json_import_export_works(formdef, include_id=False): formdef2 = FormDef.import_from_json( StringIO(formdef.export_to_json(include_id=include_id)), include_id=include_id) assert_compare_formdef(formdef, formdef2, include_id=include_id) return formdef2 + def test_empty(): formdef = FormDef() formdef.name = 'empty' assert_xml_import_export_works(formdef) assert_json_import_export_works(formdef) + def test_text_attributes(): formdef = FormDef() formdef.name = 'Foo' @@ -65,6 +71,7 @@ def test_text_attributes(): f2 = assert_json_import_export_works(formdef) assert f2.url_name == formdef.url_name + def test_empty_description_tag(): formdef = FormDef() formdef.name = 'empty' @@ -75,6 +82,7 @@ def test_empty_description_tag(): formdef2 = FormDef.import_from_xml_tree(ET.fromstring(export)) assert not formdef2.description + def test_boolean_attributes(): formdef = FormDef() formdef.name = 'Foo' @@ -88,6 +96,7 @@ def test_boolean_attributes(): assert f2.enable_tracking_codes == formdef.enable_tracking_codes assert f2.confirmation == formdef.confirmation + def test_a_field(): formdef = FormDef() formdef.name = 'Foo' @@ -99,6 +108,7 @@ def test_a_field(): f2 = assert_json_import_export_works(formdef) assert len(f2.fields) == len(formdef.fields) + def test_more_fields(): formdef = FormDef() formdef.name = 'Blah' @@ -121,6 +131,7 @@ def test_more_fields(): assert f2.fields[3].minimum_date == formdef.fields[3].minimum_date assert f2.fields[4].items == formdef.fields[4].items + def test_item_radio(): formdef = FormDef() formdef.name = 'Blah' @@ -153,6 +164,7 @@ def test_item_radio(): fd2 = FormDef.import_from_xml_tree(formdef_xml, include_id=True) assert fd2.fields[0].display_mode == 'list' + def test_include_id(): formdef = FormDef() formdef.name = 'Blah' @@ -176,6 +188,7 @@ def test_include_id(): assert f2.fields[0].id == formdef.fields[0].id assert f2.fields[4].id == formdef.fields[4].id + def test_modification_time(): formdef = FormDef() formdef.name = 'empty' @@ -184,6 +197,7 @@ def test_modification_time(): f2 = assert_json_import_export_works(formdef) assert tuple(f2.last_modification_time)[:6] == tuple(formdef.last_modification_time)[:6] + def test_workflow_options(): formdef = FormDef() formdef.name = 'workflow options' @@ -194,6 +208,7 @@ def test_workflow_options(): fd2 = assert_json_import_export_works(formdef) assert fd2.workflow_options == formdef.workflow_options + def test_workflow_options_with_no_values(): formdef = FormDef() formdef.name = 'foo' @@ -204,6 +219,7 @@ def test_workflow_options_with_no_values(): fd2 = assert_json_import_export_works(formdef) assert fd2.workflow_options == formdef.workflow_options + def test_workflow_options_with_file(): from quixote.http_request import Upload from wcs.qommon.form import UploadedFile @@ -225,6 +241,7 @@ def test_workflow_options_with_file(): assert formdef.workflow_options['foo'].filename == fd2.workflow_options['foo'].filename assert formdef.workflow_options['foo'].get_content() == fd2.workflow_options['foo'].get_content() + def test_workflow_options_with_date(): formdef = FormDef() formdef.name = 'foo' @@ -267,6 +284,7 @@ def test_workflow_reference(): assert FormDef.import_from_xml_tree(formdef_xml_with_id, include_id=False).workflow_id == '2' assert FormDef.import_from_xml_tree(formdef_xml_with_id, include_id=True).workflow_id is None + def test_category_reference(): Category.wipe() FormDef.wipe() @@ -315,6 +333,7 @@ def test_file_field(): assert_json_import_export_works(formdef, include_id=True) assert_json_import_export_works(formdef) + def test_invalid_field_type(): formdef = FormDef() formdef.name = 'foo' @@ -323,6 +342,7 @@ def test_invalid_field_type(): with pytest.raises(FormdefImportError): FormDef.import_from_xml(BytesIO(export), include_id=True) + def test_unknown_data_source(): formdef = FormDef() formdef.name = 'foo' @@ -338,6 +358,7 @@ def test_unknown_data_source(): with pytest.raises(FormdefImportError): FormDef.import_from_xml(BytesIO(export)) + def test_duplicated_field_ids(): formdef = FormDef() formdef.name = 'foo' @@ -358,6 +379,7 @@ def test_duplicated_field_ids(): assert formdef2.fields[1].id == '2' assert formdef2.fields[2].id == '3' + def test_wrong_max_field_id(): formdef = FormDef() formdef.name = 'foo' @@ -370,6 +392,7 @@ def test_wrong_max_field_id(): formdef2 = FormDef.import_from_xml(BytesIO(export), include_id=True) assert formdef2.max_field_id == 2 + def test_page_condition(): formdef = FormDef() formdef.name = 'foo' @@ -389,6 +412,7 @@ def test_page_condition(): fd2 = FormDef.import_from_xml_tree(formdef_xml, include_id=True) assert fd2.fields[0].condition == formdef.fields[0].condition + def test_page_post_conditions(): formdef = FormDef() formdef.name = 'foo' @@ -420,6 +444,7 @@ def test_page_post_conditions(): assert fd2.fields[0].post_conditions[0]['condition'] == {'type': 'python', 'value': 'blah'} assert fd2.fields[0].post_conditions[0]['error_message'] == '' + def test_workflow_roles(): Role.wipe() role = Role(name='blah') @@ -451,6 +476,7 @@ def test_workflow_roles(): fd2 = FormDef.import_from_xml_tree(xml_export, include_id=True) assert fd2.workflow_roles.get('_receiver') is None + def test_geolocations(): formdef = FormDef() formdef.name = 'foo' @@ -461,6 +487,7 @@ def test_geolocations(): fd3 = assert_json_import_export_works(formdef) assert fd3.geolocations == formdef.geolocations + def test_user_roles(): Role.wipe() @@ -487,6 +514,7 @@ def test_user_roles(): fd2 = FormDef.import_from_xml_tree(formdef_xml_no_id, include_id=False) assert fd2.roles == ['logged-users'] + def test_backoffice_submission_roles(): Role.wipe() @@ -500,6 +528,7 @@ def test_backoffice_submission_roles(): fd2 = assert_xml_import_export_works(formdef, include_id=True) assert fd2.backoffice_submission_roles == formdef.backoffice_submission_roles + def test_required_authentication_contexts(): formdef = FormDef() formdef.name = 'foo' @@ -508,6 +537,7 @@ def test_required_authentication_contexts(): fd2 = assert_xml_import_export_works(formdef, include_id=True) assert fd2.required_authentication_contexts == formdef.required_authentication_contexts + def test_field_condition(): formdef = FormDef() formdef.name = 'Foo' @@ -519,6 +549,7 @@ def test_field_condition(): assert len(f2.fields) == len(formdef.fields) assert f2.fields[0].condition == {'type': 'django', 'value': '1'} + def test_field_validation(): formdef = FormDef() formdef.name = 'Foo' @@ -554,6 +585,7 @@ def test_field_validation(): assert len(f2.fields) == len(formdef.fields) assert f2.fields[0].validation == {'type': 'regex', 'value': '\\d'} + def test_digest_template(): formdef = FormDef() formdef.name = 'Foo' diff --git a/tests/test_hobo.py b/tests/test_hobo.py index 30ad28be0..a1041fdb5 100644 --- a/tests/test_hobo.py +++ b/tests/test_hobo.py @@ -213,6 +213,7 @@ HOBO_JSON = { 'timestamp': '1431420355.31' } + def setup_module(module): global pub, hobo_cmd, alt_tempdir pub = create_temporary_pub() @@ -221,10 +222,12 @@ def setup_module(module): hobo_cmd.all_services = HOBO_JSON alt_tempdir = tempfile.mkdtemp() + def teardown_module(module): clean_temporary_pub() shutil.rmtree(alt_tempdir) + def test_configure_site_options(): service = [x for x in HOBO_JSON.get('services', []) if x.get('service-id') == 'wcs'][0] hobo_cmd.configure_site_options(service, pub) @@ -241,6 +244,7 @@ def test_configure_site_options(): self_domain = urlparse.urlsplit(service.get('base_url')).netloc assert pub.get_site_option(self_domain, 'wscall-secrets') != '0' + def test_update_configuration(): service = [x for x in HOBO_JSON.get('services', []) if x.get('service-id') == 'wcs'][0] hobo_cmd.update_configuration(service, pub) @@ -248,6 +252,7 @@ def test_update_configuration(): assert pub.cfg['emails']['footer'] == 'Hello world.' assert pub.cfg['emails']['from'] == 'noreply@example.net' + def test_update_themes(): pub.cfg['branding'] = {'theme': 'default'} service = [x for x in HOBO_JSON.get('services', []) if x.get('service-id') == 'wcs'][0] @@ -275,6 +280,7 @@ def test_update_themes(): assert os.readlink(os.path.join(pub.app_dir, 'theme')) == \ os.path.join(hobo_cmd.THEMES_DIRECTORY, 'foobar') + def test_update_profile(): profile = HOBO_JSON.get('profile') @@ -338,6 +344,7 @@ def test_update_profile(): else: assert attribute_mapping[attribute_name] == field_id + def test_configure_authentication_methods(http_requests): pub.cfg['idp'] = {} service = [x for x in HOBO_JSON.get('services', []) if x.get('service-id') == 'wcs'][0] @@ -351,6 +358,7 @@ def test_configure_authentication_methods(http_requests): assert pub.cfg['sp']['idp-manage-roles'] assert pub.get_site_option('idp_account_url', 'variables').endswith('/accounts/') + def test_deploy(): cleanup() WcsPublisher.APP_DIR = alt_tempdir @@ -381,6 +389,7 @@ def test_deploy(): pub_cfg = pickle.load(open(os.path.join(alt_tempdir, 'wcs.example.net', 'config.pck'), 'rb')) assert pub_cfg['language'] == {'language': 'fr'} + def test_configure_postgresql(): cleanup() WcsPublisher.APP_DIR = alt_tempdir diff --git a/tests/test_hobo_notify.py b/tests/test_hobo_notify.py index b0d8e9db5..7e2c85794 100644 --- a/tests/test_hobo_notify.py +++ b/tests/test_hobo_notify.py @@ -16,6 +16,7 @@ def pytest_generate_tests(metafunc): if 'pub' in metafunc.fixturenames: metafunc.parametrize('pub', ['pickle', 'sql'], indirect=True) + @pytest.fixture def pub(request): pub = create_temporary_pub(sql_mode=(request.param == 'sql')) @@ -160,6 +161,7 @@ def test_process_notification_role(pub): assert Role.select()[0].emails == ['petite-enfance@example.com'] assert Role.select()[0].emails_to_members is True + def test_process_notification_internal_role(pub): Role.wipe() @@ -186,6 +188,7 @@ def test_process_notification_internal_role(pub): role = Role.select()[0] assert role.is_internal() + def test_process_notification_role_description(pub): User = pub.user_class @@ -437,6 +440,7 @@ PROFILE = { ] } + def test_process_notification_user_provision(pub): User = pub.user_class @@ -619,9 +623,11 @@ def test_process_notification_user_provision(pub): else: # empty value : empty field assert User.select()[0].form_data['_birthdate'] is None + def notify_of_exception(exc_info, context): raise Exception(exc_info) + def test_process_notification_user_with_errors(pub): User = pub.user_class @@ -683,6 +689,7 @@ def test_process_notification_user_with_errors(pub): assert e.value.args[0][0] == KeyError assert e.value.args[0][1].args == ('user without uuid',) + def test_process_notification_role_with_errors(pub): User = pub.user_class User.wipe() diff --git a/tests/test_misc.py b/tests/test_misc.py index 7ae4661e5..aac09e880 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -33,12 +33,15 @@ from django.core.cache import cache from utilities import get_app, create_temporary_pub, clean_temporary_pub + def setup_module(module): cleanup() + def teardown_module(module): clean_temporary_pub() + def test_parse_file_size(): assert FileSizeWidget.parse_file_size('17') == 17 assert FileSizeWidget.parse_file_size('17o') == 17 @@ -52,21 +55,25 @@ def test_parse_file_size(): assert FileSizeWidget.parse_file_size('17 K') == 17*10**3 assert FileSizeWidget.parse_file_size(' 17 K ') == 17*10**3 + def test_parse_invalid_file_size(): for test_value in ('17i', 'hello', '0.4K', '2G'): with pytest.raises(ValueError): FileSizeWidget.parse_file_size(test_value) + def test_humantime(): for x in range(3, 100000, 13): assert humanduration2seconds(seconds2humanduration(x)) == x + def test_parse_mimetypes(): assert FileTypesDirectory.parse_mimetypes('application/pdf') == ['application/pdf'] assert FileTypesDirectory.parse_mimetypes('.pdf') == ['application/pdf'] assert set(FileTypesDirectory.parse_mimetypes('.pdf, .odt')) == set([ 'application/pdf', 'application/vnd.oasis.opendocument.text']) + def test_format_mimetypes(): assert FileTypesDirectory.format_mimetypes(['application/pdf']) == \ 'application/pdf (.pdf)' @@ -83,34 +90,41 @@ def test_format_mimetypes(): 'application/vnd.openxmlformats-officedocument.wordprocessingml.document (.docx)'\ '...' + def test_simplify_unchanged(): assert simplify('test') == 'test' assert simplify('another-test') == 'another-test' assert simplify('another_test', '_') == 'another_test' + def test_simplify_space(): assert simplify('test again') == 'test-again' assert simplify(' test again ') == 'test-again' assert simplify('test again', '_') == 'test_again' assert simplify(' test again ', '_') == 'test_again' + def test_simplify_apostrophes(): assert simplify('test\'again') == 'test-again' assert simplify('test\'\'\'again') == 'test-again' + def test_simplify_accented(): assert simplify(u'cliché') == 'cliche' if six.PY2: assert simplify(u'cliché'.encode('iso-8859-1')) == 'cliche' + def test_simplify_remove(): assert simplify('this is: (a) "test"') == 'this-is-a-test' assert simplify('a test; again?') == 'a-test-again' + def test_simplify_mix(): assert simplify(u' this is: (a) "cliché" ') == 'this-is-a-cliche' assert simplify(u' À "cliché"; again? ') == 'a-cliche-again' + def test_json_str_decoder(): json_str = json.dumps({ 'lst': [{'a': 'b'}, 1, 2], @@ -125,6 +139,7 @@ def test_json_str_decoder(): assert type(json_loads(json_str)['bla']) is str assert json_loads(json_str)['bla'] == force_str(u'éléphant') + def test_format_time(): assert format_time(None, '%(month_name)s') == '?' assert format_time(1500000000, '%(month_name)s') == 'July' @@ -136,6 +151,7 @@ def test_format_time(): assert format_time(time.localtime(1500000000,), '%(month)s') == '7' assert format_time(time.localtime(1500000000,), '%(weekday_name)s') == 'Friday' + def test_parse_isotime(): assert 1420107019 == parse_isotime('2015-01-01T10:10:19Z') assert 1420107019 == parse_isotime('2015-01-01T10:10:19+00:00Z') @@ -144,6 +160,7 @@ def test_parse_isotime(): with pytest.raises(ValueError): parse_isotime('2015-01-0110:10:19Z') + def test_script_substitution_variable(): pub = create_temporary_pub() pub.substitutions.feed(pub) @@ -173,11 +190,13 @@ def test_script_substitution_variable(): fd.close() assert variables['script'].hello_world() == 'http://example.net' + def test_default_charset(): pub = create_temporary_pub() resp = get_app(pub).get('/') assert 'utf-8' in resp.headers['Content-Type'] + def test_age_in_years(): create_temporary_pub() assert evalutils.age_in_years('2000-01-01', '2016-05-26') == 16 @@ -220,6 +239,7 @@ def test_age_in_seconds(): assert evalutils.age_in_seconds(time.struct_time((2000, 1, 1, 0, 0, 0, 0, 0, 0)), '2000-01-01 01:00') == 3600 + def test_date_format(): pub = create_temporary_pub() pub.cfg['language'] = {} @@ -242,6 +262,7 @@ def test_date_format(): finally: os.environ = orig_environ + def test_get_as_datetime(): pub = create_temporary_pub() datetime_value = datetime.datetime(2017, 4, 25, 12, 0) @@ -251,6 +272,7 @@ def test_get_as_datetime(): assert get_as_datetime('2017-04-25T12:00:00') == datetime_value assert get_as_datetime('25/04/2017 12:00') == datetime_value + def test_pagination(): pub = create_temporary_pub() req = HTTPRequest(None, {'SERVER_NAME': 'example.net', 'SCRIPT_NAME': ''}) @@ -278,6 +300,7 @@ def test_pagination(): assert get_texts(pagination_links(100, 20, 500)) == [ '1', '…', '3', '4', '5', '6', '7', '8', '9', '…', '25', '(101-120/500)', 'Per page: ', '10', '20', '50', '100'] + def test_email_signature_plain(emails): pub = create_temporary_pub() pub.cfg['emails'] = {'footer': 'Footer\nText'} @@ -286,6 +309,7 @@ def test_email_signature_plain(emails): assert not emails.emails['test']['msg'].is_multipart() assert b'Footer\nText' in emails.emails['test']['msg'].get_payload(decode=True) + def test_email_from(emails): pub = create_temporary_pub() send_email('test', mail_body='Hello', email_rcpt='test@localhost', want_html=False) @@ -306,6 +330,7 @@ def test_email_from(emails): assert emails.emails['test']['from'] == 'foo@localhost' assert emails.emails['test']['msg']['From'] in ('=?utf-8?q?HELLO?= ', 'HELLO ') + @pytest.mark.skipif('docutils is None') def test_email_signature_rst(emails): pub = create_temporary_pub() @@ -319,6 +344,7 @@ def test_email_signature_rst(emails): assert b'Footer\nText' in emails.emails['test']['msg'].get_payload()[0].get_payload(decode=True) assert b'>Footer<' in emails.emails['test']['msg'].get_payload()[1].get_payload(decode=True) + @pytest.mark.skipif('docutils is None') def test_email_signature_rst_pipes(emails): pub = create_temporary_pub() @@ -332,6 +358,7 @@ def test_email_signature_rst_pipes(emails): assert b'Footer\nText' in emails.emails['test']['msg'].get_payload()[0].get_payload(decode=True) assert b'>Footer<' in emails.emails['test']['msg'].get_payload()[1].get_payload(decode=True) + def test_email_plain_with_attachments(emails): pub = create_temporary_pub() @@ -392,6 +419,7 @@ def test_email_plain_with_attachments(emails): assert emails.count() == 5 + @pytest.mark.skipif('docutils is None') def test_email_plain_and_html_with_attachments(emails): pub = create_temporary_pub() @@ -410,10 +438,12 @@ def test_email_plain_and_html_with_attachments(emails): assert emails.emails['test']['msg'].get_payload()[0].get_payload()[1].get_content_type() == 'text/html' assert emails.emails['test']['msg'].get_payload()[1].get_content_type() == 'image/jpeg' + def test_cache(): cache.set('hello', 'world') assert cache.get('hello') == 'world' + def test_normalize_geolocation(): assert normalize_geolocation({'lat': 10.0, 'lon': 0.0}) == {'lat': 10.0, 'lon': 0.0} assert normalize_geolocation({'lat': -10.0, 'lon': 0.0}) == {'lat': -10.0, 'lon': 0.0} @@ -433,6 +463,7 @@ def test_normalize_geolocation(): assert normalize_geolocation({'lat': 0.0, 'lon': 400.0}) == {'lat': 0.0, 'lon': 40.0} assert normalize_geolocation({'lat': 0.0, 'lon': -400.0}) == {'lat': 0.0, 'lon': -40.0} + @pytest.mark.skipif('docutils is None') def test_email_with_enumeration(emails): pub = create_temporary_pub() @@ -460,6 +491,7 @@ M. Francis Kuntz assert b'arabic simple' in html assert b'M. Francis Kuntz' in html + @pytest.mark.skipif('docutils is None') def test_email_with_unexpected_transition(emails): pub = create_temporary_pub() @@ -486,6 +518,7 @@ bye, assert text.count(b'\n ?????????\n') == 1 assert html.count(b'
?????????
') == 1 + def test_dict_from_prefix(): d = evalutils.dict_from_prefix('var1', {}) assert d == {} @@ -499,6 +532,7 @@ def test_dict_from_prefix(): d = evalutils.dict_from_prefix('v', {'k1':'v1', 'k2':'v2'}) assert d == {} + def test_objects_repr(): workflow = Workflow(name='wf') st1 = workflow.add_status('Status1', 'st1') diff --git a/tests/test_prefill.py b/tests/test_prefill.py index 793a471db..6a448779e 100644 --- a/tests/test_prefill.py +++ b/tests/test_prefill.py @@ -10,6 +10,7 @@ from wcs import fields from utilities import create_temporary_pub + def setup_module(module): cleanup() @@ -20,6 +21,7 @@ def setup_module(module): req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'}) pub._set_request(req) + @pytest.fixture def user(request): pub.user_class.wipe() @@ -29,6 +31,7 @@ def user(request): get_request()._user = user return user + def teardown_module(module): shutil.rmtree(pub.APP_DIR) @@ -38,11 +41,13 @@ def test_prefill_string(): field.prefill = {'type': 'string', 'value': 'test'} assert field.get_prefill_value() == ('test', False) + def test_prefill_user(user): field = fields.Field() field.prefill = {'type': 'user', 'value': 'email'} assert field.get_prefill_value(user=get_request().user) == ('test@example.net', False) + def test_prefill_user_attribute(user): from wcs.admin.settings import UserFieldsFormDef formdef = UserFieldsFormDef(pub) @@ -57,6 +62,7 @@ def test_prefill_user_attribute(user): user.store() assert field.get_prefill_value(user=get_request().user) == ('Plop', False) + def test_prefill_verified_user_attribute(user): from wcs.admin.settings import UserFieldsFormDef formdef = UserFieldsFormDef(pub) @@ -72,22 +78,26 @@ def test_prefill_verified_user_attribute(user): user.store() assert field.get_prefill_value(user=get_request().user) == ('Plop', True) + def test_prefill_formula(): field = fields.Field() field.prefill = {'type': 'formula', 'value': 'str(2+5)'} assert field.get_prefill_value() == ('7', False) + def test_prefill_formula_with_error(): field = fields.Field() field.prefill = {'type': 'formula', 'value': 'foobar'} assert field.get_prefill_value() == (None, False) + def test_prefill_formula_substitution_variable(): pub.substitutions.get_context_variables = lambda: {'test': 'value'} field = fields.Field() field.prefill = {'type': 'formula', 'value': 'test'} assert field.get_prefill_value() == ('value', False) + def test_prefill_formula_date_value(): pub.substitutions.get_context_variables = lambda: {} field = fields.Field() @@ -98,6 +108,7 @@ def test_prefill_formula_date_value(): field.prefill = {'type': 'formula', 'value': 'utils.add_days("2016-01-01", 10)'} assert field.get_prefill_value() == ('2016-01-11', False) + def test_prefill_formula_today_value(): pub.substitutions.get_context_variables = pub.get_substitution_variables field = fields.Field() @@ -105,6 +116,7 @@ def test_prefill_formula_today_value(): today = datetime.date.today().strftime('%Y-%m-%d') assert field.get_prefill_value() == (today, False) + def test_prefill_formula_time_value(): pub.substitutions.get_context_variables = lambda: {} field = fields.Field() diff --git a/tests/test_publisher.py b/tests/test_publisher.py index 1e9796f9e..553ce3c81 100644 --- a/tests/test_publisher.py +++ b/tests/test_publisher.py @@ -24,6 +24,7 @@ from wcs.qommon.cron import CronJob from utilities import create_temporary_pub + def setup_module(module): cleanup() global pub @@ -31,15 +32,18 @@ def setup_module(module): pub.cfg['language'] = {'language': 'en'} pub.write_cfg() + def teardown_module(module): shutil.rmtree(pub.APP_DIR) + def get_request(): return HTTPRequest(None, { 'SERVER_NAME': 'www.example.net', 'SCRIPT_NAME': '', }) + def test_plaintext_error(): req = get_request() pub._set_request(req) @@ -62,6 +66,7 @@ def test_plaintext_error(): assert re.findall('File.*?line.*?in test_plaintext_error', s) assert not re.findall('^>.*\d+.*s = pub._generate_plaintext_error', s, re.MULTILINE) + def test_finish_failed_request(): pub.USE_LONG_TRACES = False try: @@ -100,6 +105,7 @@ def test_finish_failed_request(): assert 'Traceback (most recent call last)' in str(body) assert '
' in str(body) + def test_finish_interrupted_request(): req = HTTPRequest(StringIO(''), { 'SERVER_NAME': 'example.net', @@ -132,6 +138,7 @@ def test_finish_interrupted_request(): }) response = pub.process_request(req) + def test_get_tenants(): pub = create_temporary_pub() open(os.path.join(pub.APP_DIR, 'xxx'), 'w').close() @@ -141,15 +148,18 @@ def test_get_tenants(): assert not 'xxx' in tenants assert not 'plop.invalid' in tenants + def test_register_cronjobs(): assert not pub.cronjobs pub.register_cronjobs() assert 'apply_global_action_timeouts' in [x.function.__name__ for x in pub.cronjobs] assert 'clean_sessions' in [x.function.__name__ for x in pub.cronjobs] + def test_get_default_position(): assert pub.get_default_position() == '50.84;4.36' + def test_import_config_zip(): pub = create_temporary_pub() pub.cfg['sp'] = {'what': 'ever'} @@ -183,6 +193,7 @@ def test_import_config_zip(): assert not isinstance(pub.cfg['language'], unicode) assert not isinstance(pub.cfg['whatever2'][-1]['c'], unicode) + def test_cron_command(): pub = create_temporary_pub() with mock.patch('wcs.qommon.management.commands.cron.cron_worker') as cron_worker: diff --git a/tests/test_register.py b/tests/test_register.py index be153baad..416d123bc 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -12,10 +12,12 @@ from wcs.qommon.ident.password_accounts import PasswordAccount from utilities import get_app, create_temporary_pub, clean_temporary_pub + def pytest_generate_tests(metafunc): if 'pub' in metafunc.fixturenames: metafunc.parametrize('pub', ['pickle', 'sql'], indirect=True) + @pytest.fixture def pub(request): pub = create_temporary_pub(sql_mode=(request.param == 'sql')) @@ -29,9 +31,11 @@ def pub(request): return pub + def teardown_module(module): clean_temporary_pub() + def test_no_user_registration(pub): # makes sure the page is not published unless configured app = get_app(pub) @@ -42,11 +46,13 @@ def test_no_user_registration(pub): pub.cfg['identities'] = {'creation': 'self'} pub.write_cfg() + def test_link_on_login_page(pub): app = get_app(pub) page = app.get('/login/') assert '/register/' in page.text + def test_no_password(pub): app = get_app(pub) page = app.get('/register/') @@ -54,6 +60,7 @@ def test_no_password(pub): assert 'username' in register_form.fields assert 'password' not in register_form.fields + def test_user_registration_mismatch(pub): pub.cfg['passwords'] = {'generate': False} pub.write_cfg() @@ -66,6 +73,7 @@ def test_user_registration_mismatch(pub): resp = register_form.submit() assert 'Passwords do not match' in resp.text + def do_user_registration(pub, username='foo', password='bar'): initial_user_count = pub.user_class.count() initial_account_count = PasswordAccount.count() @@ -89,6 +97,7 @@ def do_user_registration(pub, username='foo', password='bar'): user2 = PasswordAccount.get_with_credentials(username, password) assert user.id == user2.id + def test_user_registration(pub): pub.user_class.wipe() PasswordAccount.wipe() @@ -99,6 +108,7 @@ def test_user_registration(pub): account = PasswordAccount.get('foo') assert account.password == 'bar' # check it's in clear text + def test_user_password_hashing(pub): pub.user_class.wipe() PasswordAccount.wipe() @@ -109,6 +119,7 @@ def test_user_password_hashing(pub): account = PasswordAccount.get('foo') assert account.password == hashlib.sha256(b'bar').hexdigest() + def test_user_password_accents(pub): pub.user_class.wipe() PasswordAccount.wipe() @@ -120,6 +131,7 @@ def test_user_password_accents(pub): account = PasswordAccount.get('foo') assert account.password == password + def test_admin_notification(pub, emails): pub.cfg['identities'] = {'creation': 'self', 'notify-on-register': True} pub.write_cfg() @@ -137,6 +149,7 @@ def test_admin_notification(pub, emails): assert emails.get('New Registration') assert emails.get('New Registration').get('email_rcpt') == ['admin@localhost'] + def test_user_notification(pub, emails): pub.cfg['identities'] = {'creation': 'self', 'notify-on-register': False, 'email-as-username': True} @@ -158,6 +171,7 @@ def test_user_notification(pub, emails): assert emails.get('Welcome to example.net').get('to') == 'foo@localhost' assert account.password in emails.get('Welcome to example.net').get('payload') + def test_user_login(pub): pub.cfg['identities'] = {'creation': 'self', 'notify-on-register': False} pub.user_class.wipe() @@ -182,6 +196,7 @@ def test_user_login(pub): resp = resp.forms[0].submit() assert resp.location == 'http://example.net/' + def test_forgotten(pub, emails): pub.cfg['identities'] = {'creation': 'self', 'notify-on-register': False} pub.user_class.wipe() diff --git a/tests/test_role.py b/tests/test_role.py index 5a2588e1c..f3ef5aabe 100644 --- a/tests/test_role.py +++ b/tests/test_role.py @@ -7,20 +7,24 @@ from utilities import create_temporary_pub, clean_temporary_pub from wcs.qommon.storage import StorableObject from wcs.roles import Role, get_user_roles + def setup_module(module): pub = create_temporary_pub() pub.cfg['language'] = {'language': 'en'} pub.cfg['misc'] = {'charset': 'utf-8'} + def teardown_module(module): clean_temporary_pub() + def test_slug(): Role.wipe() role = Role(name='Hello world') role.store() assert role.slug == 'hello-world' + def test_duplicated_name(): Role.wipe() role = Role(name='Hello world') @@ -30,6 +34,7 @@ def test_duplicated_name(): role.store() assert role.slug == 'hello-world-1' + def test_migrate(): Role.wipe() role = Role(name='Hello world') @@ -40,6 +45,7 @@ def test_migrate(): assert pickle.load(open(role.get_object_filename(), 'rb')).slug is None assert Role.get(role.id).slug == 'hello-world' + def test_get_user_roles(): Role.wipe() Role(name='f1').store() diff --git a/tests/test_rootdirectory.py b/tests/test_rootdirectory.py index 2b7e1af45..6829ce166 100644 --- a/tests/test_rootdirectory.py +++ b/tests/test_rootdirectory.py @@ -77,6 +77,7 @@ def test_public_site_anonymous_access(): assert 'href="category1/test-formdef-1/"' in output assert 'href="category1/test-formdef-2/"' in output + def test_private_site_anonymous_access(): FormDef.wipe() create_formdef() @@ -86,6 +87,7 @@ def test_private_site_anonymous_access(): with pytest.raises(wcs.forms.root.errors.AccessUnauthorizedError): output = indexhtml() + def test_semi_private_site_anonymous_access(): FormDef.wipe() create_formdef() @@ -95,6 +97,7 @@ def test_semi_private_site_anonymous_access(): assert 'href="category1/test-formdef-1/"' not in output assert 'href="category1/test-formdef-2/"' in output + def test_private_site_authorized_access(): FormDef.wipe() create_formdef() @@ -105,6 +108,7 @@ def test_private_site_authorized_access(): assert 'href="category1/test-formdef-1/"' in output assert 'href="category1/test-formdef-2/"' in output + def test_private_site_unauthorized_access(): FormDef.wipe() create_formdef() @@ -114,6 +118,7 @@ def test_private_site_unauthorized_access(): with pytest.raises(wcs.forms.root.errors.AccessUnauthorizedError): output = indexhtml(user2) + def test_private_site_semi_authorized_access(): FormDef.wipe() create_formdef() @@ -125,6 +130,7 @@ def test_private_site_semi_authorized_access(): assert 'href="category1/test-formdef-1/"' in output assert 'href="category1/test-formdef-2/"' not in output + def test_advertized_site_anonymous_access(): FormDef.wipe() create_formdef() @@ -137,6 +143,7 @@ def test_advertized_site_anonymous_access(): assert 'href="category1/test-formdef-2/"' not in output assert 'authentication required' in output # locales ? + def test_advertized_site_user_access(): FormDef.wipe() create_formdef() @@ -149,6 +156,7 @@ def test_advertized_site_user_access(): assert 'href="category1/test-formdef-2/"' not in output assert 'authentication required' in output # locales ? + def test_static_directories(): assert get_app(pub).get('/static/css/wcs.css') assert get_app(pub).get('/static/images/feed-icon-10x10.png') @@ -160,6 +168,7 @@ def test_static_directories(): assert 'Directory listing denied' in get_app(pub).get('/static/css/').text assert get_app(pub).get('/static/xxx', status=404) + def test_jquery_debug_mode(): FormDef.wipe() create_formdef() diff --git a/tests/test_saml2utils.py b/tests/test_saml2utils.py index bf28e254c..1beb3ab31 100644 --- a/tests/test_saml2utils.py +++ b/tests/test_saml2utils.py @@ -9,14 +9,17 @@ from wcs.qommon.saml2utils import Metadata from utilities import create_temporary_pub + def setup_module(module): cleanup() global pub pub = create_temporary_pub() + def teardown_module(module): shutil.rmtree(pub.APP_DIR) + def test_metadata_generation(): pkey, _ = x509utils.generate_rsa_keypair() meta = Metadata(publisher=pub, diff --git a/tests/test_saml_auth.py b/tests/test_saml_auth.py index af200a28a..88aea69c6 100644 --- a/tests/test_saml_auth.py +++ b/tests/test_saml_auth.py @@ -43,6 +43,7 @@ def pytest_generate_tests(metafunc): if 'pub' in metafunc.fixturenames: metafunc.parametrize('pub', ['pickle', 'sql'], indirect=True) + @pytest.fixture def pub(request): pub = create_temporary_pub(sql_mode=(request.param == 'sql')) @@ -99,6 +100,7 @@ def setup_idps(pub, idp_number=1): def teardown_module(module): clean_temporary_pub() + def test_login(pub): req = HTTPRequest(None, { 'SERVER_NAME': 'example.net', @@ -111,6 +113,7 @@ def test_login(pub): assert req.response.headers['location'].startswith('http://sso.example.net/saml2/sso?SAMLRequest') assert 'rsa-sha256' in req.response.headers['location'] + def get_authn_response_msg(pub, ni_format=lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT): idp_metadata_filepath = os.path.join(pub.app_dir, 'idp-http-sso.example.net-saml2-metadata-metadata.xml') @@ -174,6 +177,7 @@ def get_authn_response_msg(pub, ni_format=lasso.SAML2_NAME_IDENTIFIER_FORMAT_PER login.buildAuthnResponseMsg() return login.msgBody + def get_assertion_consumer_request(pub, ni_format=lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT): req = HTTPRequest(None, { 'SERVER_NAME': 'example.net', @@ -187,6 +191,7 @@ def get_assertion_consumer_request(pub, ni_format=lasso.SAML2_NAME_IDENTIFIER_FO req.form['SAMLResponse'] = get_authn_response_msg(pub, ni_format=ni_format) return req + def test_saml_metadata(pub): req = HTTPRequest(None, {'SERVER_NAME': 'example.net', 'SCRIPT_NAME': '', }) pub._set_request(req) @@ -196,6 +201,7 @@ def test_saml_metadata(pub): assert 'http://example.net/backoffice/<' in request.getOriginalXmlnode() + def test_saml_register(pub): get_app(pub).get('/register/', status=404) pub.cfg['saml_identities'] = {'identity-creation': 'self'} @@ -364,6 +380,7 @@ def test_saml_register(pub): resp = get_app(pub).get('/register/') assert resp.location == 'http://sso.example.net/registration?next_url=http%3A%2F%2Fexample.net%2Fregister%2F' + def test_saml_logout(pub): req = get_assertion_consumer_request(pub) saml2 = Saml2Directory() @@ -374,6 +391,7 @@ def test_saml_logout(pub): assert 'rsa-sha256' in req.response.headers['location'] assert req.session.user is None + def test_saml_idp_logout(pub): req = get_assertion_consumer_request(pub) saml2 = Saml2Directory() diff --git a/tests/test_sessions.py b/tests/test_sessions.py index 773ffa1d8..d67afc119 100644 --- a/tests/test_sessions.py +++ b/tests/test_sessions.py @@ -12,6 +12,7 @@ from wcs import fields from utilities import create_temporary_pub, clean_temporary_pub, get_app, login + def setup_module(): clean_temporary_pub() @@ -24,6 +25,7 @@ def pytest_generate_tests(metafunc): if 'pub' in metafunc.fixturenames: metafunc.parametrize('pub', ['pickle', 'sql'], indirect=True) + @pytest.fixture(scope='function') def pub(request): pub = create_temporary_pub(sql_mode=(request.param == 'sql')) @@ -35,6 +37,7 @@ def pub(request): pub.write_cfg() return pub + @pytest.fixture def http_request(pub): req = HTTPRequest(None, {}) @@ -73,6 +76,7 @@ session_max_age: 1 time.sleep(0.6) assert 'Logout' not in app.get('/') + def test_session_expire(pub, user, app): login(app, username='foo', password='foo') assert 'Logout' in app.get('/') @@ -84,6 +88,7 @@ def test_session_expire(pub, user, app): session.store() assert 'Logout' not in app.get('/') + def test_sessions_visiting_objects(pub, http_request): # check it starts with nothing assert len(pub.get_visited_objects()) == 0 @@ -149,6 +154,7 @@ def test_sessions_visiting_objects(pub, http_request): assert set([x[0] for x in pub.get_object_visitors('formdata-foobar-2')]) == set(['FOO', 'BAR']) assert set([x[0] for x in pub.get_object_visitors('formdata-foobar-1')]) == set([]) + def test_session_do_not_reuse_id(pub, user, app): pub.session_manager.session_class.wipe() login(app, username='foo', password='foo') @@ -162,6 +168,7 @@ def test_session_do_not_reuse_id(pub, user, app): assert resp.status_int == 302 assert pub.session_manager.session_class.count() == 2 + def test_session_substitution_variables(pub, user, app): pub.session_manager.session_class.wipe() resp = app.get('/') @@ -182,6 +189,7 @@ def test_session_substitution_variables(pub, user, app): resp = app.get('/foobar/') assert 'Hello %s' % session_id in resp.text + def test_session_substitution_variables_1st_page_condition(pub, user, app): pub.session_manager.session_class.wipe() resp = app.get('/') diff --git a/tests/test_sql.py b/tests/test_sql.py index f4ae89bba..28bd6abac 100644 --- a/tests/test_sql.py +++ b/tests/test_sql.py @@ -35,6 +35,7 @@ try: except ImportError: pass + def setup_module(module): global pub, formdef @@ -55,9 +56,11 @@ def setup_module(module): ] formdef.store() + def teardown_module(module): clean_temporary_pub() + @postgresql def test_sql_table_name_invalid_chars(): test_formdef = FormDef() @@ -68,15 +71,18 @@ def test_sql_table_name_invalid_chars(): data_class = test_formdef.data_class(mode='sql') assert data_class.count() == 0 + @postgresql def test_sql_data_class(): data_class = formdef.data_class(mode='sql') + @postgresql def test_sql_count(): data_class = formdef.data_class(mode='sql') assert data_class.count() == 0 + @postgresql def test_sql_store(): data_class = formdef.data_class(mode='sql') @@ -86,6 +92,7 @@ def test_sql_store(): formdata.store() assert formdata.id + @postgresql def test_sql_get(): data_class = formdef.data_class(mode='sql') @@ -99,6 +106,7 @@ def test_sql_get(): assert formdata.user_id == '5' assert formdata.status == 'wf-0' + @postgresql def test_sql_store_channel(): data_class = formdef.data_class(mode='sql') @@ -114,17 +122,20 @@ def test_sql_store_channel(): formdata.store() assert data_class.get(formdata.id).submission_channel is None + @postgresql def test_sql_get_missing(): data_class = formdef.data_class(mode='sql') with pytest.raises(KeyError): data_class.get(123456) + @postgresql def test_sql_get_missing_ignore_errors(): data_class = formdef.data_class(mode='sql') assert data_class.get(123456, ignore_errors=True) is None + def check_sql_field(no, value): data_class = formdef.data_class(mode='sql') formdata = data_class() @@ -135,40 +146,48 @@ def check_sql_field(no, value): formdata = data_class.get(id) assert formdata.data.get(no) == value + @postgresql def test_sql_field_string(): check_sql_field('0', 'hello world') check_sql_field('0', 'élo world') check_sql_field('0', None) + @postgresql def test_sql_field_email(): check_sql_field('1', 'fred@example.com') + @postgresql def test_sql_field_text(): check_sql_field('2', 'long text') check_sql_field('2', 'long tèxt') + @postgresql def test_sql_field_bool(): check_sql_field('3', False) check_sql_field('3', True) + @postgresql def test_sql_field_item(): check_sql_field('4', 'apricot') + @postgresql def test_sql_field_date(): check_sql_field('5', datetime.date.today().timetuple()) + @postgresql def test_sql_field_items(): check_sql_field('6', ['apricot']) check_sql_field('6', ['apricot', 'pear']) check_sql_field('6', ['pomme', 'poire', 'pêche']) + @postgresql def test_sql_geoloc(): test_formdef = FormDef() @@ -194,6 +213,7 @@ def test_sql_geoloc(): formdata2 = data_class.get(formdata.id) assert formdata2.geolocations == formdata.geolocations + @postgresql def test_sql_multi_geoloc(): test_formdef = FormDef() @@ -222,6 +242,7 @@ def test_sql_multi_geoloc(): formdata2 = data_class.get(formdata.id) assert formdata2.geolocations == {'base': {'lat': 12, 'lon': 21}} + @postgresql def test_sql_change(): data_class = formdef.data_class(mode='sql') @@ -238,6 +259,7 @@ def test_sql_change(): formdata = data_class.get(id) assert formdata.data.get('0') == 'test2' + @postgresql def test_sql_remove(): data_class = formdef.data_class(mode='sql') @@ -253,6 +275,7 @@ def test_sql_remove(): with pytest.raises(KeyError): data_class.get(id) + @postgresql def test_sql_wipe(): data_class = formdef.data_class(mode='sql') @@ -263,6 +286,7 @@ def test_sql_wipe(): data_class.wipe() assert data_class.count() == 0 + @postgresql def test_sql_evolution(): data_class = formdef.data_class(mode='sql') @@ -285,6 +309,7 @@ def test_sql_evolution(): assert len(formdata.evolution) == 2 assert formdata.evolution[-1].comment == 'hello world' + @postgresql def test_sql_evolution_change(): data_class = formdef.data_class(mode='sql') @@ -314,6 +339,7 @@ def test_sql_evolution_change(): assert len(formdata.evolution) == 2 assert formdata.evolution[-1].comment == 'foobar' + @postgresql def test_sql_multiple_evolutions(): data_class = formdef.data_class(mode='sql') @@ -336,6 +362,7 @@ def test_sql_multiple_evolutions(): data_class.load_all_evolutions(values) assert [x._evolution for x in values] + @postgresql def test_sql_get_ids_with_indexed_value(): data_class = formdef.data_class(mode='sql') @@ -358,6 +385,7 @@ def test_sql_get_ids_with_indexed_value(): ids = data_class.get_ids_with_indexed_value('user_id', '2') assert set(ids) == set([id2, id3]) + @postgresql def test_sql_get_ids_from_query(): data_class = formdef.data_class(mode='sql') @@ -418,6 +446,7 @@ def test_sql_get_ids_with_indexed_value_dict(): ids = data_class.get_ids_with_indexed_value('workflow_roles', '2') assert set(ids) == set([id2, id3]) + @postgresql def test_create_user(): sql.SqlUser.wipe() @@ -426,6 +455,7 @@ def test_create_user(): user.name = 'Pierre' user.store() + @postgresql def test_get_user(): sql.SqlUser.wipe() @@ -436,6 +466,7 @@ def test_get_user(): assert sql.SqlUser.get(user.id) is not None + @postgresql def test_get_missing_user(): sql.SqlUser.wipe() @@ -443,12 +474,14 @@ def test_get_missing_user(): with pytest.raises(KeyError): sql.SqlUser.get(12345) + @postgresql def test_get_missing_user_ignore_errors(): sql.SqlUser.wipe() assert sql.SqlUser.get(12345, ignore_errors=True) is None + @postgresql def test_user_formdef(): sql.SqlUser.wipe() @@ -468,6 +501,7 @@ def test_user_formdef(): del pub.cfg['users']['formdef'] pub.write_cfg() + @postgresql def test_get_users_with_role(): sql.SqlUser.wipe() @@ -484,6 +518,7 @@ def test_get_users_with_role(): assert len(sql.SqlUser.get_users_with_role(1)) == 1 + @postgresql def test_get_users_with_name_identifier(): sql.SqlUser.wipe() @@ -501,6 +536,7 @@ def test_get_users_with_name_identifier(): assert len(sql.SqlUser.get_users_with_name_identifier('foo')) == 1 assert sql.SqlUser.get_users_with_name_identifier('foo')[0].name == 'Pierre' + @postgresql def test_get_users_fts(): sql.SqlUser.wipe() @@ -518,6 +554,7 @@ def test_get_users_fts(): assert len(sql.SqlUser.get_ids_from_query('pierre')) == 1 assert sql.SqlUser.get(sql.SqlUser.get_ids_from_query('pierre')[0]).id == user_id + @postgresql def test_get_users_formdef_fts(): sql.SqlUser.wipe() @@ -542,6 +579,7 @@ def test_get_users_formdef_fts(): del pub.cfg['users']['formdef'] pub.write_cfg() + @postgresql def test_urlname_change(): global formef @@ -570,6 +608,7 @@ def test_urlname_change(): assert data_class.count() == 1 + @postgresql def test_sql_table_add_and_remove_fields(): test_formdef = FormDef() @@ -623,6 +662,7 @@ def test_sql_table_add_and_remove_fields(): data_class = test_formdef.data_class(mode='sql') data_class.select() + @postgresql def test_sql_table_wipe_and_drop(): test_formdef = FormDef() @@ -648,6 +688,7 @@ def test_sql_table_wipe_and_drop(): conn, cur = sql.get_connection_and_cursor() assert table_exists(cur, test_formdef.table_name) + @postgresql def test_sql_indexes(): test_formdef = FormDef() @@ -667,6 +708,7 @@ def test_sql_indexes(): conn.commit() cur.close() + @postgresql def test_sql_table_select(): test_formdef = FormDef() @@ -695,6 +737,7 @@ def test_sql_table_select(): assert len(data_class.select([st.NotContains('id', [24, 25, 86])])) == 48 assert len(data_class.select([st.NotContains('id', [])])) == 50 + @postgresql def test_sql_table_select_iterator(): test_formdef = FormDef() @@ -723,6 +766,7 @@ def test_sql_table_select_iterator(): assert len(list(data_class.select([st.Less('id', 25), st.GreaterOrEqual('id', 10), lambda x: x.id >= 15], iterator=True))) == 10 + @postgresql def test_sql_table_select_datetime(): test_formdef = FormDef() @@ -772,6 +816,7 @@ def test_select_limit_offset(): assert [x.id for x in data_class.select(func_clause, order_by='id', offset=10, iterator=iterator)] == list(range(11, 51)) assert len([x.id for x in data_class.select(lambda x: x.id > 10, limit=10, iterator=iterator)]) == 10 + @postgresql def test_select_criteria_intersects(): data_class = formdef.data_class(mode='sql') @@ -835,6 +880,7 @@ def test_select_criteria_or_and(): assert [x.id for x in data_class.select([st.And([ st.Less('id', 10), st.Greater('id', 5)])], order_by='id')] == list(range(6, 10)) + @postgresql def test_select_criteria_null(): test_formdef = FormDef() @@ -855,6 +901,7 @@ def test_select_criteria_null(): assert len(data_class.select([st.Null('submission_channel')])) == 33 assert len(data_class.select([st.NotNull('submission_channel')])) == 17 + @postgresql def test_sql_table_select_bool(): test_formdef = FormDef() @@ -901,6 +948,7 @@ def test_sql_criteria_ilike(): assert [x.id for x in data_class.select([st.ILike('f3', 'bar')], order_by='id')] == list(range(21, 51)) assert [x.id for x in data_class.select([st.ILike('f3', 'BAR')], order_by='id')] == list(range(21, 51)) + @postgresql def test_sql_criteria_fts(): test_formdef = FormDef() @@ -959,23 +1007,27 @@ def test_sql_criteria_fts(): assert data_class.count([st.FtsMatch(user.name)]) == 1 assert data_class.count([st.FtsMatch('Frederic')]) == 1 + def table_exists(cur, table_name): cur.execute('''SELECT COUNT(*) FROM information_schema.tables WHERE table_name = %s''', (table_name,)) return bool(cur.fetchone()[0] == 1) + def column_exists_in_table(cur, table_name, column_name): cur.execute('''SELECT COUNT(*) FROM information_schema.columns WHERE table_name = %s AND column_name = %s''', (table_name, column_name)) return bool(cur.fetchone()[0] == 1) + def index_exists(cur, index_name): cur.execute('''SELECT COUNT(*) FROM pg_indexes WHERE schemaname = 'public' AND indexname = %s''', (index_name,)) return bool(cur.fetchone()[0] == 1) + @postgresql def test_sql_level(): conn, cur = sql.get_connection_and_cursor() @@ -996,11 +1048,13 @@ def test_sql_level(): conn.commit() cur.close() + def migration_level(cur): cur.execute('SELECT value FROM wcs_meta WHERE key = %s', ('sql_level',)) row = cur.fetchone() return int(row[0]) + @postgresql def test_migration_1_tracking_code(): conn, cur = sql.get_connection_and_cursor() @@ -1013,6 +1067,7 @@ def test_migration_1_tracking_code(): conn.commit() cur.close() + @postgresql def test_migration_2_formdef_id_in_views(): conn, cur = sql.get_connection_and_cursor() @@ -1056,6 +1111,7 @@ def test_migration_2_formdef_id_in_views(): conn.commit() cur.close() + @postgresql def test_migration_6_actions_roles(): conn, cur = sql.get_connection_and_cursor() @@ -1081,6 +1137,7 @@ def test_migration_6_actions_roles(): conn.commit() cur.close() + @postgresql def test_migration_10_submission_channel(): conn, cur = sql.get_connection_and_cursor() @@ -1106,6 +1163,7 @@ def test_migration_10_submission_channel(): conn.commit() cur.close() + @postgresql def test_migration_12_users_fts(): conn, cur = sql.get_connection_and_cursor() @@ -1140,6 +1198,7 @@ def test_migration_12_users_fts(): conn.commit() cur.close() + @postgresql def test_migration_21_users_ascii_name(): conn, cur = sql.get_connection_and_cursor() @@ -1174,6 +1233,7 @@ def test_migration_21_users_ascii_name(): conn.commit() cur.close() + @postgresql def test_migration_24_evolution_index(): formdef = FormDef() @@ -1200,6 +1260,7 @@ def test_migration_24_evolution_index(): conn.commit() cur.close() + def drop_formdef_tables(): conn, cur = sql.get_connection_and_cursor() cur.execute('''SELECT table_name FROM information_schema.tables''') @@ -1262,6 +1323,7 @@ def test_is_at_endpoint(): cur.execute('''SELECT COUNT(*) FROM wcs_all_forms WHERE is_at_endpoint = true''') assert bool(cur.fetchone()[0] == 2) + @postgresql def test_views_fts(): drop_formdef_tables() @@ -1289,6 +1351,7 @@ def test_views_fts(): cur.execute('''SELECT COUNT(*) FROM wcs_all_forms WHERE fts @@ plainto_tsquery(%s)''', ('bar',)) assert bool(cur.fetchone()[0] == 1) + @postgresql def test_select_any_formdata(): drop_formdef_tables() @@ -1350,6 +1413,7 @@ def test_select_any_formdata(): objects2 = sql.AnyFormData.select(order_by='receipt_time', limit=10, offset=20) assert [(x.formdef_id, x.id) for x in objects2] == [(x.formdef_id, x.id) for x in objects][20:30] + @postgresql def test_load_all_evolutions_on_any_formdata(): drop_formdef_tables() @@ -1425,6 +1489,7 @@ def test_geoloc_in_global_view(): assert int(objects2[0].geolocations['base']['lat']) == formdata.geolocations['base']['lat'] assert int(objects2[0].geolocations['base']['lon']) == formdata.geolocations['base']['lon'] + @postgresql def test_actions_roles(): drop_formdef_tables() @@ -1484,6 +1549,7 @@ def test_actions_roles(): assert total_count == 1 assert formdatas[0].id == formdata_id + @postgresql def test_last_update_time(): drop_formdef_tables() @@ -1533,6 +1599,7 @@ def test_last_update_time(): cur.execute('''SELECT id FROM wcs_all_forms WHERE last_update_time = '2015-01-04 00:00' ''') assert bool(cur.fetchone()[0] == formdata2.id) + @postgresql def test_view_formdef_name(): drop_formdef_tables() @@ -1567,6 +1634,7 @@ def test_view_formdef_name(): cur.execute('''SELECT formdef_id FROM wcs_all_forms WHERE formdef_name = 'test formdef name 2' ''') assert bool(str(cur.fetchone()[0]) == str(formdef2.id)) + @postgresql def test_view_user_name(): drop_formdef_tables() @@ -1599,6 +1667,7 @@ def test_view_user_name(): cur.execute('''SELECT user_name FROM wcs_all_forms WHERE id = %s ''', (formdata2.id,)) assert bool(cur.fetchone()[0] == user.name) + @postgresql def test_select_formdata_after_formdef_removal(): drop_formdef_tables() @@ -1626,6 +1695,7 @@ def test_select_formdata_after_formdef_removal(): objects = sql.AnyFormData.select() assert len(objects) == 1 + @postgresql def test_views_submission_info(): drop_formdef_tables() @@ -1655,6 +1725,7 @@ def test_views_submission_info(): cur.execute('''SELECT COUNT(*) FROM wcs_all_forms WHERE submission_channel = %s''', ('mail',)) assert bool(cur.fetchone()[0] == 1) + @postgresql def test_get_formdef_new_id(): test1_formdef = FormDef() @@ -1672,6 +1743,7 @@ def test_get_formdef_new_id(): assert test1_id != test2_formdef.id assert test1_table_name != test2_formdef.table_name + @postgresql def test_criticality_levels(): drop_formdef_tables() @@ -1726,6 +1798,7 @@ def test_criticality_levels(): assert objects[-1].get_criticality_level_object().name == 'green' assert objects[-2].get_criticality_level_object().name == 'green' + @postgresql def test_view_performances(): pytest.skip('takes too much time') @@ -1867,6 +1940,7 @@ def test_migration_30_anonymize_evo_who(): conn.commit() cur.close() + @postgresql def test_migration_31_user_label(): conn, cur = sql.get_connection_and_cursor() diff --git a/tests/test_storage.py b/tests/test_storage.py index 5b46599d1..4e63f36c8 100644 --- a/tests/test_storage.py +++ b/tests/test_storage.py @@ -14,6 +14,7 @@ import wcs.qommon.storage as st from utilities import create_temporary_pub + def setup_module(module): cleanup() global pub @@ -33,6 +34,7 @@ class Foobar(StorableObject): unique_value = None dict_value = None + def test_store(): test = Foobar() test.value = 'value' @@ -40,14 +42,17 @@ def test_store(): test.store() assert test.id == '1' + def test_get(): test = Foobar.get(1) assert test.value == 'value' + def test_get_on_index(): test = Foobar.get_on_index('unique-value', 'unique_value') assert test.value == 'value' + def test_remove_self(): test = Foobar.get(1) test.remove_self() @@ -56,6 +61,7 @@ def test_remove_self(): test = Foobar.get(1, ignore_errors=True) assert test is None + def test_get_on_index_changes(): test = Foobar() test.value = 'value' @@ -72,6 +78,7 @@ def test_get_on_index_changes(): with pytest.raises(KeyError): test = Foobar.get_on_index('unique-value', 'unique_value') + def test_get_with_indexed_value(): Foobar.wipe() @@ -96,6 +103,7 @@ def test_get_with_indexed_value(): assert 'unique-value2' in [x.unique_value for x in tests] assert 'unique-value3' not in [x.unique_value for x in tests] + def test_get_with_indexed_value_changes(): Foobar.wipe() @@ -127,6 +135,7 @@ def test_get_with_indexed_value_changes(): assert len(tests) == 3 assert 'unique-value3' in [x.unique_value for x in tests] + def test_get_with_indexed_value_dict(): Foobar.wipe() @@ -209,6 +218,7 @@ def test_select(): assert len(Foobar.select([st.Contains('unique_value', [24, 25, 86])])) == 2 assert len(Foobar.select([st.NotContains('unique_value', [24, 25, 86])])) == 48 + def test_select_order_by(): Foobar.wipe() @@ -222,6 +232,7 @@ def test_select_order_by(): assert [int(x.id) for x in Foobar.select(order_by='unique_value')] == list(range(50, 0, -1)) assert [int(x.id) for x in Foobar.select(order_by='-unique_value')] == list(range(1, 51)) + def test_select_datetime(): Foobar.wipe() @@ -304,6 +315,7 @@ def test_select_criteria_or_and(): assert [int(x.id) for x in Foobar.select([st.And([st.Less('value', 10), st.Greater('value', 5)])], order_by='id')] == list(range(6, 10)) + def test_select_criteria_null(): Foobar.wipe() @@ -320,6 +332,7 @@ def test_select_criteria_null(): assert len(Foobar.select([st.Null('value')])) == 33 assert len(Foobar.select([st.NotNull('value')])) == 17 + def test_select_criteria_ilike(): Foobar.wipe() @@ -339,6 +352,7 @@ def test_select_criteria_ilike(): assert [int(x.id) for x in Foobar.select([st.ILike('foo', 'bar')], order_by='id')] == list(range(21, 50)) assert [int(x.id) for x in Foobar.select([st.ILike('foo', 'BAR')], order_by='id')] == list(range(21, 50)) + def test_store_async(): Foobar.wipe() @@ -359,6 +373,7 @@ def test_store_async(): test = Foobar.get(1) assert test.value == 'value' + def test_items(): Foobar.wipe() @@ -368,6 +383,7 @@ def test_items(): assert sorted([(int(x), int(y.id)) for (x, y) in Foobar.items()]) == list(zip(range(1,51), range(1, 51))) + def test_reversed_order(): Foobar.wipe() @@ -378,6 +394,7 @@ def test_reversed_order(): assert len(Foobar.select()) == 50 assert [int(x.id) for x in Foobar.select(order_by='-id', limit=10)] == list(range(50, 40, -1)) + def test_destroy_rebuild_index(): test_get_with_indexed_value() assert os.path.exists(os.path.join(Foobar.get_objects_dir(), '.indexes')) @@ -393,6 +410,7 @@ def test_destroy_rebuild_index(): # the indexes should have been rebuilt automatically assert os.path.exists(os.path.join(Foobar.get_objects_dir(), '.indexes')) + def test_concurrent_hashed_indexes(): Foobar.wipe() @@ -418,6 +436,7 @@ def test_concurrent_hashed_indexes(): index_selection = set([x.id for x in Foobar.get_with_indexed_value('dict_value', i)]) assert manual_selection == index_selection + def test_umask(): test = Foobar() test.value = 'value' diff --git a/tests/test_strftime.py b/tests/test_strftime.py index ef804fc7b..6a4b51091 100644 --- a/tests/test_strftime.py +++ b/tests/test_strftime.py @@ -2,6 +2,7 @@ import datetime from wcs.qommon.misc import strftime + def test(): # Make sure that the day names are in order # from 1/1/1800 until 1/1/2100 @@ -30,6 +31,7 @@ def test(): prevday = day testdate = testdate + one_day + def test_types(): assert strftime('%Y-%m-%d %H:%M:%S', datetime.datetime(2017, 11, 19, 13, 8, 0).timetuple()) == '2017-11-19 13:08:00' assert strftime('%Y-%m-%d %H:%M:%S', datetime.datetime(2017, 11, 19, 13, 8, 0)) == '2017-11-19 13:08:00' diff --git a/tests/test_templates.py b/tests/test_templates.py index d8f9645e6..79ca852ba 100644 --- a/tests/test_templates.py +++ b/tests/test_templates.py @@ -65,6 +65,7 @@ def test_template(): tmpl = Template('[if-any foo][foo][endif]') assert tmpl.render({'foo': 'bar'}) == '[if-any foo][foo][endif]' + def test_now_and_today_variables(): # create a today string, verify it contains the year, at least today = Template('{{d}}').render({'d': datetime.date.today()}) @@ -95,6 +96,7 @@ def test_now_and_today_variables(): context = pub.substitutions.get_context_variables(mode=mode) assert tmpl.render(context) == now + def test_template_templatetag(): # check qommon templatetags are always loaded tmpl = Template('{{ date|parse_datetime|date:"Y" }}') @@ -104,12 +106,14 @@ def test_template_templatetag(): tmpl = Template('{% load i18n %}{% trans "hello" %}') assert tmpl.render() == 'hello' + def test_startswith_templatetag(): tmpl = Template('{% if foo|startswith:"bar" %}hello{% endif %}') assert tmpl.render() == '' assert tmpl.render({'foo': 'bar-baz'}) == 'hello' assert tmpl.render({'foo': 'baz-bar'}) == '' + def test_split_templatetag(): tmpl = Template('{{ foo|split|last }}') assert tmpl.render() == '' @@ -121,6 +125,7 @@ def test_split_templatetag(): assert tmpl.render({'foo': 'bar-baz'}) == 'baz' assert tmpl.render({'foo': 'baz-bar'}) == 'bar' + def test_strip_templatetag(): tmpl = Template('{{ foo|strip }}') assert tmpl.render() == '' @@ -132,6 +137,7 @@ def test_strip_templatetag(): assert tmpl.render({'foo': 'XXfoo barXYX'}) == 'foo bar' assert tmpl.render({'foo': ' foo barXX'}) == ' foo bar' + def test_template_encoding(): # django tmpl = Template('{{ foo }} à vélo') @@ -286,6 +292,7 @@ def test_datetime_templatetags(): assert tmpl.render({'plop': {'foo': 'bar'}}) == '' assert tmpl.render() == '' + def test_date_maths(): tmpl = Template('{{ plop|add_days:4 }}') assert tmpl.render({'plop': '2017-12-21'}) == '2017-12-25' @@ -307,10 +314,12 @@ def test_date_maths(): assert tmpl.render({'plop': '2017-12-21'}) == '2017-12-21 12:30' assert tmpl.render({'plop': '2017-12-21 18:00'}) == '2017-12-22 06:30' + def test_variable_unicode_error_handling(): tmpl = Template('{{ form_var_éléphant }}') assert tmpl.render() == '' + def test_decimal_templatetag(): tmpl = Template('{{ plop|decimal }}') assert tmpl.render({'plop': 'toto'}) == '0' @@ -351,6 +360,7 @@ def test_decimal_templatetag(): tmpl = Template('{% if 3|decimal|decimal == 3 %}hello{% endif %}') assert tmpl.render() == 'hello' + def test_mathematics_templatetag(): tmpl = Template('{{ term1|add:term2 }}') @@ -415,6 +425,7 @@ def test_mathematics_templatetag(): tmpl = Template('{{ term1|divide:term2|decimal:2 }}') assert tmpl.render({'term1': 2, 'term2': 3}) == '0.67' + def test_rounding_templatetag(): # ceil tmpl = Template('{{ value|ceil }}') @@ -448,6 +459,7 @@ def test_rounding_templatetag(): assert tmpl.render({'value': ''}) == '0' assert tmpl.render({'value': None}) == '0' + def test_abs_templatetag(): tmpl = Template('{{ value|abs }}') assert tmpl.render({'value': 3.14}) == '3.14' @@ -488,6 +500,7 @@ def test_token_alphanum(): t = Template('{% if token1|token_check:token2 %}ok{% endif %}') assert t.render({'token1': tokens[0] + ' ', 'token2': tokens[0].lower()}) == 'ok' + def test_distance(): t = Template('{{ "48;2"|distance:"48.1;2.1"|floatformat }}',) assert t.render() == '13387.2' @@ -517,6 +530,7 @@ def test_distance(): t = Template(tpl,) assert t.render({'formdata': lazy_formdata, 'coords': '49.1;3.1'}) == '' + def test_get_filter(): tmpl = Template('{{ foo|get:"bar" }}') assert tmpl.render({'foo': {'bar': 'baz'}}) == 'baz' @@ -524,6 +538,7 @@ def test_get_filter(): tmpl = Template('{{ foo|get:0 }}') assert tmpl.render({'foo': ['bar', 'baz']}) == 'bar' + def test_reproj(): class MockFormData(object): diff --git a/tests/test_texts.py b/tests/test_texts.py index 6dade49d8..3ea1e050f 100644 --- a/tests/test_texts.py +++ b/tests/test_texts.py @@ -7,6 +7,7 @@ from wcs.qommon.admin.texts import TextsDirectory from utilities import create_temporary_pub, MockSubstitutionVariables + def setup_module(module): cleanup() global pub @@ -18,17 +19,21 @@ def setup_module(module): user = pub.user_class(name='foo') user.store() + def teardown_module(module): shutil.rmtree(pub.APP_DIR) + def test_get_html_text_unset(): TextsDirectory.register('foo', 'Foo') assert TextsDirectory.get_html_text('foo') == '' + def test_get_html_text_default(): TextsDirectory.register('foo2', 'Foo', default='Foo...') assert TextsDirectory.get_html_text('foo2') == '

Foo...

' + def test_get_html_text_set(): TextsDirectory.register('foo3', 'Foo', default='Foo...') pub.cfg['texts'] = {'text-foo3': None} @@ -41,6 +46,7 @@ def test_get_html_text_set(): pub.write_cfg() assert TextsDirectory.get_html_text('foo3') == '
Bar...
' + def test_get_html_subst(): # test for variable substitution TextsDirectory.register('foo4', 'Foo', default='Foo...') diff --git a/tests/test_tracking_code.py b/tests/test_tracking_code.py index c6fc2829b..255ef0505 100644 --- a/tests/test_tracking_code.py +++ b/tests/test_tracking_code.py @@ -4,17 +4,21 @@ from wcs.formdef import FormDef from utilities import create_temporary_pub, clean_temporary_pub + def pytest_generate_tests(metafunc): if 'pub' in metafunc.fixturenames: metafunc.parametrize('pub', ['pickle', 'sql'], indirect=True) + @pytest.fixture def pub(request): return create_temporary_pub(sql_mode=(request.param == 'sql')) + def teardown_module(module): clean_temporary_pub() + def test_tracking_code(pub): klass = pub.tracking_code_class klass.wipe() @@ -46,6 +50,7 @@ def test_tracking_code(pub): assert klass.get(code.id).formdata.tracking_code == code.id + def test_tracking_code_duplicate(pub): klass = pub.tracking_code_class klass.wipe() diff --git a/tests/test_users.py b/tests/test_users.py index ca039e197..2215ecb44 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -58,6 +58,7 @@ def test_get_users_with_role(): assert len(pub.user_class.get_users_with_role(1)) == 1 assert pub.user_class.get_users_with_role(1)[0].name == 'Pierre' + def test_user_formdef_getattr(): from wcs.admin.settings import UserFieldsFormDef formdef = UserFieldsFormDef(pub) diff --git a/tests/test_variadic_url.py b/tests/test_variadic_url.py index 690cc1565..0880d3edb 100644 --- a/tests/test_variadic_url.py +++ b/tests/test_variadic_url.py @@ -5,10 +5,12 @@ from wcs.qommon.ezt import EZTException from utilities import create_temporary_pub, clean_temporary_pub + @pytest.fixture def pub(request): return create_temporary_pub() + def teardown_module(module): clean_temporary_pub() @@ -303,6 +305,7 @@ def test_url_base_and_missing_var(pub): assert get_variadic_url('{{ url }}foobar/{{ path }}', {'url': 'http://www.example.net/'}) == 'http://www.example.net/foobar/' + def test_url_bad_syntax(pub): with pytest.raises(EZTException): get_variadic_url('[if-any form_avr]https://example.net/[foo]/', {'foo': 'bar'}) diff --git a/tests/test_widgets.py b/tests/test_widgets.py index 0bc7968f3..eb2b82a9d 100644 --- a/tests/test_widgets.py +++ b/tests/test_widgets.py @@ -18,6 +18,7 @@ from wcs.qommon.http_request import HTTPRequest from utilities import create_temporary_pub + def setup_module(module): cleanup() global pub, req @@ -27,9 +28,11 @@ def setup_module(module): req.language = None pub._set_request(req) + def teardown_module(module): shutil.rmtree(pub.APP_DIR) + class MockHtmlForm(object): def __init__(self, widget): widget = copy.deepcopy(widget) @@ -52,6 +55,7 @@ class MockHtmlForm(object): def get_parsed_query(self): return parse_query(self.form._request_data()[1], 'utf-8') + def mock_form_submission(req, widget, html_vars={}, click=None, hidden_html_vars={}): form = MockHtmlForm(widget) for k, v in html_vars.items(): @@ -64,6 +68,7 @@ def mock_form_submission(req, widget, html_vars={}, click=None, hidden_html_vars else: req.form = form.get_parsed_query() + def test_stringwidget_values(): widget = StringWidget('test') form = MockHtmlForm(widget) @@ -83,6 +88,7 @@ def test_stringwidget_values(): mock_form_submission(req, widget, {'test': 'bar'}) assert widget.parse() == 'bar' + def test_stringwidget_required(): widget = StringWidget('test', value='foo', required=True) mock_form_submission(req, widget, {'test': ''}) @@ -93,6 +99,7 @@ def test_stringwidget_required(): assert not widget.has_error() assert widget.parse() == 'bar' + def test_table_list_rows(): widget = TableListRowsWidget('test', columns=['a', 'b', 'c']) form = MockHtmlForm(widget) @@ -107,6 +114,7 @@ def test_table_list_rows(): 'test$element1$col1': 'foo'}) assert widget.parse() == [[u'bar', None, None], [None, u'foo', None]] + def test_table_list_rows_add_row(): widget = TableListRowsWidget('test', columns=['a', 'b', 'c']) form = MockHtmlForm(widget) @@ -118,6 +126,7 @@ def test_table_list_rows_add_row(): for col in range(3): assert 'test$element%d$col%d' % (row, col) in form.as_html + def test_table_list_rows_required(): req.form = {} widget = TableListRowsWidget('test', columns=['a', 'b', 'c'], required=True) @@ -132,6 +141,7 @@ def test_table_list_rows_required(): widget = TableListRowsWidget('test', columns=['a', 'b', 'c'], required=True) assert not widget.has_error() + def test_table_list_rows_set_many_values(): widget = TableListRowsWidget('test', columns=['a', 'b', 'c']) form = MockHtmlForm(widget) @@ -149,6 +159,7 @@ def test_table_list_rows_set_many_values(): form = MockHtmlForm(widget) assert 'test$element%d$col%d' % (10, 0) in form.as_html + def test_table_widget(): req.form = {} widget = TableWidget('test', columns=['a', 'b', 'c'], rows=['A', 'B']) @@ -164,6 +175,7 @@ def test_table_widget(): mock_form_submission(req, widget, {'test$c-0-0': 'X', 'test$c-0-1': 'Y'}) assert widget.parse() == [[u'X', u'Y', None], [u'1', None, None]] + def test_passwordentry_widget_success(): widget = PasswordEntryWidget('test') form = MockHtmlForm(widget) @@ -282,6 +294,7 @@ def test_emailwidget(): mock_form_submission(req, widget, {'test': 'foo@localhost..localdomain'}) assert widget.has_error() + def test_date_widget(): widget = DateWidget('test') form = MockHtmlForm(widget) @@ -405,6 +418,7 @@ def test_wysiwygwidget(): assert widget.parse() == '

bla bla bla

' wcs.qommon.form._sanitizeHTML = sanitize_html + def test_select_widget(): widget = SingleSelectHintWidget('test', options=[ @@ -444,6 +458,7 @@ def test_select_widget(): ('peach', 'Peach', 'peach', {'disabled': True})]) assert not widget.has_valid_options() + def test_select_or_other_widget(): widget = SingleSelectWidgetWithOther('test', options=[('apple', 'Apple'), ('pear', 'Pear'), ('peach', 'Peach')]) @@ -563,6 +578,7 @@ def test_computed_expression_widget(): assert widget.has_error() assert widget.get_error().startswith('syntax error in ezt template') + def test_wcsextrastringwidget(): widget = WcsExtraStringWidget('test', value='foo', required=True) mock_form_submission(req, widget, {'test': ''}) @@ -573,6 +589,7 @@ def test_wcsextrastringwidget(): assert not widget.has_error() assert widget.parse() == 'bar' + def test_wcsextrastringwidget_regex_validation(): # check regex validation class FakeField: pass @@ -610,6 +627,7 @@ def test_wcsextrastringwidget_regex_validation(): mock_form_submission(req, widget, {'test': '12,34'}) assert widget.has_error() + def test_wcsextrastringwidget_builtin_validation(): class FakeField: pass fakefield = FakeField() @@ -636,6 +654,7 @@ def test_wcsextrastringwidget_builtin_validation(): mock_form_submission(req, widget, {'test': '1234'}) assert widget.has_error() + def test_wcsextrastringwidget_siren_validation(): class FakeField: pass fakefield = FakeField() @@ -824,6 +843,7 @@ def test_widgetdict_widget(): html_frags.index('name="test$element2key"') < # b html_frags.index('name="test$element1key"')) # c + def test_map_widget(): widget = MapWidget('test', title='Map') form = MockHtmlForm(widget) diff --git a/tests/test_workflow_import.py b/tests/test_workflow_import.py index 96cd162d3..27374fee2 100644 --- a/tests/test_workflow_import.py +++ b/tests/test_workflow_import.py @@ -25,34 +25,41 @@ from wcs.qommon.misc import indent_xml as indent from utilities import create_temporary_pub, clean_temporary_pub + @pytest.fixture def pub(request): return create_temporary_pub() + def teardown_module(module): clean_temporary_pub() + def export_to_indented_xml(workflow, include_id=False): workflow_xml = workflow.export_to_xml(include_id=include_id) indent(workflow_xml) return workflow_xml + def assert_import_export_works(wf, include_id=False): wf2 = Workflow.import_from_xml_tree( ET.fromstring(ET.tostring(wf.export_to_xml(include_id))), include_id) assert ET.tostring(export_to_indented_xml(wf)) == ET.tostring(export_to_indented_xml(wf2)) return wf2 + def test_empty(pub): wf = Workflow(name='empty') assert_import_export_works(wf) + def test_status(pub): wf = Workflow(name='status') st1 = wf.add_status('Status1', 'st1') st2 = wf.add_status('Status2', 'st2') assert_import_export_works(wf) + def test_status_actions(pub): wf = Workflow(name='status') st1 = wf.add_status('Status1', 'st1') @@ -66,6 +73,7 @@ def test_status_actions(pub): assert_import_export_works(wf) + def test_status_colour_css_class(pub): wf = Workflow(name='status') st1 = wf.add_status('Status1', 'st1') @@ -74,6 +82,7 @@ def test_status_colour_css_class(pub): st2 = wf.add_status('Status2', 'st2') assert_import_export_works(wf) + def test_status_forced_endpoint(pub): wf = Workflow(name='status') st1 = wf.add_status('Status1', 'st1') @@ -83,6 +92,7 @@ def test_status_forced_endpoint(pub): assert wf2.possible_status[0].forced_endpoint is True assert wf2.possible_status[1].forced_endpoint is False + def test_default_wf(pub): wf = Workflow.get_default_workflow() assert_import_export_works(wf) @@ -136,6 +146,7 @@ def test_action_dispatch(pub): wf2 = assert_import_export_works(wf, include_id=True) assert wf2.possible_status[0].items[0].role_id == 'Rolé [form_var_foo]' + def test_status_actions_named_role(pub): wf = Workflow(name='status') st1 = wf.add_status('Status1', 'st1') @@ -379,6 +390,7 @@ def test_variables_formdef(pub): wf2 = assert_import_export_works(wf) assert wf2.variables_formdef.fields[0].label == 'Test' + def test_wscall_action(pub): wf = Workflow(name='status') st1 = wf.add_status('Status1', 'st1') @@ -403,6 +415,7 @@ def test_wscall_action(pub): assert wscall2.post_data == {'one': '1', 'two': '=2', 'good:name': 'ok'} assert wscall2.qs_data == {'one': '2', 'two': '=3', 'good:name': 'ok'} + def test_backoffice_info_text(pub): wf = Workflow(name='info texts') st1 = wf.add_status('Status1', 'st1') @@ -419,6 +432,7 @@ def test_backoffice_info_text(pub): assert wf2.possible_status[0].backoffice_info_text == '

Foo

' assert wf2.possible_status[0].items[0].backoffice_info_text == '

Bar

' + def test_global_actions(pub): role = Role() role.id = '5' @@ -444,6 +458,7 @@ def test_global_actions(pub): wf2 = assert_import_export_works(wf, True) + def test_backoffice_fields(pub): wf = Workflow(name='bo fields') wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(wf) @@ -453,6 +468,7 @@ def test_backoffice_fields(pub): ] wf2 = assert_import_export_works(wf, True) + def test_complex_dispatch_action(pub): wf = Workflow(name='status') st1 = wf.add_status('Status1', 'st1') @@ -510,6 +526,7 @@ def test_complex_dispatch_action(pub): xml_export = export_to_indented_xml(wf, include_id=True) assert xml_export.find('possible_status/status/items/item/rules') is None + def test_display_message_action(pub): wf = Workflow(name='status') st1 = wf.add_status('Status1', 'st1') @@ -529,6 +546,7 @@ def test_display_message_action(pub): wf2 = assert_import_export_works(wf, include_id=True) + def test_sendmail_other_destination(pub): wf = Workflow(name='status') st1 = wf.add_status('Status1', 'st1') @@ -547,6 +565,7 @@ def test_sendmail_other_destination(pub): assert Role.count() == 0 assert wf2.possible_status[0].items[0].to == sendmail.to + def test_sendmail_attachments(pub): wf = Workflow(name='status') st1 = wf.add_status('Status1', 'st1') @@ -563,6 +582,7 @@ def test_sendmail_attachments(pub): wf2 = assert_import_export_works(wf) assert wf2.possible_status[0].items[0].attachments == [] + def test_sms(pub): wf = Workflow(name='status') st1 = wf.add_status('Status1', 'st1') @@ -578,6 +598,7 @@ def test_sms(pub): assert Role.count() == 0 assert wf2.possible_status[0].items[0].to == sendsms.to + def test_criticality_level(pub): wf = Workflow(name='criticality level') wf.criticality_levels = [ @@ -590,6 +611,7 @@ def test_criticality_level(pub): assert wf2.criticality_levels[0].name == 'green' assert wf2.criticality_levels[1].name == 'yellow' + def test_global_timeout_trigger(pub): wf = Workflow(name='global actions') ac1 = wf.add_global_action('Action', 'ac1') @@ -601,6 +623,7 @@ def test_global_timeout_trigger(pub): assert wf2.global_actions[0].triggers[-1].id == trigger.id assert wf2.global_actions[0].triggers[-1].anchor == trigger.anchor + def test_global_webservice_trigger(pub): wf = Workflow(name='global actions') ac1 = wf.add_global_action('Action', 'ac1') @@ -611,6 +634,7 @@ def test_global_webservice_trigger(pub): assert wf2.global_actions[0].triggers[-1].id == trigger.id assert wf2.global_actions[0].triggers[-1].identifier == trigger.identifier + def test_profile_action(pub): wf = Workflow(name='status') st1 = wf.add_status('Status1', 'st1') @@ -625,6 +649,7 @@ def test_profile_action(pub): item2 = wf2.possible_status[0].items[0] assert item2.fields == [{'field_id': '__email', 'value': '=form_var_foo'}] + def test_set_backoffice_fields_action(pub): wf = Workflow(name='status') st1 = wf.add_status('Status1', 'st1') @@ -639,6 +664,7 @@ def test_set_backoffice_fields_action(pub): item2 = wf2.possible_status[0].items[0] assert item2.fields == [{'field_id': 'bo1', 'value': '=form_var_foo'}] + def test_action_condition(pub): wf = Workflow(name='status') st1 = wf.add_status('Status1', 'st1') diff --git a/tests/test_workflows.py b/tests/test_workflows.py index 825f922fe..dae4e634b 100644 --- a/tests/test_workflows.py +++ b/tests/test_workflows.py @@ -51,16 +51,20 @@ from wcs.wf.notification import SendNotificationWorkflowStatusItem from utilities import (create_temporary_pub, MockSubstitutionVariables, clean_temporary_pub) + def setup_module(module): cleanup() + def teardown_module(module): clean_temporary_pub() + def pytest_generate_tests(metafunc): if 'two_pubs' in metafunc.fixturenames: metafunc.parametrize('two_pubs', ['pickle', 'sql'], indirect=True) + @pytest.fixture def pub(request): pub = create_temporary_pub() @@ -74,6 +78,7 @@ def pub(request): pub.set_config(req) return pub + @pytest.fixture def two_pubs(request): pub = create_temporary_pub(sql_mode=(request.param == 'sql')) @@ -87,6 +92,7 @@ def two_pubs(request): pub.set_config(req) return pub + def test_get_json_export_dict(pub): workflow = Workflow(name='wf') st1 = workflow.add_status('Status1', 'st1') @@ -119,6 +125,7 @@ def test_get_json_export_dict(pub): assert root['statuses'][1]['forced_endpoint'] is True assert root['statuses'][1]['endpoint'] is True + def test_variable_compute(pub): FormDef.wipe() formdef = FormDef() @@ -175,6 +182,7 @@ def test_variable_compute(pub): assert item.compute('{{ form_var_foo|safe }}') == 'hello' # no escaping (implicit |safe) assert item.compute('{{ form_var_foo|escape }}') == '<b>hello</b>' #escaping + def test_variable_compute_dates(pub): FormDef.wipe() formdef = FormDef() @@ -193,6 +201,7 @@ def test_variable_compute_dates(pub): assert item.compute('=date(form_var_foo) + days(1)') == datetime.date(2017, 7, 18) assert item.compute('=date(2017, 7, 18)') == datetime.date(2017, 7, 18) + def test_jump_nothing(pub): FormDef.wipe() formdef = FormDef() @@ -202,6 +211,7 @@ def test_jump_nothing(pub): item = JumpWorkflowStatusItem() assert item.must_jump(formdata) is True + def test_jump_datetime_condition(pub): FormDef.wipe() formdef = FormDef() @@ -219,6 +229,7 @@ def test_jump_datetime_condition(pub): tomorrow.timetuple()[:3]} assert item.must_jump(formdata) is False + def test_jump_date_conditions(pub): FormDef.wipe() formdef = FormDef() @@ -264,6 +275,7 @@ def test_jump_date_conditions(pub): 'value': 'utils.time_delta(utils.time.localtime(), "2015-01-04").days > 0'} assert item.must_jump(formdata) is True + def test_jump_count_condition(pub): FormDef.wipe() formdef = FormDef() @@ -283,6 +295,7 @@ def test_jump_count_condition(pub): item.condition = {'type': 'python', 'value': 'form_objects.count < 2'} assert item.must_jump(formdata) is False + def test_jump_bad_python_condition(pub): FormDef.wipe() formdef = FormDef() @@ -315,6 +328,7 @@ def test_jump_bad_python_condition(pub): assert logged_error.expression == '~ invalid ~' assert logged_error.expression_type == 'python' + def test_jump_django_conditions(pub): FormDef.wipe() formdef = FormDef() @@ -351,6 +365,7 @@ def test_jump_django_conditions(pub): assert logged_error.expression == '~ invalid ~' assert logged_error.expression_type == 'django' + def test_check_auth(pub): user = pub.user_class(name='foo') user.store() @@ -398,6 +413,7 @@ def test_check_auth(pub): formdata.workflow_roles = None assert status_item.check_auth(formdata, user) is True + def test_dispatch(pub): formdef = FormDef() formdef.name = 'baz' @@ -419,6 +435,7 @@ def test_dispatch(pub): item.perform(formdata) assert formdata.workflow_roles == {'_receiver': role.id} + def test_dispatch_auto(pub): formdef = FormDef() formdef.name = 'baz' @@ -478,6 +495,7 @@ def test_dispatch_auto(pub): item.perform(formdata) assert formdata.workflow_roles == {'_receiver': role2.id} + def test_dispatch_computed(pub, caplog): pub.cfg['debug'] = {'logger': True} pub.write_cfg() @@ -518,6 +536,7 @@ def test_dispatch_computed(pub, caplog): assert not formdata.workflow_roles assert caplog.records[-1].message == 'error in dispatch, missing role (="foobar")' + def test_roles(pub): user = pub.user_class() user.store() @@ -559,6 +578,7 @@ def test_roles(pub): item.perform(formdata) assert pub.user_class.get(user.id).roles == ['2'] + def test_add_remove_computed_roles(pub): user = pub.user_class() user.store() @@ -605,6 +625,7 @@ def test_add_remove_computed_roles(pub): item.perform(formdata) assert pub.user_class.get(user.id).roles == [role2.id] + def test_roles_idp(pub): pub.cfg['sp'] = {'idp-manage-user-attributes': True} pub.cfg['idp'] = {'xxx': {'metadata_url': 'http://idp.example.net/idp/saml2/metadata'}} @@ -680,6 +701,7 @@ def test_roles_idp(pub): item2.perform(formdata) assert pub.user_class.get(user.id).roles == [] + def test_anonymise(two_pubs): # build a backoffice field Workflow.wipe() @@ -725,6 +747,7 @@ def test_anonymise(two_pubs): assert formdef.data_class().get(formdata.id).workflow_data is None assert formdef.data_class().get(formdata.id).evolution[0].who is None + def test_remove(pub): formdef = FormDef() formdef.name = 'baz' @@ -750,6 +773,7 @@ def test_remove(pub): req.response.filter = {} assert req.session.message + def test_register_comment(pub): pub.substitutions.feed(MockSubstitutionVariables()) @@ -832,6 +856,7 @@ def test_register_comment(pub): formdata.evolution[-1]._display_parts = None assert formdata.evolution[-1].display_parts()[-1] == '
1 < 3
' + def test_register_comment_django_escaping(pub, emails): formdef = FormDef() formdef.name = 'baz' @@ -857,6 +882,7 @@ def test_register_comment_django_escaping(pub, emails): formdata.evolution[-1]._display_parts = None assert formdata.evolution[-1].display_parts()[-1] == '

hello

' + def test_register_comment_attachment(pub): pub.substitutions.feed(MockSubstitutionVariables()) @@ -1199,6 +1225,7 @@ def test_email(pub, emails): assert emails.count() == 1 assert emails.get('foobar').get('from') == 'foobar@localhost' + def test_email_django_escaping(pub, emails): formdef = FormDef() formdef.name = 'baz' @@ -1252,6 +1279,7 @@ def test_email_django_escaping(pub, emails): assert emails.count() == 1 assert emails.get('1 < 3') + def test_email_attachments(pub, emails): formdef = FormDef() formdef.name = 'baz' @@ -1706,6 +1734,7 @@ def test_webservice_call(http_requests, pub): payload = json.loads(http_requests.get_last('body')) assert payload == {'one': 1, 'str': 'abcd', 'evalme': formdata.get_display_id()} + def test_webservice_waitpoint(pub): item = WebserviceCallStatusItem() assert item.waitpoint @@ -1718,6 +1747,7 @@ def test_webservice_waitpoint(pub): item.action_on_network_errors = ':stop' assert item.waitpoint + def test_webservice_call_error_handling(http_requests, pub): pub.substitutions.feed(MockSubstitutionVariables()) @@ -1944,6 +1974,7 @@ def test_webservice_call_error_handling(http_requests, pub): assert 'ConnectionError: error\n' in formdata.evolution[-1].parts[-1].summary assert formdata.workflow_data['plop_connection_error'] == 'error' + def test_webservice_call_store_in_backoffice_filefield(http_requests, pub): wf = Workflow(name='wscall to backoffice file field') wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(wf) @@ -2052,6 +2083,7 @@ def test_webservice_target_status(pub): assert targets.count(status1) == 2 assert targets.count(status2) == 2 + def test_timeout(two_pubs): workflow = Workflow(name='timeout') st1 = workflow.add_status('Status1', 'st1') @@ -2090,6 +2122,7 @@ def test_timeout(two_pubs): formdef.store() _apply_timeouts(two_pubs) + def test_legacy_timeout(pub): workflow = Workflow(name='timeout') st1 = workflow.add_status('Status1', 'st1') @@ -2121,6 +2154,7 @@ def test_legacy_timeout(pub): assert formdef.data_class().get(formdata_id).status == 'wf-st2' + def test_timeout_then_remove(two_pubs): workflow = Workflow(name='timeout-then-remove') st1 = workflow.add_status('Status1', 'st1') @@ -2159,6 +2193,7 @@ def test_timeout_then_remove(two_pubs): assert not str(formdata_id) in [str(x) for x in formdef.data_class().keys()] + def test_timeout_with_mark(two_pubs): workflow = Workflow(name='timeout') st1 = workflow.add_status('Status1', 'st1') @@ -2193,6 +2228,7 @@ def test_timeout_with_mark(two_pubs): formdata = formdef.data_class().get(formdata_id) assert formdata.workflow_data.get('_markers_stack') == [{'status_id': 'st1'}] + def test_sms(pub, sms_mocking): pub.cfg['sms'] = {'mode': 'xxx'} formdef = FormDef() @@ -2244,6 +2280,7 @@ def test_sms(pub, sms_mocking): item.perform(formdata) # nothing assert len(sms_mocking.sms) == 4 + def test_sms_with_passerelle(pub): pub.cfg['sms'] = {'mode': 'passerelle', 'passerelle_url': 'http://passerelle.example.com/send?nostop=1', @@ -2277,6 +2314,7 @@ def test_sms_with_passerelle(pub): assert json_payload['to'] == ['1234'] assert json_payload['from'] == 'Passerelle' + def test_display_form(two_pubs): formdef = FormDef() formdef.name = 'baz' @@ -2329,6 +2367,7 @@ def test_display_form(two_pubs): two_pubs.cfg['language'] = {'language': 'en'} + def test_display_form_and_comment(pub): role = Role(name='bar1') role.store() @@ -2374,6 +2413,7 @@ def test_display_form_and_comment(pub): assert 'Test' in str(form.widgets[0].render()) assert 'TEST' in str(form.render()) + def test_workflow_role_type_migration(pub): workflow = Workflow(name='role migration') st1 = workflow.add_status('Status1', 'st1') @@ -2454,6 +2495,7 @@ def test_workflow_role_type_migration(pub): reloaded_workflow = Workflow.get(workflow.id) assert reloaded_workflow.possible_status[0].items[0].by == ['1', '2'] + def test_workflow_display_message(pub): pub.substitutions.feed(MockSubstitutionVariables()) @@ -2492,6 +2534,7 @@ def test_workflow_display_message(pub): display_message.message = '[foo]' assert display_message.get_message(formdata) == '1 < 3' + def test_workflow_display_message_to(pub): workflow = Workflow(name='display message to') st1 = workflow.add_status('Status1', 'st1') @@ -2573,6 +2616,7 @@ def test_workflow_display_message_to(pub): assert 'd1' in formdata.get_workflow_messages() assert 'd2' in formdata.get_workflow_messages() + def test_workflow_display_message_line_details(pub): workflow = Workflow(name='display message to') st1 = workflow.add_status('Status1', 'st1') @@ -2592,6 +2636,7 @@ def test_workflow_display_message_line_details(pub): display_message.to = [role.id] assert display_message.get_line_details() == 'with actions, for foorole' + def test_workflow_roles(pub, emails): pub.substitutions.feed(MockSubstitutionVariables()) @@ -2648,6 +2693,7 @@ def test_workflow_roles(pub, emails): assert substvars.get('form_role_slug_with_dash_name') == 'foo' assert substvars.get('form_role_slug_with_dash_details') == 'Hello World' + def test_criticality(pub): FormDef.wipe() @@ -2695,6 +2741,7 @@ def test_criticality(pub): item.perform(formdata) assert formdata.get_criticality_level_object().name == 'green' + def test_geolocate_address(pub): formdef = FormDef() formdef.geolocations = {'base': 'bla'} @@ -2791,6 +2838,7 @@ def test_geolocate_address(pub): item.perform(formdata) assert formdata.geolocations == {} + def test_geolocate_image(pub): formdef = FormDef() formdef.name = 'baz' @@ -2837,6 +2885,7 @@ def test_geolocate_image(pub): item.perform(formdata) assert formdata.geolocations == {} + def test_geolocate_map(pub): formdef = FormDef() formdef.name = 'baz' @@ -2866,6 +2915,7 @@ def test_geolocate_map(pub): item.perform(formdata) assert formdata.geolocations == {} + def test_geolocate_overwrite(pub): formdef = FormDef() formdef.name = 'baz' @@ -2900,6 +2950,7 @@ def test_geolocate_overwrite(pub): assert int(formdata.geolocations['base']['lat']) == 48 assert int(formdata.geolocations['base']['lon']) == 3 + @pytest.mark.skipif(transform_to_pdf is None, reason='libreoffice not found') def test_transform_to_pdf(): instream = open(os.path.join(os.path.dirname(__file__), 'template.odt'), 'rb') @@ -2907,6 +2958,7 @@ def test_transform_to_pdf(): assert outstream is not False assert outstream.read(10).startswith(b'%PDF-') + def test_export_to_model_image(pub): formdef = FormDef() formdef.name = 'baz' @@ -2965,6 +3017,7 @@ def test_export_to_model_image(pub): assert formdata.evolution[-1].parts[-1].base_filename == 'formulaire-%s-%s-2.odt' % (formdef.id, formdata.id) + def test_export_to_model_backoffice_field(pub): wf = Workflow(name='email with attachments') wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(wf) @@ -3030,6 +3083,7 @@ def test_export_to_model_backoffice_field(pub): item.perform(formdata) assert formdata.data == {} + def test_export_to_model_django_template(pub): formdef = FormDef() formdef.name = 'foo-export-to-template-with-django' @@ -3073,6 +3127,7 @@ def test_export_to_model_django_template(pub): new_content = zipfile.ZipFile(open(formdata.evolution[0].parts[2].filename, 'rb')).read('content.xml') assert b'>A <> name<' in new_content + def test_global_timeouts(two_pubs): pub = two_pubs FormDef.wipe() @@ -3291,6 +3346,7 @@ def test_global_timeouts(two_pubs): assert formdef.data_class().get(formdata1.id).get_criticality_level_object().name == 'green' formdata1.store() + def test_profile(two_pubs): User = two_pubs.user_class user = User() @@ -3505,6 +3561,7 @@ def test_set_backoffice_field(http_requests, two_pubs): assert logged_error.exception_class == 'TemplateError' assert logged_error.exception_message.startswith('syntax error in Django template') + def test_set_backoffice_field_file(http_requests, two_pubs): Workflow.wipe() FormDef.wipe() @@ -3651,6 +3708,7 @@ def test_set_backoffice_field_file(http_requests, two_pubs): assert formdata.data.get('bo1') is None assert formdata.data.get('bo3') is None + def test_set_backoffice_field_item(two_pubs): Workflow.wipe() FormDef.wipe() @@ -3756,6 +3814,7 @@ def test_set_backoffice_field_item(two_pubs): assert formdata.data.get('bo1_display') is None assert formdata.data.get('bo1_structured') is None + def test_set_backoffice_field_items(two_pubs): Workflow.wipe() FormDef.wipe() @@ -3832,6 +3891,7 @@ def test_set_backoffice_field_items(two_pubs): assert {'id': 'a', 'more': 'aaa', 'text': 'aa'} in formdata.data['bo1_structured'] assert {'id': 'c', 'more': 'ccc', 'text': 'cc'} in formdata.data['bo1_structured'] + def test_set_backoffice_field_date(two_pubs): Workflow.wipe() FormDef.wipe() @@ -3964,6 +4024,7 @@ def test_set_backoffice_field_immediate_use(http_requests, two_pubs): assert formdata.data.get('bo1') == 'XHELLOX' assert formdata.data.get('bo2') == 'YXHELLOXY' + def test_redirect_to_url(pub): formdef = FormDef() formdef.name = 'baz' @@ -3995,6 +4056,7 @@ def test_redirect_to_url(pub): pub.substitutions.feed(formdata) assert item.perform(formdata) == None + def test_workflow_jump_condition_migration(pub): workflow = Workflow(name='jump condition migration') st1 = workflow.add_status('Status1', 'st1') @@ -4014,6 +4076,7 @@ def test_workflow_jump_condition_migration(pub): assert reloaded_workflow.possible_status[0].items[0].condition == { 'type': 'python', 'value': 'foobar'} + def test_workflow_action_condition(two_pubs): pub = two_pubs pub._set_request(None) # to avoid after jobs @@ -4089,6 +4152,7 @@ def test_workflow_action_condition(two_pubs): assert logged_error.expression == 'foobar == barfoo' assert logged_error.expression_type == 'python' + def test_notifications(pub, http_requests): formdef = FormDef() formdef.name = 'baz' @@ -4159,6 +4223,7 @@ def test_notifications(pub, http_requests): 'https://portal/api/notification/add/?NameID=xxy1', 'https://portal/api/notification/add/?NameID=xxy2']) + def test_workflow_field_migration(pub): Workflow.wipe() wf = Workflow(name='wf with backoffice field') diff --git a/tests/test_wscall.py b/tests/test_wscall.py index f716df90a..5376380bd 100644 --- a/tests/test_wscall.py +++ b/tests/test_wscall.py @@ -110,6 +110,7 @@ def test_webservice_post_with_no_payload(http_requests, pub): wscall.call() assert http_requests.get_last('body') is None + def test_wscall_ezt(http_requests, pub): NamedWsCall.wipe() @@ -133,6 +134,7 @@ def test_wscall_ezt(http_requests, pub): template = Template('

[webservice.hello.foo]

') assert template.render(variables) == '

[webservice.hello.foo]

' + def test_webservice_post_put_patch(http_requests, pub): NamedWsCall.wipe() @@ -149,6 +151,7 @@ def test_webservice_post_put_patch(http_requests, pub): assert http_requests.get_last('method') == wscall.request['method'] assert json.loads(http_requests.get_last('body')) == wscall.request['post_data'] + def test_webservice_delete(http_requests, pub): NamedWsCall.wipe() diff --git a/tests/test_x509utils.py b/tests/test_x509utils.py index 322bb2302..6d0cde663 100644 --- a/tests/test_x509utils.py +++ b/tests/test_x509utils.py @@ -1,5 +1,6 @@ from wcs.qommon.x509utils import * + def test_x509utils(): assert can_generate_rsa_key_pair() publickey, privatekey = generate_rsa_keypair() diff --git a/tests/utilities.py b/tests/utilities.py index acd59fbca..1124222a0 100644 --- a/tests/utilities.py +++ b/tests/utilities.py @@ -32,6 +32,7 @@ import wcs.middleware wcs.middleware.AfterJobsMiddleware.ASYNC = False + class KnownElements(object): pickle_app_dir = None sql_app_dir = None @@ -41,6 +42,7 @@ class KnownElements(object): known_elements = KnownElements() + def create_temporary_pub(sql_mode=False, templates_mode=False, lazy_mode=False): if sql_mode is True: if pytest.config.getoption('without_postgresql_tests'): @@ -175,6 +177,7 @@ def create_temporary_pub(sql_mode=False, templates_mode=False, lazy_mode=False): return pub + def clean_temporary_pub(): if get_publisher(): get_publisher().cleanup() @@ -224,6 +227,7 @@ def get_app(pub, https=False): extra_environ['HTTPS'] = 'off' return TestApp(wcs.wsgi.application, extra_environ=extra_environ) + def login(app, username='admin', password='admin'): login_page = app.get('/login/') login_form = login_page.forms['login-form'] diff --git a/wcs/admin/categories.py b/wcs/admin/categories.py index 891821a19..b9a0e8e44 100644 --- a/wcs/admin/categories.py +++ b/wcs/admin/categories.py @@ -25,6 +25,7 @@ from wcs.qommon.backoffice.menu import html_top from wcs.formdef import FormDef + class CategoryUI(object): def __init__(self, category): self.category = category diff --git a/wcs/admin/data_sources.py b/wcs/admin/data_sources.py index ada9af699..4e75110eb 100644 --- a/wcs/admin/data_sources.py +++ b/wcs/admin/data_sources.py @@ -27,6 +27,7 @@ from wcs.data_sources import (NamedDataSource, DataSourceSelectionWidget, get_structured_items) from wcs.formdef import FormDef, get_formdefs_of_all_kinds + class NamedDataSourceUI(object): def __init__(self, datasource): self.datasource = datasource diff --git a/wcs/admin/fields.py b/wcs/admin/fields.py index 428df71ba..e77a5c35f 100644 --- a/wcs/admin/fields.py +++ b/wcs/admin/fields.py @@ -31,6 +31,7 @@ from wcs.formdef import FormDef from wcs.fields import get_field_types import copy + class FieldDefPage(Directory): _q_exports = ['', 'delete', 'duplicate'] diff --git a/wcs/admin/forms.py b/wcs/admin/forms.py index c21f83aa8..8d335bea9 100644 --- a/wcs/admin/forms.py +++ b/wcs/admin/forms.py @@ -49,6 +49,7 @@ from .categories import CategoriesDirectory from .data_sources import NamedDataSourcesDirectory from .logged_errors import LoggedErrorsDirectory + def get_categories(): t = sorted([(misc.simplify(x.name), x.id, x.name, x.id) for x in Category.select()]) return [x[1:] for x in t] diff --git a/wcs/admin/logged_errors.py b/wcs/admin/logged_errors.py index 15432569e..283ae202e 100644 --- a/wcs/admin/logged_errors.py +++ b/wcs/admin/logged_errors.py @@ -25,6 +25,7 @@ from wcs.qommon.misc import localstrftime from wcs.logged_errors import LoggedError + class LoggedErrorDirectory(Directory): _q_exports = ['', 'delete', 'ack'] diff --git a/wcs/admin/roles.py b/wcs/admin/roles.py index c8d7665b2..1c5b1d7d4 100644 --- a/wcs/admin/roles.py +++ b/wcs/admin/roles.py @@ -27,6 +27,7 @@ from wcs.qommon.backoffice.menu import html_top from wcs.roles import Role, get_user_roles from wcs.formdef import FormDef + class RoleUI(object): def __init__(self, role): self.role = role diff --git a/wcs/admin/settings.py b/wcs/admin/settings.py index ec080d665..06071ace4 100644 --- a/wcs/admin/settings.py +++ b/wcs/admin/settings.py @@ -62,6 +62,7 @@ from .fields import FieldDefPage, FieldsDirectory from .data_sources import NamedDataSourcesDirectory from .wscalls import NamedWsCallsDirectory + class UserFormDirectory(Directory): _q_exports = [''] diff --git a/wcs/admin/users.py b/wcs/admin/users.py index 1a38f94d6..186bb93a9 100644 --- a/wcs/admin/users.py +++ b/wcs/admin/users.py @@ -35,6 +35,7 @@ from wcs.qommon.admin.emails import EmailsDirectory from wcs.qommon.backoffice.menu import html_top from wcs.qommon.admin.menu import error_page + class UserUI(object): def __init__(self, user): self.user = user @@ -298,6 +299,7 @@ class UserPage(Directory): get_response().breadcrumb.append((component + '/', None)) return ident.get_method_user_directory(component, self.user) + class UsersDirectory(Directory): _q_exports = ['', 'new'] diff --git a/wcs/admin/workflows.py b/wcs/admin/workflows.py index 6480d9660..9fe571b12 100644 --- a/wcs/admin/workflows.py +++ b/wcs/admin/workflows.py @@ -52,6 +52,7 @@ from wcs.backoffice.studio import StudioDirectory def svg(tag): return '{http://www.w3.org/2000/svg}%s' % tag + def xlink(tag): return '{http://www.w3.org/1999/xlink}%s' % tag @@ -59,11 +60,13 @@ TITLE = svg('title') POLYGON = svg('polygon') XLINK_TITLE = xlink('title') + def remove_tag(node, tag): for child in node: if child.tag == tag: node.remove(child) + def remove_attribute(node, att): if att in node.attrib: del node.attrib[att] @@ -98,6 +101,7 @@ def adjust_style(node, top, colours, white_text=False, colour_class=None): remove_attribute(child, 'style') adjust_style(child, top, colours, white_text=white_text, colour_class=colour_class) + def graphviz_post_treatment(content, colours, include=False): ''' Remove all svg:title and top-level svg:polygon nodes, remove style attributes and xlink:title attributes. @@ -120,6 +124,7 @@ def graphviz_post_treatment(content, colours, include=False): adjust_style(child, child, colours) return force_str(ET.tostring(tree)) + def graphviz(workflow, url_prefix='', select=None, svg=True, include=False): out = StringIO() @@ -249,6 +254,7 @@ class WorkflowUI(object): workflow.store() return workflow + class WorkflowItemPage(Directory): _q_exports = ['', 'delete'] @@ -373,6 +379,7 @@ class GlobalActionTriggerPage(Directory): self.workflow.store() return redirect('../../') + class ToChildDirectory(Directory): _q_exports = [''] klass = None diff --git a/wcs/admin/wscalls.py b/wcs/admin/wscalls.py index 444b12a8b..941817f63 100644 --- a/wcs/admin/wscalls.py +++ b/wcs/admin/wscalls.py @@ -24,6 +24,7 @@ from wcs.qommon.form import * from wcs.qommon.backoffice.menu import html_top from wcs.wscalls import NamedWsCall, WsCallRequestWidget + class NamedWsCallUI(object): def __init__(self, wscall): self.wscall = wscall diff --git a/wcs/api.py b/wcs/api.py index 0085f4e35..d80e718b2 100644 --- a/wcs/api.py +++ b/wcs/api.py @@ -46,6 +46,7 @@ from wcs.api_utils import sign_url_auto_orig, is_url_signed, get_user_from_api_q from .backoffice.management import FormPage as BackofficeFormPage from .backoffice.management import ManagementDirectory + def posted_json_data_to_formdata_data(formdef, data): # remap fields from varname to field id for field in formdef.get_all_fields(): @@ -855,6 +856,7 @@ def reverse_geocoding(request, *args, **kwargs): url += '&accept-language=%s' % (get_publisher().get_site_language() or 'en') return HttpResponse(misc.urlopen(url).read(), content_type='application/json') + def geocoding(request, *args, **kwargs): if not 'q' in request.GET: return HttpResponseBadRequest() @@ -883,6 +885,7 @@ def validate_expression(request, *args, **kwargs): hint['msg'] = _('Make sure you want a Python expression, not a simple template string.') return HttpResponse(json.dumps(hint), content_type='application/json') + def validate_condition(request, *args, **kwargs): condition = {} condition['type'] = request.GET.get('type') or '' diff --git a/wcs/api_utils.py b/wcs/api_utils.py index d5570cc0c..040c11ee6 100644 --- a/wcs/api_utils.py +++ b/wcs/api_utils.py @@ -189,6 +189,7 @@ def get_secret_and_orig(url): raise MissingSecret() return secret, orig + def sign_url_auto_orig(url): try: signature_key, orig = get_secret_and_orig(url) diff --git a/wcs/backoffice/management.py b/wcs/backoffice/management.py index c348d6fff..c75f85cc1 100644 --- a/wcs/backoffice/management.py +++ b/wcs/backoffice/management.py @@ -65,6 +65,7 @@ from wcs.workflows import get_role_translation, template_on_formdata from .submission import FormFillPage + def geojson_formdatas(formdatas, geoloc_key='base', fields=None): geojson = { 'type': 'FeatureCollection', @@ -2835,6 +2836,7 @@ def get_global_criteria(request, parsed_values=None): return criterias + def format_time(t, units = 2): days = int(t/86400) hours = int((t-days*86400)/3600) diff --git a/wcs/cache.py b/wcs/cache.py index cbee4c235..d7d9eb144 100644 --- a/wcs/cache.py +++ b/wcs/cache.py @@ -20,6 +20,7 @@ from django.core.cache.backends.base import InvalidCacheBackendError from quixote import get_publisher + class TenantBaseCache(object): '''Prepend the tenant application directory to the cache prefix''' def set_key_prefix(self, prefix): diff --git a/wcs/categories.py b/wcs/categories.py index 562f18c5f..ddd5e4598 100644 --- a/wcs/categories.py +++ b/wcs/categories.py @@ -23,6 +23,7 @@ from .qommon.misc import simplify from .qommon.substitution import Substitutions from .qommon.xml_storage import XmlStorableObject + class Category(XmlStorableObject): _names = 'categories' _xml_tagname = 'category' diff --git a/wcs/compat.py b/wcs/compat.py index 2331eae71..70fb07760 100644 --- a/wcs/compat.py +++ b/wcs/compat.py @@ -254,6 +254,7 @@ class CompatWcsPublisher(WcsPublisher): # the publisher instance can't be shared for concurrent requests. quixote_lock = Lock() + def quixote(request): pub = get_publisher() return pub.process_request(pub.get_request()) diff --git a/wcs/context_processors.py b/wcs/context_processors.py index 09617ae50..2a19b33d1 100644 --- a/wcs/context_processors.py +++ b/wcs/context_processors.py @@ -16,11 +16,13 @@ from quixote import get_publisher, get_response, get_request, get_session + def get_global_context(): pub = get_publisher() if pub: return pub.substitutions.get_context_variables(mode='lazy') + def publisher(request): template_base = 'wcs/base.html' if request.path.startswith('/backoffice/'): diff --git a/wcs/ctl/check_hobos.py b/wcs/ctl/check_hobos.py index 83c1d39e6..9e5fba0f6 100644 --- a/wcs/ctl/check_hobos.py +++ b/wcs/ctl/check_hobos.py @@ -43,6 +43,7 @@ from django.utils.encoding import force_bytes class NoChange(Exception): pass + def atomic_symlink(src, dst): if os.path.exists(dst) and os.readlink(dst) == src: return @@ -51,6 +52,7 @@ def atomic_symlink(src, dst): os.symlink(src, dst + '.tmp') os.rename(dst + '.tmp', dst) + class CmdCheckHobos(Command): name = 'hobo_deploy' diff --git a/wcs/ctl/export_settings.py b/wcs/ctl/export_settings.py index bdba47d6c..5f74855bb 100644 --- a/wcs/ctl/export_settings.py +++ b/wcs/ctl/export_settings.py @@ -20,6 +20,7 @@ import os from ..qommon.ctl import Command, make_option + class CmdExportSettings(Command): name = 'export_settings' diff --git a/wcs/ctl/management/commands/trigger_jumps.py b/wcs/ctl/management/commands/trigger_jumps.py index 2d488a94a..06937acdc 100644 --- a/wcs/ctl/management/commands/trigger_jumps.py +++ b/wcs/ctl/management/commands/trigger_jumps.py @@ -86,6 +86,7 @@ def get_rows(args): for row in json.load(file(arg)): yield row + def get_status_ids_accepting_trigger(workflow, trigger): for status in workflow.possible_status: for item in status.items: @@ -94,6 +95,7 @@ def get_status_ids_accepting_trigger(workflow, trigger): yield 'wf-%s' % status.id, item break + def get_formdata_accepting_trigger(formdef, trigger, status_ids=None): if status_ids is None: workflow = formdef.get_workflow() @@ -106,6 +108,7 @@ def get_formdata_accepting_trigger(formdef, trigger, status_ids=None): for formdata_id in formdata_ids: yield data_class.get(id=formdata_id), action_item + def match_row(substitution_variables, row): select = row['select'] for key, value in select.items(): @@ -113,6 +116,7 @@ def match_row(substitution_variables, row): return False return True + def jump_and_perform(formdata, action, workflow_data=None): get_publisher().substitutions.reset() get_publisher().substitutions.feed(get_publisher()) @@ -121,6 +125,7 @@ def jump_and_perform(formdata, action, workflow_data=None): print('formdata %s jumps to status %s' % (formdata, action.status)) wcs_jump_and_perform(formdata, action, workflow_data=workflow_data) + def select_and_jump_formdata(formdef, trigger, rows, status_ids=None): for formdata, action_item in get_formdata_accepting_trigger(formdef, trigger, status_ids): if rows == '__all__': diff --git a/wcs/ctl/rebuild_indexes.py b/wcs/ctl/rebuild_indexes.py index 4cdf64e9f..9adde784c 100644 --- a/wcs/ctl/rebuild_indexes.py +++ b/wcs/ctl/rebuild_indexes.py @@ -18,6 +18,7 @@ import os from ..qommon.ctl import Command, make_option + def rebuild_vhost_indexes(pub, destroy=False): from wcs.formdef import FormDef if destroy: diff --git a/wcs/ctl/runscript.py b/wcs/ctl/runscript.py index 6a3183fc1..e7ebe91ce 100644 --- a/wcs/ctl/runscript.py +++ b/wcs/ctl/runscript.py @@ -21,6 +21,7 @@ import sys from ..qommon.ctl import Command, make_option + class CmdRunScript(Command): '''Run a script within a given host publisher context''' diff --git a/wcs/ctl/shell.py b/wcs/ctl/shell.py index ab9a030a4..adf525c3f 100644 --- a/wcs/ctl/shell.py +++ b/wcs/ctl/shell.py @@ -25,6 +25,7 @@ import os.path from ..qommon.ctl import Command, make_option + class CmdShell(Command): '''Launch a shell and initialize a publisher on a given host''' diff --git a/wcs/ctl/wipe_data.py b/wcs/ctl/wipe_data.py index a0ff29d69..5e7b30f61 100644 --- a/wcs/ctl/wipe_data.py +++ b/wcs/ctl/wipe_data.py @@ -21,6 +21,7 @@ import sys from ..qommon.ctl import Command, make_option + class CmdWipeData(Command): name = 'wipe-data' diff --git a/wcs/data_sources.py b/wcs/data_sources.py index a2a4326cd..4f980ba6d 100644 --- a/wcs/data_sources.py +++ b/wcs/data_sources.py @@ -42,6 +42,7 @@ from .api_utils import sign_url_auto_orig data_source_functions = {} + def register_data_source_function(function, function_name=None): if not function_name: function_name = function.__name__ diff --git a/wcs/fields.py b/wcs/fields.py index 782d493ad..d0297ee3f 100644 --- a/wcs/fields.py +++ b/wcs/fields.py @@ -575,6 +575,7 @@ class WidgetField(Field): field_classes = [] field_types = [] + def register_field_class(klass): if not klass in field_classes: field_classes.append(klass) @@ -702,6 +703,7 @@ class CommentField(Field): register_field_class(CommentField) + def is_datasource_advanced(value): data_source_in_advanced = (not value) if data_source_in_advanced and (get_request().form.get('data_source$apply') or @@ -2411,6 +2413,7 @@ def get_field_class_by_type(type): return k raise KeyError() + def get_field_types(): return field_types diff --git a/wcs/formdata.py b/wcs/formdata.py index cf609c75c..abe91c6b6 100644 --- a/wcs/formdata.py +++ b/wcs/formdata.py @@ -95,6 +95,7 @@ def get_dict_with_varnames(fields, data, formdata=None, varnames_only=False): new_data['var_%s_%s_%s' % (field.varname, i, k)] = v return new_data + def flatten_dict(d): for k, v in list(d.items()): if type(v) is dict: diff --git a/wcs/forms/backoffice.py b/wcs/forms/backoffice.py index feed36706..5f33a3621 100644 --- a/wcs/forms/backoffice.py +++ b/wcs/forms/backoffice.py @@ -26,6 +26,7 @@ from ..qommon.backoffice.listing import pagination_links from wcs.qommon.storage import Null from wcs.roles import logged_users_role + class FormDefUI(object): def __init__(self, formdef): self.formdef = formdef diff --git a/wcs/forms/root.py b/wcs/forms/root.py index 63d342146..43a71f536 100644 --- a/wcs/forms/root.py +++ b/wcs/forms/root.py @@ -67,6 +67,7 @@ class SubmittedDraftException(Exception): def html_top(title = None): template.html_top(title = title, default_org = _('Forms')) + def get_user_forms(formdef): """Return forms data for the current user @@ -82,6 +83,7 @@ def get_user_forms(formdef): from wcs.forms.common import FormStatusPage + def tryauth(url): # tries to log the user in before redirecting to the asked url; this won't # do anything for local logins but will use a passive SAML request when @@ -96,6 +98,7 @@ def tryauth(url): login_url = '/login/?ReturnUrl=%s&IsPassive=true' % quote(url) return redirect(login_url) + def auth(url): # logs the user in before redirecting to asked url. if get_request().user: @@ -191,6 +194,7 @@ class TrackingCodesDirectory(Directory): def _q_lookup(self, component): return TrackingCodeDirectory(component, self.formdef) + class FormPage(Directory, FormTemplateMixin): _q_exports = ['', 'tempfile', 'schema', 'tryauth', 'auth', 'forceauth', 'qrcode', 'autosave', 'code', 'removedraft', 'live'] diff --git a/wcs/logged_errors.py b/wcs/logged_errors.py index b9c15ea4d..c60e97d78 100644 --- a/wcs/logged_errors.py +++ b/wcs/logged_errors.py @@ -21,6 +21,7 @@ from .qommon.xml_storage import XmlStorableObject from wcs.formdef import FormDef from wcs.workflows import Workflow + class LoggedError(XmlStorableObject): _names = 'logged-errors' _xml_tagname = 'error' diff --git a/wcs/monkeypatch.py b/wcs/monkeypatch.py index 7d473a18c..e220bf238 100644 --- a/wcs/monkeypatch.py +++ b/wcs/monkeypatch.py @@ -28,6 +28,7 @@ _thread_local = threading.local() cleanup_orig = quixote.publish.cleanup PublisherOrig = quixote.publish.Publisher + class Publisher(quixote.publish.Publisher): def __init__(self, root_directory, *args, **kwargs): try: @@ -41,24 +42,31 @@ class Publisher(quixote.publish.Publisher): def set_in_thread(self): _thread_local.publisher = self + def get_publisher(): return getattr(_thread_local, 'publisher', None) + def get_request(): return _thread_local.publisher.get_request() + def get_response(): return get_request() and _thread_local.publisher.get_request().response + def get_field(name, default=None): return _thread_local.publisher.get_request().get_field(name, default) + def get_cookie(name, default=None): return _thread_local.publisher.get_request().get_cookie(name, default) + def get_path(n=0): return _thread_local.publisher.get_request().get_path(n) + def redirect(location, permanent=False): """(location : string, permanent : boolean = false) -> string @@ -71,12 +79,15 @@ def redirect(location, permanent=False): location = urlparse.urljoin(request.get_url(), str(location)) return request.response.redirect(location, permanent) + def get_session(): return _thread_local.publisher.get_request().session + def get_session_manager(): return _thread_local.publisher.session_manager + def get_user(): session = _thread_local.publisher.get_request().session if session is None: @@ -84,6 +95,7 @@ def get_user(): else: return session.user + def cleanup(): cleanup_orig() _thread_local.publisher = None diff --git a/wcs/myspace.py b/wcs/myspace.py index e0a2ed396..1e146fd84 100644 --- a/wcs/myspace.py +++ b/wcs/myspace.py @@ -17,6 +17,7 @@ from quixote import get_request, redirect from .qommon import myspace + class MyspaceDirectory(myspace.MyspaceDirectory): _q_exports = ['', 'profile', 'new', 'password', 'remove', 'drafts', 'forms'] diff --git a/wcs/publisher.py b/wcs/publisher.py index 555b43969..d33a7ff0f 100644 --- a/wcs/publisher.py +++ b/wcs/publisher.py @@ -55,6 +55,7 @@ from .logged_errors import LoggedError import pickle + class UnpicklerClass(pickle.Unpickler): def find_class(self, module, name): if module == 'qommon.form': diff --git a/wcs/qommon/__init__.py b/wcs/qommon/__init__.py index bf2173615..85335a1a9 100644 --- a/wcs/qommon/__init__.py +++ b/wcs/qommon/__init__.py @@ -47,12 +47,14 @@ else: # unpickle python2 strings as bytes PICKLE_KWARGS = {'encoding': 'bytes', 'fix_imports': True} + def _(message): pub = get_publisher() if pub is None: return message return force_str(force_text(pub.gettext(str(message)))) + def ngettext(*args): pub = get_publisher() if pub is None: diff --git a/wcs/qommon/admin/cfg.py b/wcs/qommon/admin/cfg.py index 9b5b8e1bc..56a36ea42 100644 --- a/wcs/qommon/admin/cfg.py +++ b/wcs/qommon/admin/cfg.py @@ -18,6 +18,7 @@ from quixote import get_publisher from .. import get_cfg + def cfg_submit(form, cfg_key, fields): get_publisher().reload_cfg() cfg_key = str(cfg_key) diff --git a/wcs/qommon/admin/emails.py b/wcs/qommon/admin/emails.py index 6e65d6201..2b8998bda 100644 --- a/wcs/qommon/admin/emails.py +++ b/wcs/qommon/admin/emails.py @@ -24,6 +24,7 @@ from .. import misc, get_cfg from ..backoffice.menu import html_top from ..admin.cfg import cfg_submit + class EmailsDirectory(Directory): emails_dict = {} _q_exports = ['', 'options'] diff --git a/wcs/qommon/admin/logger.py b/wcs/qommon/admin/logger.py index 7e935eb6b..bde20b554 100644 --- a/wcs/qommon/admin/logger.py +++ b/wcs/qommon/admin/logger.py @@ -26,6 +26,7 @@ from .. import logger, errors from ..backoffice.menu import html_top from ..admin.menu import error_page + class ByUserDirectory(Directory): def _q_lookup(self, component): return ByUserPages(component) diff --git a/wcs/qommon/admin/menu.py b/wcs/qommon/admin/menu.py index d4e7054b8..27368542f 100644 --- a/wcs/qommon/admin/menu.py +++ b/wcs/qommon/admin/menu.py @@ -21,6 +21,7 @@ from .. import _ from ..backoffice.menu import html_top import re + def _find_vc_version(): '''Find current version of the source code''' import os.path @@ -91,9 +92,11 @@ def _find_vc_version(): vc_version = _find_vc_version() + def get_vc_version(): return vc_version + def command_icon(url, type, label=None, popup=False): labels = { 'add': N_('Add'), @@ -116,6 +119,7 @@ def command_icon(url, type, label=None, popup=False): %(label)s ''') % locals() + def error_page(section, error): html_top(section, title = _('Error')) r = TemplateIO(html=True) diff --git a/wcs/qommon/admin/settings.py b/wcs/qommon/admin/settings.py index bdbd71ee2..ea312cf2f 100644 --- a/wcs/qommon/admin/settings.py +++ b/wcs/qommon/admin/settings.py @@ -23,6 +23,7 @@ from ..form import * from ..admin.cfg import cfg_submit from ..backoffice.menu import html_top + class SettingsDirectory(AccessControlled, Directory): def _q_access(self): get_response().breadcrumb.append( ('settings/', _('Settings')) ) diff --git a/wcs/qommon/afterjobs.py b/wcs/qommon/afterjobs.py index 06f2ce5c9..e54172f8a 100644 --- a/wcs/qommon/afterjobs.py +++ b/wcs/qommon/afterjobs.py @@ -21,6 +21,7 @@ from . import errors from .http_response import AfterJob from . import _ + class AfterJobStatusDirectory(Directory): def _q_lookup(self, component): try: diff --git a/wcs/qommon/backoffice/listing.py b/wcs/qommon/backoffice/listing.py index eb5d44bb5..459329de6 100644 --- a/wcs/qommon/backoffice/listing.py +++ b/wcs/qommon/backoffice/listing.py @@ -20,6 +20,7 @@ from quixote import get_request, get_response from .. import _ + def pagination_links(offset, limit, total_count): get_response().add_javascript(['jquery.js', 'wcs.listing.js']) # pagination diff --git a/wcs/qommon/backoffice/menu.py b/wcs/qommon/backoffice/menu.py index 6048ceea3..f9c1d22b6 100644 --- a/wcs/qommon/backoffice/menu.py +++ b/wcs/qommon/backoffice/menu.py @@ -20,6 +20,7 @@ from quixote.html import htmltext, TemplateIO from .. import get_cfg from .. import _ + def generate_header_menu(selected = None): return get_publisher().get_backoffice_root().generate_header_menu(selected=selected) diff --git a/wcs/qommon/backoffice/root.py b/wcs/qommon/backoffice/root.py index b7a951ccb..5aae09160 100644 --- a/wcs/qommon/backoffice/root.py +++ b/wcs/qommon/backoffice/root.py @@ -20,6 +20,7 @@ from quixote import get_publisher, get_request, get_response from .. import _ from .. import errors + class BackofficeRootDirectory(AccessControlled, Directory): @classmethod diff --git a/wcs/qommon/cron.py b/wcs/qommon/cron.py index 46843d559..793353fde 100644 --- a/wcs/qommon/cron.py +++ b/wcs/qommon/cron.py @@ -18,6 +18,7 @@ import sys from django.conf import settings + class CronJob(object): name = None hours = None @@ -34,6 +35,7 @@ class CronJob(object): self.weekdays = weekdays self.days = days + def cron_worker(publisher, now, job_name=None): try: publisher.set_config() diff --git a/wcs/qommon/ctl.py b/wcs/qommon/ctl.py index 37db0a153..d323f42cd 100644 --- a/wcs/qommon/ctl.py +++ b/wcs/qommon/ctl.py @@ -31,6 +31,7 @@ from wcs import qommon from . import _ qommon._commands = {} + class Command(object): doc = '' name = None @@ -87,6 +88,7 @@ class Command(object): def register(cls): qommon._commands[cls.name] = cls + class Ctl(object): def __init__(self, cmd_prefixes=[]): self.cmd_prefixes = cmd_prefixes diff --git a/wcs/qommon/emails.py b/wcs/qommon/emails.py index efa3f715a..1ce1d7c15 100644 --- a/wcs/qommon/emails.py +++ b/wcs/qommon/emails.py @@ -110,6 +110,7 @@ def custom_template_email(key, mail_body_data, email_rcpt, **kwargs): return template_email(mail_subject, mail_body, mail_body_data, email_type=key, email_rcpt=email_rcpt, **kwargs) + def template_email(subject, mail_body, mail_body_data, email_rcpt, email_type=None, **kwargs): data = get_publisher().substitutions.get_context_variables() if mail_body_data: @@ -119,16 +120,19 @@ def template_email(subject, mail_body, mail_body_data, email_rcpt, email_type=No return email(real_subject, real_mail_body, email_rcpt=email_rcpt, email_type=email_type, **kwargs) + def data_as_octet_stream(data, filename, **kwargs): msg = MIMEApplication(data, **kwargs) msg.add_header('Content-Disposition', 'attachment', filename=filename) return msg + def data_as_text(data, filename, **kwargs): msg = MIMEText(data, **kwargs) msg.add_header('Content-Disposition', 'attachment', filename=filename) return msg + def convert_to_mime(attachment): if hasattr(attachment, 'get_file_pointer'): # qommon.form.PicklableUpload-like object attachment.get_file_pointer().seek(0) @@ -154,6 +158,7 @@ def convert_to_mime(attachment): return part get_logger().warn('Failed to build MIME part from %r', attachment) + def email(subject, mail_body, email_rcpt, replyto=None, bcc=None, email_from=None, exclude_current_user=False, email_type=None, want_html=True, hide_recipients=False, fire_and_forget=False, @@ -338,6 +343,7 @@ def email(subject, mail_body, email_rcpt, replyto=None, bcc=None, EmailToSend(email_from, rcpts, msg.as_string()), fire_and_forget = True) + def create_smtp_server(emails_cfg, smtp_timeout=None): try: s = smtplib.SMTP(emails_cfg.get('smtp_server', None) or 'localhost', @@ -373,6 +379,7 @@ def create_smtp_server(emails_cfg, smtp_timeout=None): raise errors.EmailError('Failed to authenticate to SMTP server, unknown error.') return s + class EmailToSend(object): def __init__(self, msg_from, rcpts, msg_as_string): self.msg_from = msg_from diff --git a/wcs/qommon/errors.py b/wcs/qommon/errors.py index d7435f552..246c77759 100644 --- a/wcs/qommon/errors.py +++ b/wcs/qommon/errors.py @@ -58,9 +58,11 @@ class AccessUnauthorizedError(AccessForbiddenError): login_url += '?' + urllib.urlencode({'next': request.get_frontoffice_url()}) return quixote.redirect(login_url) + class EmailError(Exception): pass + class InternalServerError(object): def render(self): from . import _ @@ -76,6 +78,7 @@ class InternalServerError(object): r += htmltext('

') return r.getvalue() + class FormError(Exception): def __init__(self, field, msg): self.field = field @@ -84,6 +87,7 @@ class FormError(Exception): def __str__(self): return self.msg + class ConnectionError(Exception): def __init__(self, msg): self.msg = msg @@ -91,6 +95,7 @@ class ConnectionError(Exception): def __str__(self): return self.msg + class ConfigurationError(Exception): def __init__(self, msg): self.msg = msg @@ -98,12 +103,15 @@ class ConfigurationError(Exception): def __str__(self): return self.msg + class LoginError(Exception): pass + class CertificateError(Exception): pass + class SMSError(Exception): pass diff --git a/wcs/qommon/evalutils.py b/wcs/qommon/evalutils.py index be514bc2f..a6143767e 100644 --- a/wcs/qommon/evalutils.py +++ b/wcs/qommon/evalutils.py @@ -63,17 +63,21 @@ def make_datetime(datetime_var): except ValueError: raise ValueError('invalid datetime value: %s' % datetime_var) + def date(var, month=None, day=None): if var and month and day: return datetime.date(int(var), int(month), int(day)) return make_date(var) + def days(count): return datetime.timedelta(days=int(count)) + def time_delta(t1, t2): return make_date(t1) - make_date(t2) + def date_delta(t1, t2): '''Return the timedelta between two date like values''' t1, t2 = make_date(t1), make_date(t2) diff --git a/wcs/qommon/ezt.py b/wcs/qommon/ezt.py index da547721b..99b036f45 100644 --- a/wcs/qommon/ezt.py +++ b/wcs/qommon/ezt.py @@ -275,6 +275,7 @@ _re_whitespace = re.compile(r'\s\s+') # an integer. _re_subst = re.compile('%(%|[0-9]+)') + class Template: _printers = { @@ -572,6 +573,7 @@ class Template: self._execute(section, valfp, ctx) ctx.defines[name] = valfp.getvalue() + def boolean(value): "Return a value suitable for [if-any bool_var] usage in a template." if value: @@ -627,6 +629,7 @@ def _prepare_ref(refname, for_names, file_args): return refname, start, rest + def _get_value(value_ref, ctx): """(refname, start, rest) -> a prepared `value reference' (see above). ctx -> an execution context instance. @@ -673,6 +676,7 @@ def _get_value(value_ref, ctx): # string or a sequence return ob + def _get_value_fallback(value_ref, ctx): try: return _get_value(value_ref, ctx) @@ -680,6 +684,7 @@ def _get_value_fallback(value_ref, ctx): (refname, start, rest) = value_ref return '[' + refname + ']' + def _write_value(valrefs, fp, ctx, format=lambda s: s): try: value = _get_value(valrefs[0], ctx) @@ -738,6 +743,7 @@ class _context: class Reader: "Abstract class which allows EZT to detect Reader objects." + class _FileReader(Reader): """Reads templates from the filesystem.""" def __init__(self, fname): @@ -746,6 +752,7 @@ class _FileReader(Reader): def read_other(self, relative): return _FileReader(os.path.join(self._dir, relative)) + class _TextReader(Reader): """'Reads' a template from provided text.""" def __init__(self, text): @@ -770,29 +777,38 @@ class EZTException(Exception): s += ' at line %d column %d' % (self.line + 1, self.column + 1) return s + class ArgCountSyntaxError(EZTException): """A bracket directive got the wrong number of arguments.""" + class UnknownReference(EZTException): """The template references an object not contained in the data dictionary.""" + class NeedSequenceError(EZTException): """The object dereferenced by the template is no sequence (tuple or list).""" + class UnclosedBlocksError(EZTException): """This error may be simply a missing [end].""" + class UnmatchedEndError(EZTException): """This error may be caused by a misspelled if directive.""" + class UnmatchedElseError(EZTException): """This error may be caused by a misspelled if directive.""" + class BaseUnavailableError(EZTException): """Base location is unavailable, which disables includes.""" + class BadFormatConstantError(EZTException): """Format specifiers must be string constants.""" + class UnknownFormatConstantError(EZTException): """The format specifier is an unknown value.""" diff --git a/wcs/qommon/form.py b/wcs/qommon/form.py index 0ffdb0f67..3735cc476 100644 --- a/wcs/qommon/form.py +++ b/wcs/qommon/form.py @@ -87,18 +87,21 @@ QuixoteForm = Form Widget.REQUIRED_ERROR = N_('required field') get_error_orig = Widget.get_error + def get_i18n_error(self, request=None): error = get_error_orig(self, request) if error == Widget.REQUIRED_ERROR: return _(error) return error + def is_prefilled(self): if hasattr(self, 'prefilled'): return self.prefilled else: return False + def render_title(self, title): if title: if self.required: @@ -109,6 +112,7 @@ def render_title(self, title): else: return '' + def get_template_names(widget): template_names = [] widget_template_name = getattr(widget, 'template_name', None) @@ -127,6 +131,7 @@ def get_template_names(widget): template_names.append('qommon/forms/widget.html') return template_names + def render(self): # quixote/form/widget.py, Widget::render def safe(text): @@ -151,6 +156,7 @@ Widget.cleanup = None Widget.render_title = render_title Widget.is_prefilled = is_prefilled + def string_render_content(self): attrs = {'id': 'form_' + self.name} if self.required: @@ -219,6 +225,7 @@ class RadiobuttonsWidget(quixote.form.RadiobuttonsWidget): 'selected': self.is_selected(object) } + def checkbox_render_content(self, standalone=True): attrs = {'id': 'form_' + self.name} if self.required: @@ -540,6 +547,7 @@ class TextWidget(quixote.form.TextWidget): except ValueError as e: self.error = str(e) + class CheckboxWidget(quixote.form.CheckboxWidget): ''' Widget just like CheckboxWidget but with an effective support for the @@ -558,6 +566,7 @@ class CheckboxWidget(quixote.form.CheckboxWidget): else: self.value = True + class UploadedFile: #pylint: disable=C1001 def __init__(self, directory, filename, upload): self.directory = directory @@ -1225,6 +1234,7 @@ class CheckboxesWidget(CompositeWidget): r += htmltext('') return r.getvalue() + class ValidatedStringWidget(StringWidget): '''StringWidget which checks the value entered is correct according to a regex''' regex = None @@ -1241,6 +1251,7 @@ class ValidatedStringWidget(StringWidget): if not match or not match.group() == self.value: self.error = _('wrong format') + class UrlWidget(ValidatedStringWidget): '''StringWidget which checks the value entered is a correct url starting with http or https''' regex = r'^https?://.+' @@ -1350,6 +1361,7 @@ class CaptchaWidget(CompositeWidget): r += widget.render_content() return r.getvalue() + class WidgetList(quixote.form.widget.WidgetList): def __init__(self, name, value=None, element_type=StringWidget, @@ -1382,6 +1394,7 @@ class WidgetList(quixote.form.widget.WidgetList): r += add_element_widget.render() return r.getvalue() + class WidgetDict(quixote.form.widget.WidgetDict): # Fix the title and hint setting # FIXME: to be fixed in Quixote upstream : title and hint parameters should be removed @@ -1438,6 +1451,7 @@ class WidgetDict(quixote.form.widget.WidgetDict): r += self.get_widget('added_elements').render() return r.getvalue() + class TagsWidget(StringWidget): def __init__(self, name, value = None, known_tags = None, **kwargs): StringWidget.__init__(self, name, value, **kwargs) @@ -1476,6 +1490,7 @@ $("#%s").autocompleteArray([ ''' % (id, known_tags)) return r.getvalue() + class WysiwygTextWidget(TextWidget): def _parse(self, request): TextWidget._parse(self, request) @@ -1702,6 +1717,7 @@ class TableRowWidget(CompositeWidget): for i, column in enumerate(self.columns): self.add(StringWidget, name='col%s'%i, title=column, **kwargs) + class TableListRowsWidget(WidgetListAsTable): readonly = False @@ -1788,6 +1804,7 @@ class TableListRowsWidget(WidgetListAsTable): except IndexError: pass + class RankedItemsWidget(CompositeWidget): readonly = False @@ -1929,6 +1946,7 @@ class JsonpSingleSelectWidget(Widget): return Widget.parse(self, request=request) + class AutocompleteStringWidget(WcsExtraStringWidget): url = None diff --git a/wcs/qommon/http_request.py b/wcs/qommon/http_request.py index d016778ff..69b37c94c 100644 --- a/wcs/qommon/http_request.py +++ b/wcs/qommon/http_request.py @@ -27,6 +27,7 @@ from quixote.errors import RequestError from .http_response import HTTPResponse + class HTTPRequest(quixote.http_request.HTTPRequest): signed = False parsed = False diff --git a/wcs/qommon/http_response.py b/wcs/qommon/http_response.py index aae865785..85934c761 100644 --- a/wcs/qommon/http_response.py +++ b/wcs/qommon/http_response.py @@ -27,6 +27,7 @@ from quixote import get_publisher, get_request from .storage import StorableObject + class AfterJob(StorableObject): _names = 'afterjobs' diff --git a/wcs/qommon/humantime.py b/wcs/qommon/humantime.py index cb3b3201b..e29a98571 100644 --- a/wcs/qommon/humantime.py +++ b/wcs/qommon/humantime.py @@ -24,6 +24,7 @@ _day = _hour * 24 _month = _day * 31 _year = int(_day * 365.25) + def list2human(stringlist): '''Transform a string list to human enumeration''' beginning = stringlist[:-1] @@ -38,6 +39,7 @@ _humandurations = (( (N_("day"), N_("days")), _day), ( (N_("minute"), N_("minutes")), _minute), ( (N_("second"), N_("seconds")), 1),) + def timewords(): '''List of words one can use to specify durations''' result = [] @@ -46,6 +48,7 @@ def timewords(): result.append(_(word)) return result + def humanduration2seconds(humanduration): if not humanduration: raise ValueError() @@ -59,6 +62,7 @@ def humanduration2seconds(humanduration): break return seconds + def seconds2humanduration(seconds): '''Convert a time range in seconds to a human string representation ''' diff --git a/wcs/qommon/ident/__init__.py b/wcs/qommon/ident/__init__.py index ec1fddcc7..2ce8174c7 100644 --- a/wcs/qommon/ident/__init__.py +++ b/wcs/qommon/ident/__init__.py @@ -18,30 +18,36 @@ from quixote import get_publisher from . import base + def login(method): m = get_publisher().ident_methods.get(method) if m and hasattr(m, 'login'): return m().login() return get_method_directory(method).login() + def register(method): return get_method_directory(method).register() + def get_method_directory(method): m = get_publisher().ident_methods.get(method) if not m: raise KeyError return m.method_directory() + def get_method_admin_directory(method): return get_publisher().ident_methods.get(method).method_admin_directory() + def get_method_user_directory(method, user): try: return get_publisher().ident_methods.get(method).method_user_directory(user) except (AttributeError, NotImplementedError, base.NoSuchMethodForUserError): return None + def get_method_classes(): return get_publisher().ident_methods.values() diff --git a/wcs/qommon/ident/base.py b/wcs/qommon/ident/base.py index ced2cde8a..46d2afc14 100644 --- a/wcs/qommon/ident/base.py +++ b/wcs/qommon/ident/base.py @@ -14,6 +14,7 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, see . + class AuthMethod(object): method_admin_widget = None diff --git a/wcs/qommon/ident/idp.py b/wcs/qommon/ident/idp.py index 6f7f3ce04..bb11a99fc 100644 --- a/wcs/qommon/ident/idp.py +++ b/wcs/qommon/ident/idp.py @@ -46,18 +46,22 @@ from .. import saml2utils ADMIN_TITLE = N_('SAML2') + def is_idp_managing_user_attributes(): return get_cfg('sp', {}).get('idp-manage-user-attributes', False) + def is_idp_managing_user_roles(): return get_cfg('sp', {}).get('idp-manage-roles', False) + def get_file_content(filename): try: return open(filename,'r').read() except: return None + def get_text_file_preview(filename): '''Return a preformatted HTML blocks displaying content of filename, or None if filename is not accessible diff --git a/wcs/qommon/ident/password.py b/wcs/qommon/ident/password.py index b7039b8ca..e336eb81f 100644 --- a/wcs/qommon/ident/password.py +++ b/wcs/qommon/ident/password.py @@ -676,6 +676,7 @@ class MethodDirectory(Directory): ADMIN_TITLE = N_('Username / Password') + class MethodAdminDirectory(Directory): title = ADMIN_TITLE label = N_('Configure username/password identification method') @@ -1122,6 +1123,7 @@ class MethodUserDirectory(Directory): emails.custom_template_email(email_key, data, self.user.email) + class PasswordAuthMethod(AuthMethod): key = 'password' description = _('Username / password') diff --git a/wcs/qommon/logger.py b/wcs/qommon/logger.py index 8c9978a55..9f41aeaef 100644 --- a/wcs/qommon/logger.py +++ b/wcs/qommon/logger.py @@ -122,6 +122,7 @@ def parse_logstream(stream): r['message'] = r['message'].strip() yield r + def readline(line): if not line: return None diff --git a/wcs/qommon/management/commands/collectstatic.py b/wcs/qommon/management/commands/collectstatic.py index c2c0e822e..7d0c64602 100644 --- a/wcs/qommon/management/commands/collectstatic.py +++ b/wcs/qommon/management/commands/collectstatic.py @@ -20,6 +20,7 @@ import shutil from django.core.management.base import BaseCommand from wcs.qommon.publisher import get_publisher_class + class Command(BaseCommand): help = "Collect static files in a single location." diff --git a/wcs/qommon/management/commands/makemessages.py b/wcs/qommon/management/commands/makemessages.py index 4560d92c9..a3ae21147 100644 --- a/wcs/qommon/management/commands/makemessages.py +++ b/wcs/qommon/management/commands/makemessages.py @@ -16,5 +16,6 @@ from django.core.management.commands import makemessages + class Command(makemessages.Command): xgettext_options = makemessages.Command.xgettext_options + ['--keyword=N_', '--keyword=C_'] diff --git a/wcs/qommon/management/commands/migrate.py b/wcs/qommon/management/commands/migrate.py index 0fb02da75..269335fed 100644 --- a/wcs/qommon/management/commands/migrate.py +++ b/wcs/qommon/management/commands/migrate.py @@ -20,6 +20,7 @@ import quixote from django.core.management.base import BaseCommand from wcs.qommon.publisher import get_publisher_class + class Command(BaseCommand): help = 'Migrate databases' diff --git a/wcs/qommon/management/commands/migrate_schemas.py b/wcs/qommon/management/commands/migrate_schemas.py index caa29e7ef..ed380aeeb 100644 --- a/wcs/qommon/management/commands/migrate_schemas.py +++ b/wcs/qommon/management/commands/migrate_schemas.py @@ -17,5 +17,6 @@ from .migrate import Command as MigrateCommand + class Command(MigrateCommand): help = 'Migrate databases (alias)' diff --git a/wcs/qommon/misc.py b/wcs/qommon/misc.py index efe01f592..9397581a2 100644 --- a/wcs/qommon/misc.py +++ b/wcs/qommon/misc.py @@ -63,6 +63,7 @@ except subprocess.CalledProcessError: EXIF_ORIENTATION = 0x0112 + class ThumbnailError(Exception): pass @@ -74,6 +75,7 @@ def get_abs_path(s): return s return os.path.join(get_publisher().app_dir, s) + def get_lasso_server(): if not get_cfg('sp'): return None @@ -117,6 +119,7 @@ def get_lasso_server(): return server + def get_provider_label(provider): if not provider: return None @@ -134,6 +137,7 @@ def get_provider_label(provider): return provider.providerId return htmltext(name[0].decode('utf8').encode(get_publisher().site_charset)) + def get_provider(provider_key): lp = get_cfg('idp', {}).get(provider_key) if not lp: @@ -154,9 +158,11 @@ def get_provider(provider_key): return provider + def get_provider_key(provider_id): return provider_id.replace('://', '-').replace('/', '-').replace('?', '-').replace(':', '-') + def simplify(s, space='-'): if s is None: return '' @@ -172,6 +178,7 @@ def simplify(s, space='-'): return s.encode('ascii') return s + def get_datetime_language(): lang = get_cfg('language', {}).get('language', None) if lang is None: @@ -181,6 +188,7 @@ def get_datetime_language(): lang = os.environ.get('LC_ALL')[:2] return lang + def strftime(fmt, dt): if not dt: return '' @@ -196,6 +204,7 @@ def strftime(fmt, dt): dt = dt.replace() return datetime_safe.strftime(dt, fmt) + def localstrftime(t): if not t: return '' @@ -213,18 +222,21 @@ DATETIME_FORMATS = { '%d/%m/%y %H:%M', '%d/%m/%y %H:%M:%S', '%d/%m/%y %Hh%M'], } + def datetime_format(): lang = get_datetime_language() if lang not in DATETIME_FORMATS: lang = 'C' return DATETIME_FORMATS[lang][0] + def date_format(): lang = get_datetime_language() if lang not in DATE_FORMATS: lang = 'C' return DATE_FORMATS[lang][0] + def get_as_datetime(s): formats = [datetime_format(), date_format()] # prefer current locale for value in DATETIME_FORMATS.values(): @@ -238,6 +250,7 @@ def get_as_datetime(s): pass raise ValueError() + def site_encode(s): if s is None: return None @@ -247,18 +260,21 @@ def site_encode(s): s = force_text(s) return s.encode(get_publisher().site_charset) + def ellipsize(s, length=30): s = force_text(s, get_publisher().site_charset, errors='replace') if s and len(s) >= length: s = s[:length-5] + ' (...)' return force_str(s) + def get_month_name(month): month_names = [_('January'), _('February'), _('March'), _('April'), _('May'), _('June'), _('July'), _('August'), _('September'), _('October'), _('November'), _('December') ] return month_names[month-1] + def format_time(datetime, formatstring, gmtime = False): if not datetime: return '?' @@ -290,6 +306,7 @@ def format_time(datetime, formatstring, gmtime = False): return formatstring % locals() + def _http_request(url, method='GET', body=None, headers={}, cert_file=None, timeout=None, raise_on_http_errors=False): get_publisher().reload_cfg() @@ -318,6 +335,7 @@ def _http_request(url, method='GET', body=None, headers={}, cert_file=None, time return response, status, data, auth_header + def urlopen(url, data=None): response, status, data, auth_header = _http_request( url, 'GET' if data is None else 'POST', @@ -329,15 +347,19 @@ def urlopen(url, data=None): def http_get_page(url, **kwargs): return _http_request(url, **kwargs) + def http_patch_request(url, body=None, **kwargs): return _http_request(url, 'PATCH', body, **kwargs) + def http_post_request(url, body=None, **kwargs): return _http_request(url, 'POST', body, **kwargs) + def http_delete_request(url, **kwargs): return _http_request(url, 'DELETE', **kwargs) + def get_variadic_url(url, variables, encode_query=True): if not Template.is_template_string(url): return url @@ -452,10 +474,12 @@ def get_foreground_colour(background_colour): fg_colour = 'white' return fg_colour + def C_(msg): '''Translates and removes context from message''' return _(msg).split('|', 1)[1] + def indent_xml(elem, level=0): # in-place prettyprint formatter # http://effbot.org/zone/element-lib.htm#prettyprint @@ -521,6 +545,7 @@ class JSONEncoder(json.JSONEncoder): # Let the base class default method raise the TypeError return json.JSONEncoder.default(self, obj) + def json_encode_helper(d, charset): '''Encode a JSON structure into local charset''' if six.PY3: @@ -538,12 +563,14 @@ def json_encode_helper(d, charset): else: return d + def json_loads(value, charset=None): if six.PY3: return json.loads(force_text(value)) charset = (get_publisher() and get_publisher().site_charset) or 'utf-8' return json_encode_helper(json.loads(value), charset) + def json_response(data): get_response().set_content_type('application/json') if get_request().get_environ('HTTP_ORIGIN'): @@ -559,6 +586,7 @@ def json_response(data): break return json_str + def parse_isotime(s): s = s.replace('+00:00Z', 'Z') # clean lemonldap dates with both timezone and Z t = time.strptime(s, '%Y-%m-%dT%H:%M:%SZ') @@ -643,6 +671,7 @@ def normalize_geolocation(lat_lon): lon = wrap(lon, decimal.Decimal('-180.0'), decimal.Decimal('180.0')) return {'lat': float(lat), 'lon': float(lon)} + def html2text(text): if isinstance(text, (htmltext, str)): text = force_text(str(text), get_publisher().site_charset) diff --git a/wcs/qommon/pages.py b/wcs/qommon/pages.py index c5a6cf4b7..b13ccfea3 100644 --- a/wcs/qommon/pages.py +++ b/wcs/qommon/pages.py @@ -23,6 +23,7 @@ from quixote.util import StaticDirectory from . import errors from . import template + class PagesDirectory(Directory): _q_exports = [''] diff --git a/wcs/qommon/publisher.py b/wcs/qommon/publisher.py index ff746278d..5e529fa16 100644 --- a/wcs/qommon/publisher.py +++ b/wcs/qommon/publisher.py @@ -66,10 +66,12 @@ import logging.handlers from . import logger from . import storage + class ImmediateRedirectException(Exception): def __init__(self, location): self.location = location + class QommonPublisher(Publisher, object): APP_NAME = None APP_DIR = None @@ -975,6 +977,7 @@ def get_cfg(key, default = None): return {} return r + def get_logger(): return get_publisher().get_app_logger() @@ -982,6 +985,7 @@ def get_logger(): def set_publisher_class(klass): builtins.__dict__['__publisher_class'] = klass + def get_publisher_class(): return builtins.__dict__.get('__publisher_class') diff --git a/wcs/qommon/saml2.py b/wcs/qommon/saml2.py index c0ca818c5..6c9a45f73 100644 --- a/wcs/qommon/saml2.py +++ b/wcs/qommon/saml2.py @@ -108,6 +108,7 @@ def save_identity(profile): else: user.store() + def load_identity(profile): request = get_request() session = get_session() @@ -117,6 +118,7 @@ def load_identity(profile): if session and session.lasso_anonymous_identity_dump: profile.setIdentityFromDump(session.lasso_anonymous_identity_dump) + def saml2_status_summary(response): if not response.status or not response.status.statusCode: return 'No status or status code' @@ -125,11 +127,13 @@ def saml2_status_summary(response): code += ':' + response.status.statusCode.statusCode.value return code + def get_remote_provider_cfg(profile): '''Lookup the configuration for a remote provider given a profile''' remote_provider_key = misc.get_provider_key(profile.remoteProviderId) return get_cfg('idp', {}).get(remote_provider_key) + class Saml2Directory(Directory): _q_exports = ['login', 'singleSignOnArtifact', 'singleSignOnPost', 'singleSignOnSOAP', 'singleSignOnRedirect', diff --git a/wcs/qommon/saml2utils.py b/wcs/qommon/saml2utils.py index 2e36426fb..4151e79c1 100644 --- a/wcs/qommon/saml2utils.py +++ b/wcs/qommon/saml2utils.py @@ -18,6 +18,7 @@ import os from . import force_str, x509utils + def bool2xs(boolean): '''Convert a boolean value to XSchema boolean representation''' if boolean is True: diff --git a/wcs/qommon/sessions.py b/wcs/qommon/sessions.py index 9846ecffe..3889a6f81 100644 --- a/wcs/qommon/sessions.py +++ b/wcs/qommon/sessions.py @@ -41,6 +41,7 @@ class QommonSession(QuixoteSession): # add support for X_FORWARDED_FOR self.__remote_address = env.get('X_FORWARDED_FOR', env.get('REMOTE_ADDR')) + class CaptchaSession(object): MAX_CAPTCHA_TOKENS = 8 _captcha_tokens = None @@ -72,6 +73,7 @@ class CaptchaSession(object): def has_info(self): return self.won_captcha or self._captcha_tokens + class Session(QommonSession, CaptchaSession, StorableObject): _names = 'sessions' @@ -373,6 +375,7 @@ class QommonSessionManager(QuixoteSessionManager): QuixoteSessionManager.start_request(self) session = get_session() + class StorageSessionManager(QommonSessionManager): def forget_changes(self, session): diff --git a/wcs/qommon/sms.py b/wcs/qommon/sms.py index ba33ab10e..f052f9bd4 100644 --- a/wcs/qommon/sms.py +++ b/wcs/qommon/sms.py @@ -197,6 +197,7 @@ class ChoositSMS(object): def get_money_left(self): raise NotImplementedError + class PasserelleSMS(object): """This class allows to send a SMS using Passerelle """ @@ -232,6 +233,7 @@ class PasserelleSMS(object): def get_money_left(self): raise NotImplementedError + class SMS(object): providers = { 'mobyt': (N_('Mobyt provider'), MobytSMS), diff --git a/wcs/qommon/storage.py b/wcs/qommon/storage.py index bf9404104..4c8ce59a6 100644 --- a/wcs/qommon/storage.py +++ b/wcs/qommon/storage.py @@ -50,6 +50,7 @@ def cache_umask(): # cache umask when loading up the module cache_umask() + def _take(objects, limit, offset=0): for y in objects: if offset: @@ -63,17 +64,20 @@ def _take(objects, limit, offset=0): pass yield y + def lax_int(s): try: return int(s) except ValueError: return -1 + def fix_key(k): # insure key can be inserted in filesystem if not k: return k return str(k).replace('/', '-') + def atomic_write(path, content, async_op=False): '''Rewrite a complete file automatically, that is write to new file with temporary name, fsync, then rename to final name. Use threads to remove blocking.''' @@ -158,33 +162,41 @@ class Criteria(object): class Less(Criteria): op = operator.lt + class Greater(Criteria): op = operator.gt + class Equal(Criteria): op = operator.eq + class NotEqual(Criteria): op = operator.ne + class LessOrEqual(Criteria): op = operator.le + class GreaterOrEqual(Criteria): op = operator.ge + class Contains(Criteria): op = operator.contains def build_lambda(self): return lambda x: self.op(self.value, getattr(x, self.attribute, '')) + class NotContains(Criteria): op = operator.contains def build_lambda(self): return lambda x: not self.op(self.value, getattr(x, self.attribute, '')) + class Intersects(Criteria): def build_lambda(self): value = set(self.value) @@ -197,6 +209,7 @@ class Intersects(Criteria): return False return func + class Or(Criteria): def __init__(self, criterias, **kwargs): self.criterias = criterias @@ -209,6 +222,7 @@ class Or(Criteria): func = combine_callables(func, element.build_lambda()) return func + class And(Criteria): def __init__(self, criterias, **kwargs): self.criterias = criterias @@ -221,10 +235,12 @@ class And(Criteria): func = combine_callables(func, element.build_lambda()) return func + class ILike(Criteria): def build_lambda(self): return lambda x: self.value.lower() in (getattr(x, self.attribute, '') or '').lower() + class FtsMatch(Criteria): def __init__(self, value): self.value = value @@ -232,6 +248,7 @@ class FtsMatch(Criteria): def build_lambda(self): raise NotImplementedError() + class NotNull(Criteria): def __init__(self, attribute): self.attribute = attribute @@ -239,6 +256,7 @@ class NotNull(Criteria): def build_lambda(self): return lambda x: getattr(x, self.attribute, None) is not None + class Null(Criteria): def __init__(self, attribute): self.attribute = attribute @@ -269,6 +287,7 @@ def parse_clause(clause): class StorageIndexException(Exception): pass + class StorableObject(object): _indexes = None _hashed_indexes = None diff --git a/wcs/qommon/template.py b/wcs/qommon/template.py index 2232cebb9..7be9a444e 100644 --- a/wcs/qommon/template.py +++ b/wcs/qommon/template.py @@ -79,6 +79,7 @@ class ThemesDirectory(Directory): else: return StaticFile(location) + def get_themes_dict(): system_location = os.path.join(get_publisher().data_dir, 'themes') local_location = os.path.join(get_publisher().app_dir, 'themes') @@ -92,6 +93,7 @@ def get_themes_dict(): themes[theme_dict.get('name')] = theme_dict return themes + def get_theme_dict(theme_xml): try: tree = ET.parse(theme_xml).getroot() @@ -115,6 +117,7 @@ def get_theme_dict(theme_xml): theme['keywords'].append(keyword.text) return theme + def get_themes(): # backward compatibility function, it returns a tuple with theme info, # newer code should use get_themes_dict() @@ -123,6 +126,7 @@ def get_themes(): themes[k] = (v['label'], v['desc'], v['author'], v['icon']) return themes + def get_current_theme(): from .publisher import get_cfg current_theme = get_cfg('branding', {}).get('theme', 'default') @@ -181,6 +185,7 @@ def html_top(title=None, default_org=None): get_response().filter['title'] = title get_response().filter['default_org'] = default_org + def error_page(error_message, error_title = None, exception = None, continue_to = None, location_hint = None): from . import _ @@ -215,6 +220,7 @@ def error_page(error_message, error_title = None, exception = None, continue_to r += htmltext('
') return htmltext(r.getvalue()) + def get_default_ezt_template(): from .publisher import get_cfg current_theme = get_cfg('branding', {}).get('theme', 'default') @@ -522,6 +528,7 @@ class Template(object): # strings to unicode variable_resolve_orig = django.template.base.Variable.resolve + def variable_resolve(self, context): try: value = variable_resolve_orig(self, context) diff --git a/wcs/qommon/templatetags/qommon.py b/wcs/qommon/templatetags/qommon.py index 58a3aa383..d64ab63b2 100644 --- a/wcs/qommon/templatetags/qommon.py +++ b/wcs/qommon/templatetags/qommon.py @@ -39,22 +39,26 @@ from wcs.roles import Role register = template.Library() + @register.filter def get(mapping, key): if hasattr(mapping, 'get'): return mapping.get(key) return mapping[key] + @register.filter def startswith(string, substring): return string and force_text(string).startswith(force_text(substring)) + @register.filter def split(string, separator=' '): if not string: return [] return force_text(string).split(force_text(separator)) + @register.filter def strip(string, chars=None): if not string: @@ -64,6 +68,7 @@ def strip(string, chars=None): else: return force_text(string).strip() + @register.filter def parse_date(date_string): try: @@ -76,6 +81,7 @@ def parse_date(date_string): except (ValueError, TypeError): return None + @register.filter(expects_localtime=True, is_safe=False) def date(value, arg=None): if arg is None: @@ -88,6 +94,7 @@ def date(value, arg=None): value = parse_datetime(value) or parse_date(value) return defaultfilters.date(value, arg=arg) + @register.filter def parse_datetime(datetime_string): try: @@ -100,6 +107,7 @@ def parse_datetime(datetime_string): except (ValueError, TypeError): return None + @register.filter(name='datetime', expects_localtime=True, is_safe=False) def datetime_(value, arg=None): if arg is None: @@ -112,6 +120,7 @@ def datetime_(value, arg=None): value = parse_datetime(value) return defaultfilters.date(value, arg=arg) + @register.filter def parse_time(time_string): # if input is a datetime, extract its time @@ -127,6 +136,7 @@ def parse_time(time_string): except (ValueError, TypeError): return None + @register.filter(expects_localtime=True, is_safe=False) def time(value, arg=None): if arg is None: @@ -136,6 +146,7 @@ def time(value, arg=None): value = parse_time(value) return defaultfilters.date(value, arg=arg) + def parse_decimal(value): if hasattr(value, 'get_value'): value = value.get_value() # unlazy @@ -147,6 +158,7 @@ def parse_decimal(value): except (ArithmeticError, TypeError): return Decimal(0) + @register.filter(is_safe=False) def decimal(value, arg=None): if not isinstance(value, Decimal): @@ -157,6 +169,7 @@ def decimal(value, arg=None): arg = arg.get_value() # unlazy return defaultfilters.floatformat(value, arg=arg) + @register.filter(expects_localtime=True, is_safe=False) def add_days(value, arg): if hasattr(value, 'timetuple'): @@ -174,6 +187,7 @@ def add_days(value, arg): result = result.date() return lazy_date(result) + @register.filter(expects_localtime=True, is_safe=False) def add_hours(value, arg): if hasattr(value, 'timetuple'): @@ -188,6 +202,7 @@ def add_hours(value, arg): return lazy_date(value) return lazy_date(value + datetime.timedelta(hours=float(arg))) + @register.filter(expects_localtime=True, is_safe=False) def age_in_days(value, now=None): try: @@ -195,6 +210,7 @@ def age_in_days(value, now=None): except ValueError: return '' + @register.filter(expects_localtime=True, is_safe=False) def age_in_hours(value, now=None): # consider value and now as datetimes (and not dates) @@ -214,6 +230,7 @@ def age_in_hours(value, now=None): now = datetime.datetime.now() return int((now - value).total_seconds() / 3600) + @register.filter(expects_localtime=True, is_safe=False) def age_in_years(value, today=None): try: @@ -221,6 +238,7 @@ def age_in_years(value, today=None): except ValueError: return '' + @register.filter(expects_localtime=True, is_safe=False) def age_in_months(value, today=None): try: @@ -229,10 +247,12 @@ def age_in_months(value, today=None): return '' return years*12 + months + @register.simple_tag def standard_text(text_id): return mark_safe(TextsDirectory.get_html_text(str(text_id))) + @register.simple_tag(takes_context=True) def action_button(context, action_id, label, delay=3): from wcs.formdef import FormDef @@ -253,19 +273,23 @@ def action_button(context, action_id, label, delay=3): token.store() return '---===BUTTON:%s:%s===---' % (token.id, label) + @register.filter def add(term1, term2): '''replace the "add" native django filter''' return parse_decimal(term1) + parse_decimal(term2) + @register.filter def subtract(term1, term2): return parse_decimal(term1) - parse_decimal(term2) + @register.filter def multiply(term1, term2): return parse_decimal(term1) * parse_decimal(term2) + @register.filter def divide(term1, term2): try: @@ -275,19 +299,23 @@ def divide(term1, term2): except DecimalDivisionByZero: return '' + @register.filter def ceil(value): '''the smallest integer value greater than or equal to value''' return decimal(math.ceil(parse_decimal(value))) + @register.filter def floor(value): return decimal(math.floor(parse_decimal(value))) + @register.filter(name='abs') def abs_(value): return decimal(abs(parse_decimal(value))) + @register.simple_tag def version_hash(): from wcs.qommon.admin.menu import get_vc_version @@ -335,6 +363,7 @@ def get_latlon(obj): except ValueError: return None, None + @register.filter def distance(obj1, obj2): lat1, lon1 = get_latlon(obj1) @@ -347,20 +376,24 @@ def distance(obj1, obj2): distance = geod.inv(lon1, lat1, lon2, lat2)[2] return distance + @register.filter def distance_filter(queryset, distance=1000): return queryset.distance_filter(distance=int(distance)) + @register.filter def order_by(queryset, attribute): return queryset.order_by(attribute) + @register.filter def reproj(coords, projection_name): proj = pyproj.Proj(init='EPSG:4326') target_proj = pyproj.Proj(init=projection_name) return pyproj.transform(proj, target_proj, coords['lon'], coords['lat']) + @register.filter def has_role(user, role_name): if not callable(getattr(user, 'get_roles', None)): diff --git a/wcs/qommon/tokens.py b/wcs/qommon/tokens.py index 64514ca09..209ec1464 100644 --- a/wcs/qommon/tokens.py +++ b/wcs/qommon/tokens.py @@ -20,6 +20,7 @@ import string from .storage import StorableObject import time + class Token(StorableObject): _names = 'tokens' diff --git a/wcs/qommon/vendor/locket.py b/wcs/qommon/vendor/locket.py index d70495c7c..ecf7bace3 100644 --- a/wcs/qommon/vendor/locket.py +++ b/wcs/qommon/vendor/locket.py @@ -40,9 +40,11 @@ __all__ = ["lock_file"] import fcntl + def _lock_file_blocking(file_): fcntl.flock(file_.fileno(), fcntl.LOCK_EX) + def _lock_file_non_blocking(file_): try: fcntl.flock(file_.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) @@ -53,6 +55,7 @@ def _lock_file_non_blocking(file_): else: raise + def _unlock_file(file_): fcntl.flock(file_.fileno(), fcntl.LOCK_UN) diff --git a/wcs/qommon/x509utils.py b/wcs/qommon/x509utils.py index fae2025a1..40594909c 100644 --- a/wcs/qommon/x509utils.py +++ b/wcs/qommon/x509utils.py @@ -26,6 +26,7 @@ from django.utils.encoding import force_text _openssl = 'openssl' + def decapsulate_pem_file(file_or_string): '''Remove PEM header lines''' if not isinstance(file_or_string, six.string_types): @@ -38,6 +39,7 @@ def decapsulate_pem_file(file_or_string): l = content.rfind('\n', 0, k) return content[j+1:l] + def _call_openssl(args): '''Use subprocees to spawn an openssl process @@ -61,6 +63,7 @@ def _protect_file(fd,filepath): else: # handle python <2.6 os.chmod(filepath, stat.S_IRUSR | stat.S_IWUSR) + def check_key_pair_consistency(publickey=None,privatekey=None): '''Check if two PEM key pair whether they are publickey or certificate, are well formed and related. @@ -96,6 +99,7 @@ def check_key_pair_consistency(publickey=None,privatekey=None): os.unlink(publickey_fn) return None + def generate_rsa_keypair(numbits=1024): '''Generate simple RSA public and private key files ''' @@ -113,6 +117,7 @@ def generate_rsa_keypair(numbits=1024): os.unlink(privatekey_fn) os.unlink(publickey_fn) + def get_rsa_public_key_modulus(publickey): try: publickey_file_fd, publickey_fn = tempfile.mkstemp() @@ -132,6 +137,7 @@ def get_rsa_public_key_modulus(publickey): os.unlink(publickey_fn) return None + def get_rsa_public_key_exponent(publickey): try: publickey_file_fd, publickey_fn = tempfile.mkstemp() @@ -154,6 +160,7 @@ def get_rsa_public_key_exponent(publickey): os.unlink(publickey_fn) return None + def can_generate_rsa_key_pair(): syspath = os.environ.get('PATH') if syspath: @@ -163,6 +170,7 @@ def can_generate_rsa_key_pair(): else: return False + def get_xmldsig_rsa_key_value(publickey): def int_to_bin(i): h = hex(i)[2:].strip('L') diff --git a/wcs/qommon/xml_storage.py b/wcs/qommon/xml_storage.py index 6c8afcdb8..e21210580 100644 --- a/wcs/qommon/xml_storage.py +++ b/wcs/qommon/xml_storage.py @@ -24,6 +24,7 @@ from . import force_str from .misc import indent_xml from .storage import StorableObject + class XmlStorableObject(StorableObject): XML_NODES = [] diff --git a/wcs/roles.py b/wcs/roles.py index 8b649ed4a..ae8c584e2 100644 --- a/wcs/roles.py +++ b/wcs/roles.py @@ -20,6 +20,7 @@ from quixote import get_publisher from .qommon import _, misc from .qommon.storage import StorableObject + class Role(StorableObject): _names = 'roles' _indexes = ['uuid', 'slug'] diff --git a/wcs/root.py b/wcs/root.py index 742106be5..eba77aea7 100644 --- a/wcs/root.py +++ b/wcs/root.py @@ -48,6 +48,7 @@ from .forms.actions import ActionsDirectory from . import portfolio + class CompatibilityDirectory(Directory): _q_exports = [''] diff --git a/wcs/scripts.py b/wcs/scripts.py index c11b6ae08..ee5237081 100644 --- a/wcs/scripts.py +++ b/wcs/scripts.py @@ -19,6 +19,7 @@ import os from quixote import get_publisher + class Script(object): ezt_call_mode = 'simple' diff --git a/wcs/sessions.py b/wcs/sessions.py index 4b32c0737..cc1b9a9ee 100644 --- a/wcs/sessions.py +++ b/wcs/sessions.py @@ -21,6 +21,7 @@ import uuid from .qommon import sessions from .qommon.sessions import Session + class BasicSession(Session): magictokens = None diff --git a/wcs/sql.py b/wcs/sql.py index d4658d192..d0d5b8324 100644 --- a/wcs/sql.py +++ b/wcs/sql.py @@ -90,12 +90,15 @@ class Criteria(qommon.storage.Criteria): value = self.value return {'c%s' % id(self.value): value} + class Less(Criteria): sql_op = '<' + class Greater(Criteria): sql_op = '>' + class Equal(Criteria): sql_op = '=' @@ -104,15 +107,19 @@ class Equal(Criteria): return 'ARRAY_LENGTH(%s, 1) IS NULL' % self.attribute return super(Equal, self).as_sql() + class LessOrEqual(Criteria): sql_op = '<=' + class GreaterOrEqual(Criteria): sql_op = '>=' + class NotEqual(Criteria): sql_op = '!=' + class Contains(Criteria): sql_op = 'IN' @@ -124,6 +131,7 @@ class Contains(Criteria): def as_sql_param(self): return {'c%s' % id(self.value): tuple(self.value)} + class NotContains(Contains): sql_op = 'NOT IN' @@ -132,6 +140,7 @@ class NotContains(Contains): return 'TRUE' return super(NotContains, self).as_sql() + class NotNull(Criteria): sql_op = 'IS NOT NULL' @@ -144,6 +153,7 @@ class NotNull(Criteria): def as_sql_param(self): return {} + class Null(Criteria): sql_op = 'IS NULL' @@ -156,6 +166,7 @@ class Null(Criteria): def as_sql_param(self): return {} + class Or(Criteria): def __init__(self, criterias, **kwargs): self.criterias = [] @@ -173,6 +184,7 @@ class Or(Criteria): d.update(criteria.as_sql_param()) return d + class And(Criteria): def __init__(self, criterias, **kwargs): self.criterias = [] @@ -210,6 +222,7 @@ class ILike(Criteria): def as_sql(self): return '%s ILIKE %%(c%s)s' % (self.attribute, id(self.value)) + class FtsMatch(Criteria): def __init__(self, value): self.value = qommon.misc.simplify(value, space=' ') @@ -229,6 +242,7 @@ def get_name_as_sql_identifier(name): def get_field_id(field): return 'f' + str(field.id).replace('-', '_').lower() + def parse_clause(clause): # returns a three-elements tuple with: # - a list of SQL 'WHERE' clauses @@ -272,9 +286,11 @@ def str_encode(value): return value.encode(get_publisher().site_charset) return value + def site_unicode(value): return force_text(value, get_publisher().site_charset) + def get_connection(new=False): if new: cleanup_connection() @@ -297,11 +313,13 @@ def get_connection(new=False): cur.close() return get_publisher().pgconn + def cleanup_connection(): if hasattr(get_publisher(), 'pgconn') and get_publisher().pgconn is not None: get_publisher().pgconn.close() get_publisher().pgconn = None + def get_connection_and_cursor(new=False): conn = get_connection(new=new) try: @@ -312,6 +330,7 @@ def get_connection_and_cursor(new=False): cur = conn.cursor() return (conn, cur) + def get_formdef_table_name(formdef): # PostgreSQL limits identifier length to 63 bytes # @@ -332,6 +351,7 @@ def get_formdef_table_name(formdef): formdef.store() return formdef.table_name + def get_formdef_new_id(id_start): new_id = id_start conn, cur = get_connection_and_cursor() @@ -346,6 +366,7 @@ def get_formdef_new_id(id_start): cur.close() return new_id + def get_carddef_new_id(id_start): new_id = id_start conn, cur = get_connection_and_cursor() @@ -360,6 +381,7 @@ def get_carddef_new_id(id_start): cur.close() return new_id + def formdef_wipe(): conn, cur = get_connection_and_cursor() cur.execute('''SELECT table_name FROM information_schema.tables @@ -370,6 +392,7 @@ def formdef_wipe(): conn.commit() cur.close() + def carddef_wipe(): conn, cur = get_connection_and_cursor() cur.execute('''SELECT table_name FROM information_schema.tables @@ -380,6 +403,7 @@ def carddef_wipe(): conn.commit() cur.close() + def get_formdef_view_name(formdef): prefix = 'wcs_view' if formdef.data_sql_prefix != 'formdata': @@ -387,6 +411,7 @@ def get_formdef_view_name(formdef): return '%s_%s_%s' % (prefix, formdef.id, get_name_as_sql_identifier(formdef.url_name)[:40]) + def guard_postgres(func): def f(*args, **kwargs): try: @@ -396,6 +421,7 @@ def guard_postgres(func): raise return f + @guard_postgres def do_formdef_tables(formdef, conn=None, cur=None, rebuild_views=False, rebuild_global_views=True): if formdef.id is None: @@ -561,6 +587,7 @@ def do_formdef_tables(formdef, conn=None, cur=None, rebuild_views=False, rebuild return actions + def do_formdef_indexes(formdef, created, conn, cur, concurrently=False): table_name = get_formdef_table_name(formdef) evolutions_table_name = table_name + '_evolutions' @@ -587,6 +614,7 @@ def do_formdef_indexes(formdef, created, conn, cur, concurrently=False): 'table_name': table_name, 'attr': attr}) + @guard_postgres def do_user_table(): conn, cur = get_connection_and_cursor() @@ -667,6 +695,7 @@ def do_user_table(): cur.close() + def do_tracking_code_table(): conn, cur = get_connection_and_cursor() table_name = 'tracking_codes' @@ -753,6 +782,7 @@ def do_meta_table(conn=None, cur=None, insert_current_sql_level=True): conn.commit() cur.close() + @guard_postgres def redo_views(conn, cur, formdef, rebuild_global_views=False): if get_publisher().get_site_option('postgresql_views') == 'false': @@ -764,6 +794,7 @@ def redo_views(conn, cur, formdef, rebuild_global_views=False): drop_views(formdef, conn, cur) do_views(formdef, conn, cur, rebuild_global_views=rebuild_global_views) + @guard_postgres def drop_views(formdef, conn, cur): # remove the global views @@ -792,6 +823,7 @@ def drop_views(formdef, conn, cur): for view_name in view_names: cur.execute('''DROP VIEW IF EXISTS %s''' % view_name) + def get_view_fields(formdef): view_fields = [] view_fields.append(("int '%s'" % (formdef.category_id or 0), 'category_id')) @@ -905,6 +937,7 @@ def drop_global_views(conn, cur): cur.execute('''DROP VIEW IF EXISTS wcs_all_forms''') + def do_global_views(conn, cur): # recreate global views from wcs.formdef import FormDef @@ -2166,6 +2199,7 @@ def get_period_query(period_start=None, period_end=None, criterias=None, paramet statement += ' WHERE ' + ' AND '.join(where_clauses) return statement + @guard_postgres def get_actionable_counts(user_roles): conn, cur = get_connection_and_cursor() @@ -2182,6 +2216,7 @@ def get_actionable_counts(user_roles): cur.close() return counts + @guard_postgres def get_total_counts(user_roles): conn, cur = get_connection_and_cursor() @@ -2199,6 +2234,7 @@ def get_total_counts(user_roles): cur.close() return counts + @guard_postgres def get_weekday_totals(period_start=None, period_end=None, criterias=None): conn, cur = get_connection_and_cursor() @@ -2305,6 +2341,7 @@ def get_yearly_totals(period_start=None, period_end=None, criterias=None): SQL_LEVEL = 36 + def migrate_global_views(conn, cur): cur.execute('''SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public' @@ -2314,6 +2351,7 @@ def migrate_global_views(conn, cur): drop_global_views(conn, cur) do_global_views(conn, cur) + @guard_postgres def get_sql_level(conn, cur): do_meta_table(conn, cur, insert_current_sql_level=False) @@ -2321,6 +2359,7 @@ def get_sql_level(conn, cur): sql_level = int(cur.fetchone()[0]) return sql_level + @guard_postgres def is_reindex_needed(index, conn, cur): do_meta_table(conn, cur, insert_current_sql_level=False) @@ -2333,6 +2372,7 @@ def is_reindex_needed(index, conn, cur): return False return row[0] == 'needed' + @guard_postgres def set_reindex(index, value, conn=None, cur=None): own_conn = False @@ -2353,6 +2393,7 @@ def set_reindex(index, value, conn=None, cur=None): conn.commit() cur.close() + def migrate_views(conn, cur): drop_views(None, conn, cur) from wcs.formdef import FormDef @@ -2362,6 +2403,7 @@ def migrate_views(conn, cur): do_formdef_tables(formdef, conn=conn, cur=cur, rebuild_views=True, rebuild_global_views=False) migrate_global_views(conn, cur) + @guard_postgres def migrate(): conn, cur = get_connection_and_cursor() diff --git a/wcs/tracking_code.py b/wcs/tracking_code.py index 9011f956b..ee38acb68 100644 --- a/wcs/tracking_code.py +++ b/wcs/tracking_code.py @@ -20,6 +20,7 @@ from .qommon.storage import StorableObject from .formdef import FormDef + class TrackingCode(StorableObject): _names = 'tracking-codes' id = None diff --git a/wcs/users.py b/wcs/users.py index 4dd5c55cf..4e7fe6ed3 100644 --- a/wcs/users.py +++ b/wcs/users.py @@ -22,6 +22,7 @@ import wcs.qommon.storage as st from .qommon.substitution import Substitutions, invalidate_substitution_cache + class User(StorableObject): _names = 'users' diff --git a/wcs/utils.py b/wcs/utils.py index 8317a87ab..e3b0dc0ad 100644 --- a/wcs/utils.py +++ b/wcs/utils.py @@ -22,6 +22,7 @@ from django.conf import settings from .qommon.template import get_theme_directory from .qommon.publisher import get_cfg, get_publisher + class TemplateLoader(django.template.loaders.filesystem.Loader): def get_template_sources(self, template_name, template_dirs=None): if not template_dirs: diff --git a/wcs/wf/aggregation_email.py b/wcs/wf/aggregation_email.py index d70c904e7..32e8acd69 100644 --- a/wcs/wf/aggregation_email.py +++ b/wcs/wf/aggregation_email.py @@ -26,6 +26,7 @@ from wcs.workflows import WorkflowStatusItem, register_item_class, \ from wcs.roles import Role + class AggregationEmailWorkflowStatusItem(WorkflowStatusItem): description = N_('Daily Summary Email') key = 'aggregationemail' diff --git a/wcs/wf/anonymise.py b/wcs/wf/anonymise.py index 169a8c391..4c45945d9 100644 --- a/wcs/wf/anonymise.py +++ b/wcs/wf/anonymise.py @@ -16,6 +16,7 @@ from wcs.workflows import WorkflowStatusItem, register_item_class + class AnonymiseWorkflowStatusItem(WorkflowStatusItem): description = N_('Anonymisation') key = 'anonymise' diff --git a/wcs/wf/attachment.py b/wcs/wf/attachment.py index fd835859d..9f95350e7 100644 --- a/wcs/wf/attachment.py +++ b/wcs/wf/attachment.py @@ -24,6 +24,7 @@ from ..qommon.errors import * from wcs.forms.common import FormStatusPage, FileDirectory from wcs.portfolio import has_portfolio, push_document + def lookup_wf_attachment(self, filename): # supports for URLs such as /$formdata/$id/files/attachment/test.txt # and /$formdata/$id/files/attachment-$file-reference/test.txt diff --git a/wcs/wf/criticality.py b/wcs/wf/criticality.py index 55e40379d..f71b93456 100644 --- a/wcs/wf/criticality.py +++ b/wcs/wf/criticality.py @@ -24,6 +24,7 @@ MODE_INC = '1' MODE_DEC = '2' MODE_SET = '3' + class ModifyCriticalityWorkflowStatusItem(WorkflowStatusItem): description = N_('Criticality Levels') key = 'modify_criticality' diff --git a/wcs/wf/form.py b/wcs/wf/form.py index f77b31a4b..e8a4fc552 100644 --- a/wcs/wf/form.py +++ b/wcs/wf/form.py @@ -27,6 +27,7 @@ from wcs.formdata import get_dict_with_varnames from wcs.forms.common import FileDirectory + def lookup_wf_form_file(self, filename): # supports for URLs such as /$formdata/$id/files/form-$formvar-$fieldvar/test.txt try: diff --git a/wcs/wf/geolocate.py b/wcs/wf/geolocate.py index b167112cf..04ce43ada 100644 --- a/wcs/wf/geolocate.py +++ b/wcs/wf/geolocate.py @@ -33,6 +33,7 @@ from ..qommon.form import RadiobuttonsWidget, ComputedExpressionWidget, Checkbox from ..qommon.misc import http_get_page, normalize_geolocation from wcs.workflows import WorkflowStatusItem, register_item_class + class GeolocateWorkflowStatusItem(WorkflowStatusItem): description = N_('Geolocation') key = 'geolocate' diff --git a/wcs/wf/jump.py b/wcs/wf/jump.py index 37500a026..e6ad4c851 100644 --- a/wcs/wf/jump.py +++ b/wcs/wf/jump.py @@ -37,6 +37,7 @@ from wcs.conditions import Condition JUMP_TIMEOUT_INTERVAL = max((60 // int(os.environ.get('WCS_JUMP_TIMEOUT_CHECKS', '3')), 1)) + def jump_and_perform(formdata, action, workflow_data=None): action.handle_markers_stack(formdata) if workflow_data: @@ -278,6 +279,7 @@ def workflows_with_timeout(): return wfs_status + def _apply_timeouts(publisher): '''Traverse all filled form and apply expired timeout jumps if needed''' from ..formdef import FormDef diff --git a/wcs/wf/profile.py b/wcs/wf/profile.py index 24d6cbedb..32217a55f 100644 --- a/wcs/wf/profile.py +++ b/wcs/wf/profile.py @@ -84,6 +84,7 @@ class ProfileUpdateTableWidget(WidgetListAsTable): super(ProfileUpdateTableWidget, self).__init__(name, element_type=ProfileUpdateRowWidget, **kwargs) + class FieldNode(XmlSerialisable): node_name = 'field' diff --git a/wcs/wf/register_comment.py b/wcs/wf/register_comment.py index e99942807..9b2038c79 100644 --- a/wcs/wf/register_comment.py +++ b/wcs/wf/register_comment.py @@ -26,6 +26,7 @@ from wcs.workflows import (WorkflowStatusItem, register_item_class, template_on_ import sys + class JournalEvolutionPart: #pylint: disable=C1001 content = None diff --git a/wcs/wf/remove.py b/wcs/wf/remove.py index a681e239d..1fcd14661 100644 --- a/wcs/wf/remove.py +++ b/wcs/wf/remove.py @@ -18,6 +18,7 @@ from quixote import get_publisher, get_request, get_response, get_session from ..qommon import _ from wcs.workflows import WorkflowStatusItem, register_item_class + class RemoveWorkflowStatusItem(WorkflowStatusItem): description = N_('Deletion') key = 'remove' diff --git a/wcs/wf/timeout_jump.py b/wcs/wf/timeout_jump.py index 46d420902..02836c2b8 100644 --- a/wcs/wf/timeout_jump.py +++ b/wcs/wf/timeout_jump.py @@ -16,6 +16,7 @@ from .jump import JumpWorkflowStatusItem, register_item_class + class TimeoutWorkflowStatusItem(JumpWorkflowStatusItem): description = N_('Change Status on Timeout') key = 'timeout' diff --git a/wcs/wf/wscall.py b/wcs/wf/wscall.py index 791db7417..4f44ea3b1 100644 --- a/wcs/wf/wscall.py +++ b/wcs/wf/wscall.py @@ -36,6 +36,7 @@ from wcs.workflows import (WorkflowStatusItem, register_item_class, AbortActionException, AttachmentEvolutionPart) from wcs.wscalls import call_webservice + class JournalWsCallErrorPart: #pylint: disable=C1001 content = None data = None diff --git a/wcs/workflows.py b/wcs/workflows.py index 63dad6050..43ac79562 100644 --- a/wcs/workflows.py +++ b/wcs/workflows.py @@ -50,6 +50,7 @@ from .formdata import Evolution if not __name__.startswith('wcs.') and not __name__ == "__main__": raise ImportError('Import of workflows module must be absolute (import wcs.workflows)') + def lax_int(s): try: return int(s) @@ -1037,6 +1038,7 @@ class WorkflowGlobalActionTimeoutTriggerMarker(object): def __init__(self, timeout_id): self.timeout_id = timeout_id + class WorkflowGlobalActionTimeoutTrigger(WorkflowGlobalActionTrigger): key = 'timeout' anchor = None @@ -2149,6 +2151,7 @@ def get_role_translation(formdata, role_name): else: return str(role_name) + def get_role_translation_label(workflow, role_id): if role_id == logged_users_role().id: return logged_users_role().name @@ -2162,6 +2165,7 @@ def get_role_translation_label(workflow, role_id): except KeyError: return + def render_list_of_roles(workflow, roles): t = [] for r in roles: @@ -2173,6 +2177,7 @@ def render_list_of_roles(workflow, roles): item_classes = [] + def register_item_class(klass): if not klass.key in [x.key for x in item_classes]: item_classes.append(klass) diff --git a/wcs/wscalls.py b/wcs/wscalls.py index d477ae378..9389a04b5 100644 --- a/wcs/wscalls.py +++ b/wcs/wscalls.py @@ -36,6 +36,7 @@ from .qommon.template import Template from wcs.api_utils import sign_url, get_secret_and_orig, MissingSecret from wcs.workflows import WorkflowStatusItem + def call_webservice(url, qs_data=None, request_signature_key=None, method=None, post_data=None, post_formdata=None, formdata=None, cache=False, **kwargs): @@ -202,6 +203,7 @@ class NamedWsCall(XmlStorableObject): (response, status, data) = call_webservice(cache=True, **self.request) return json_loads(data) + class WsCallsSubstitutionProxy(object): def __getattr__(self, attr): try: