From b31d0d8a064aa6a9465d62bc137274449da40c91 Mon Sep 17 00:00:00 2001 From: Thomas NOEL Date: Tue, 12 Jun 2018 16:49:12 +0200 Subject: [PATCH] workflows: only log condition errors as functional errors (#24472) --- tests/test_backoffice_pages.py | 18 +++++++---- tests/test_form_pages.py | 3 +- tests/test_workflows.py | 58 +++++++++++++++++++++++++++++++++- wcs/admin/logged_errors.py | 30 ++++++++++++++++-- wcs/conditions.py | 10 ++++-- wcs/logged_errors.py | 30 +++++++++++++++--- wcs/wf/jump.py | 3 +- wcs/workflows.py | 3 +- 8 files changed, 136 insertions(+), 19 deletions(-) diff --git a/tests/test_backoffice_pages.py b/tests/test_backoffice_pages.py index efc58466..39b174bc 100644 --- a/tests/test_backoffice_pages.py +++ b/tests/test_backoffice_pages.py @@ -3907,14 +3907,17 @@ def test_backoffice_logged_errors(pub): app = login(get_app(pub)) resp = app.get('/backoffice/forms/%s/' % formdef.id) - assert 'ZeroDivisionError' in resp.body + assert 'Failed to evaluate condition' in resp.body + assert 'error ZeroDivisionError' in resp.body resp = resp.click('1 error') resp = app.get('/backoffice/workflows/%s/' % workflow.id) resp2 = resp.click('1 error') - assert 'ZeroDivisionError' in resp2.body - resp = resp2.click('ZeroDivisionError') - assert 'integer division or modulo by zero' in resp.body + assert 'Failed to evaluate condition' in resp2.body + assert 'error ZeroDivisionError' in resp2.body + resp = resp2.click('Failed to evaluate condition') + assert 'ZeroDivisionError: integer division or modulo by zero' in resp.body + assert 'Python Expression:
1/0
' in resp.body assert not 'Acked' in resp.body resp = resp.click('Ack').follow() assert 'Acked' in resp.body @@ -3933,9 +3936,10 @@ def test_backoffice_logged_errors(pub): app = login(get_app(pub)) resp = app.get('/backoffice/workflows/%s/' % workflow.id) - assert 'ZeroDivisionError' in resp.body + assert 'Failed to evaluate condition' in resp2.body + assert 'error ZeroDivisionError' in resp2.body resp2 = resp.click('1 error') - resp = resp2.click('ZeroDivisionError') + resp = resp2.click('Failed to evaluate condition') assert 'href="http://example.net/backoffice/management/test/' in resp.body # very long error string (check it creates a viable tech_id) @@ -3950,7 +3954,7 @@ def test_backoffice_logged_errors(pub): # remove formdef FormDef.wipe() - resp = resp2.click('ZeroDivisionError') + resp = resp2.click('Failed to evaluate condition') assert not 'href="http://example.net/backoffice/management/test/' in resp.body def test_backoffice_private_status_and_history(pub): diff --git a/tests/test_form_pages.py b/tests/test_form_pages.py index eeb93707..39d4bf5b 100644 --- a/tests/test_form_pages.py +++ b/tests/test_form_pages.py @@ -4492,7 +4492,8 @@ def test_logged_errors(pub): assert LoggedError.count() == 1 error = LoggedError.get_on_index( - '34-12-None-None-zerodivisionerror-integer-division-or-modulo-by-zero', 'tech_id') + '34-12-just_submitted-_jump-failed-to-evaluate-condition-ZeroDivisionError-integer-division-or-modulo-by-zero', + 'tech_id') assert error.occurences_count == 2 assert len(LoggedError.get_ids_with_indexed_value('formdef_id', '34')) == 1 diff --git a/tests/test_workflows.py b/tests/test_workflows.py index 81a4d2c6..b94319b3 100644 --- a/tests/test_workflows.py +++ b/tests/test_workflows.py @@ -19,6 +19,7 @@ from wcs.formdef import FormDef from wcs import sessions from wcs.fields import (StringField, DateField, MapField, FileField, ItemField, ItemsField, CommentField) +from wcs.logged_errors import LoggedError from wcs.roles import Role from wcs.workflows import (Workflow, WorkflowStatusItem, SendmailWorkflowStatusItem, SendSMSWorkflowStatusItem, @@ -258,7 +259,6 @@ 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() @@ -278,6 +278,38 @@ 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() + formdef.name = 'foobar' + formdef.store() + pub.substitutions.feed(formdef) + formdef.data_class().wipe() + formdata = formdef.data_class()() + item = JumpWorkflowStatusItem() + + LoggedError.wipe() + item.condition = {'type': 'python', 'value': 'form_var_foobar == 0'} + assert item.must_jump(formdata) is False + assert LoggedError.count() == 1 + logged_error = LoggedError.select()[0] + assert logged_error.summary == 'Failed to evaluate condition' + assert logged_error.exception_class == 'NameError' + assert logged_error.exception_message == "name 'form_var_foobar' is not defined" + assert logged_error.expression == 'form_var_foobar == 0' + assert logged_error.expression_type == 'python' + + LoggedError.wipe() + item.condition = {'type': 'python', 'value': '~ invalid ~'} + assert item.must_jump(formdata) is False + assert LoggedError.count() == 1 + logged_error = LoggedError.select()[0] + assert logged_error.summary == 'Failed to evaluate condition' + assert logged_error.exception_class == 'SyntaxError' + assert logged_error.exception_message == 'unexpected EOF while parsing (, line 1)' + assert logged_error.expression == '~ invalid ~' + assert logged_error.expression_type == 'python' + def test_jump_django_conditions(pub): FormDef.wipe() formdef = FormDef() @@ -289,6 +321,7 @@ def test_jump_django_conditions(pub): pub.substitutions.feed(formdata) item = JumpWorkflowStatusItem() + LoggedError.wipe() item.condition = {'type': 'django', 'value': '1 < 2'} assert item.must_jump(formdata) is True @@ -301,8 +334,17 @@ def test_jump_django_conditions(pub): item.condition = {'type': 'django', 'value': 'form_var_foo|first|upper == "X"'} assert item.must_jump(formdata) is False + assert LoggedError.count() == 0 + item.condition = {'type': 'django', 'value': '~ invalid ~'} assert item.must_jump(formdata) is False + assert LoggedError.count() == 1 + logged_error = LoggedError.select()[0] + assert logged_error.summary == 'Failed to evaluate condition' + assert logged_error.exception_class == 'TemplateSyntaxError' + assert logged_error.exception_message == "Could not parse the remainder: '~' from '~'" + assert logged_error.expression == '~ invalid ~' + assert logged_error.expression_type == 'django' def test_check_auth(pub): user = pub.user_class(name='foo') @@ -3504,3 +3546,17 @@ def test_workflow_action_condition(two_pubs): choice.condition = {'type': 'python', 'value': 'form_name == "baz"'} workflow.store() assert len(FormDef.get(formdef.id).data_class().get_actionable_ids([role.id])) == 2 + + # bad condition + LoggedError.wipe() + choice.condition = {'type': 'python', 'value': 'foobar == barfoo'} + workflow.store() + assert len(FormDef.get(formdef.id).data_class().get_actionable_ids([role.id])) == 0 + assert LoggedError.count() == 1 + logged_error = LoggedError.select()[0] + assert logged_error.occurences_count > 1 # should be 2... == 12 with pickle, 4 with sql + assert logged_error.summary == 'Failed to evaluate condition' + assert logged_error.exception_class == 'NameError' + assert logged_error.exception_message == "name 'foobar' is not defined" + assert logged_error.expression == 'foobar == barfoo' + assert logged_error.expression_type == 'python' diff --git a/wcs/admin/logged_errors.py b/wcs/admin/logged_errors.py index 4ddf1cdb..106da872 100644 --- a/wcs/admin/logged_errors.py +++ b/wcs/admin/logged_errors.py @@ -70,6 +70,22 @@ class LoggedErrorDirectory(Directory): _(status_item.description)) r += htmltext('') + + if self.error.expression or self.error.expression_type: + expression_title = { + 'python': N_('Python Expression'), + 'django': N_('Django Expression'), + 'template': N_('Template'), + 'text': N_('Text'), + }.get(self.error.expression_type, N_('Unknown')) + r += htmltext('
  • %s %s
  • ') % ( + _('%s:') % expression_title, self.error.expression) + + if self.error.exception_class or self.error.exception_message: + r += htmltext('
  • %s %s: %s
  • ') % (_('Error message:'), + self.error.exception_class, + self.error.exception_message) + if formdef: formdata = self.error.get_formdata() if formdata: @@ -159,7 +175,12 @@ class LoggedErrorsDirectory(Directory): '%(count)d error', '%(count)d errors', len(errors)) % {'count': len(errors)} r += htmltext('