0001-workflows-only-log-condition-errors-as-functional-er.patch
tests/test_backoffice_pages.py | ||
---|---|---|
3907 | 3907 | |
3908 | 3908 |
app = login(get_app(pub)) |
3909 | 3909 |
resp = app.get('/backoffice/forms/%s/' % formdef.id) |
3910 |
assert 'ZeroDivisionError' in resp.body
|
|
3910 |
assert 'Failed to evaluate condition (error ZeroDivisionError)' in resp.body
|
|
3911 | 3911 |
resp = resp.click('1 error') |
3912 | 3912 | |
3913 | 3913 |
resp = app.get('/backoffice/workflows/%s/' % workflow.id) |
3914 | 3914 |
resp2 = resp.click('1 error') |
3915 |
assert 'ZeroDivisionError' in resp2.body
|
|
3915 |
assert 'Failed to evaluate condition (error ZeroDivisionError)' in resp2.body
|
|
3916 | 3916 |
resp = resp2.click('ZeroDivisionError') |
3917 |
assert 'integer division or modulo by zero' in resp.body |
|
3917 |
assert 'ZeroDivisionError: integer division or modulo by zero' in resp.body |
|
3918 |
assert 'Expression (type python): <pre>1/0</pre>' in resp.body |
|
3918 | 3919 |
assert not 'Acked' in resp.body |
3919 | 3920 |
resp = resp.click('Ack').follow() |
3920 | 3921 |
assert 'Acked' in resp.body |
... | ... | |
3933 | 3934 | |
3934 | 3935 |
app = login(get_app(pub)) |
3935 | 3936 |
resp = app.get('/backoffice/workflows/%s/' % workflow.id) |
3936 |
assert 'ZeroDivisionError' in resp.body
|
|
3937 |
assert 'Failed to evaluate condition (error ZeroDivisionError)' in resp2.body
|
|
3937 | 3938 |
resp2 = resp.click('1 error') |
3938 | 3939 |
resp = resp2.click('ZeroDivisionError') |
3939 | 3940 |
assert 'href="http://example.net/backoffice/management/test/' in resp.body |
tests/test_form_pages.py | ||
---|---|---|
4492 | 4492 |
assert LoggedError.count() == 1 |
4493 | 4493 | |
4494 | 4494 |
error = LoggedError.get_on_index( |
4495 |
'34-12-None-None-zerodivisionerror-integer-division-or-modulo-by-zero', 'tech_id') |
|
4495 |
'34-12-just_submitted-_jump-failed-to-evaluate-condition-error-zerodivisionerror', |
|
4496 |
'tech_id') |
|
4496 | 4497 |
assert error.occurences_count == 2 |
4497 | 4498 | |
4498 | 4499 |
assert len(LoggedError.get_ids_with_indexed_value('formdef_id', '34')) == 1 |
tests/test_workflows.py | ||
---|---|---|
19 | 19 |
from wcs import sessions |
20 | 20 |
from wcs.fields import (StringField, DateField, MapField, FileField, ItemField, |
21 | 21 |
ItemsField, CommentField) |
22 |
from wcs.logged_errors import LoggedError |
|
22 | 23 |
from wcs.roles import Role |
23 | 24 |
from wcs.workflows import (Workflow, WorkflowStatusItem, |
24 | 25 |
SendmailWorkflowStatusItem, SendSMSWorkflowStatusItem, |
... | ... | |
258 | 259 |
'value': 'utils.time_delta(utils.time.localtime(), "2015-01-04").days > 0'} |
259 | 260 |
assert item.must_jump(formdata) is True |
260 | 261 | |
261 | ||
262 | 262 |
def test_jump_count_condition(pub): |
263 | 263 |
FormDef.wipe() |
264 | 264 |
formdef = FormDef() |
... | ... | |
278 | 278 |
item.condition = {'type': 'python', 'value': 'form_objects.count < 2'} |
279 | 279 |
assert item.must_jump(formdata) is False |
280 | 280 | |
281 |
def test_jump_bad_python_condition(pub): |
|
282 |
FormDef.wipe() |
|
283 |
formdef = FormDef() |
|
284 |
formdef.name = 'foobar' |
|
285 |
formdef.store() |
|
286 |
pub.substitutions.feed(formdef) |
|
287 |
formdef.data_class().wipe() |
|
288 |
formdata = formdef.data_class()() |
|
289 |
item = JumpWorkflowStatusItem() |
|
290 | ||
291 |
LoggedError.wipe() |
|
292 |
item.condition = {'type': 'python', 'value': 'form_var_foobar == 0'} |
|
293 |
assert item.must_jump(formdata) is False |
|
294 |
assert LoggedError.count() == 1 |
|
295 |
logged_error = LoggedError.select()[0] |
|
296 |
assert logged_error.summary == 'Failed to evaluate condition (error NameError)' |
|
297 |
assert logged_error.error_message == "NameError: name 'form_var_foobar' is not defined" |
|
298 |
assert logged_error.expression == 'form_var_foobar == 0' |
|
299 |
assert logged_error.expression_type == 'python' |
|
300 | ||
301 |
LoggedError.wipe() |
|
302 |
item.condition = {'type': 'python', 'value': '~ invalid ~'} |
|
303 |
assert item.must_jump(formdata) is False |
|
304 |
assert LoggedError.count() == 1 |
|
305 |
logged_error = LoggedError.select()[0] |
|
306 |
assert logged_error.summary == 'Failed to evaluate condition (error SyntaxError)' |
|
307 |
assert logged_error.error_message == "SyntaxError: unexpected EOF while parsing (<string>, line 1)" |
|
308 |
assert logged_error.expression == '~ invalid ~' |
|
309 |
assert logged_error.expression_type == 'python' |
|
310 | ||
281 | 311 |
def test_jump_django_conditions(pub): |
282 | 312 |
FormDef.wipe() |
283 | 313 |
formdef = FormDef() |
... | ... | |
289 | 319 |
pub.substitutions.feed(formdata) |
290 | 320 |
item = JumpWorkflowStatusItem() |
291 | 321 | |
322 |
LoggedError.wipe() |
|
292 | 323 |
item.condition = {'type': 'django', 'value': '1 < 2'} |
293 | 324 |
assert item.must_jump(formdata) is True |
294 | 325 | |
... | ... | |
301 | 332 |
item.condition = {'type': 'django', 'value': 'form_var_foo|first|upper == "X"'} |
302 | 333 |
assert item.must_jump(formdata) is False |
303 | 334 | |
335 |
assert LoggedError.count() == 0 |
|
336 | ||
304 | 337 |
item.condition = {'type': 'django', 'value': '~ invalid ~'} |
305 | 338 |
assert item.must_jump(formdata) is False |
339 |
assert LoggedError.count() == 1 |
|
340 |
logged_error = LoggedError.select()[0] |
|
341 |
assert logged_error.summary == 'Failed to evaluate condition (error TemplateSyntaxError)' |
|
342 |
assert logged_error.error_message == "TemplateSyntaxError: Could not parse the remainder: '~' from '~'" |
|
343 |
assert logged_error.expression == '~ invalid ~' |
|
344 |
assert logged_error.expression_type == 'django' |
|
306 | 345 | |
307 | 346 |
def test_check_auth(pub): |
308 | 347 |
user = pub.user_class(name='foo') |
... | ... | |
3504 | 3543 |
choice.condition = {'type': 'python', 'value': 'form_name == "baz"'} |
3505 | 3544 |
workflow.store() |
3506 | 3545 |
assert len(FormDef.get(formdef.id).data_class().get_actionable_ids([role.id])) == 2 |
3546 | ||
3547 |
# bad condition |
|
3548 |
LoggedError.wipe() |
|
3549 |
choice.condition = {'type': 'python', 'value': 'foobar == barfoo'} |
|
3550 |
workflow.store() |
|
3551 |
assert len(FormDef.get(formdef.id).data_class().get_actionable_ids([role.id])) == 0 |
|
3552 |
assert LoggedError.count() == 1 |
|
3553 |
logged_error = LoggedError.select()[0] |
|
3554 |
assert logged_error.occurences_count > 1 # should be 2... == 12 with pickle, 4 with sql |
|
3555 |
assert logged_error.summary == 'Failed to evaluate condition (error NameError)' |
|
3556 |
assert logged_error.error_message == "NameError: name 'foobar' is not defined" |
|
3557 |
assert logged_error.expression == 'foobar == barfoo' |
|
3558 |
assert logged_error.expression_type == 'python' |
wcs/admin/logged_errors.py | ||
---|---|---|
70 | 70 |
_(status_item.description)) |
71 | 71 |
r += htmltext('</ul>') |
72 | 72 | |
73 |
if self.error.expression or self.error.expression_type: |
|
74 |
r += htmltext(' <li>%s <pre>%s</pre></li>') % ( |
|
75 |
_('Expression (type %s):') % self.error.expression_type, |
|
76 |
self.error.expression) |
|
77 | ||
78 |
if self.error.error_message: |
|
79 |
r += htmltext(' <li>%s %s</li>') % (_('Error message:'), self.error.error_message) |
|
80 | ||
73 | 81 |
if formdef: |
74 | 82 |
formdata = self.error.get_formdata() |
75 | 83 |
if formdata: |
wcs/conditions.py | ||
---|---|---|
30 | 30 |
record_errors = True |
31 | 31 |
log_errors = False |
32 | 32 | |
33 |
def __init__(self, condition, context=None):
|
|
33 |
def __init__(self, condition, context={}):
|
|
34 | 34 |
if not condition: |
35 | 35 |
condition = {} |
36 | 36 |
self.type = condition.get('type') |
... | ... | |
53 | 53 |
if self.log_errors: |
54 | 54 |
get_logger().warn('failed to evaluate %r (%r)', self, e) |
55 | 55 |
if self.record_errors: |
56 |
get_publisher().notify_of_exception(sys.exc_info()) |
|
56 |
from wcs.logged_errors import LoggedError |
|
57 |
summary = _('Failed to evaluate condition (error %s)' % e.__class__.__name__) |
|
58 |
LoggedError.record(summary, |
|
59 |
formdata=self.context.get('formdata'), |
|
60 |
status_item=self.context.get('status_item'), |
|
61 |
expression=self.value, |
|
62 |
expression_type=self.type, |
|
63 |
error_message='%s: %s' % (e.__class__.__name__, e)) |
|
57 | 64 |
raise RuntimeError() |
58 | 65 | |
59 | 66 |
def evaluate_python(self, local_variables): |
wcs/logged_errors.py | ||
---|---|---|
33 | 33 |
workflow_id = None |
34 | 34 |
status_id = None |
35 | 35 |
status_item_id = None |
36 |
expression = None |
|
37 |
expression_type = None |
|
38 |
error_message = None |
|
36 | 39 |
traceback = None |
37 | 40 |
occurences_count = 0 |
38 | 41 |
first_occurence_timestamp = None |
... | ... | |
42 | 45 |
# declarations for serialization |
43 | 46 |
XML_NODES = [ |
44 | 47 |
('summary', 'str'), ('traceback', 'str'), |
48 |
('expression', 'str'), ('expression_type', 'str'), ('error_message', 'str'), |
|
45 | 49 |
('formdata_id', 'str'), ('formdef_id', 'str'), ('workflow_id', 'str'), |
46 | 50 |
('status_id', 'str'), ('status_item_id', 'str'), |
47 | 51 |
('occurences_count', 'int'), |
... | ... | |
51 | 55 | |
52 | 56 |
@classmethod |
53 | 57 |
def record(cls, error_summary, plain_error_msg=None, formdata=None, |
54 |
formdef=None, workflow=None, status=None, status_item=None): |
|
58 |
formdef=None, workflow=None, status=None, status_item=None, |
|
59 |
expression=None, expression_type=None, error_message=None): |
|
55 | 60 |
error = cls() |
56 | 61 |
error.summary = error_summary |
57 | 62 |
error.traceback = plain_error_msg |
63 |
error.expression = expression |
|
64 |
error.expression_type = expression_type |
|
65 |
error.error_message = error_message |
|
58 | 66 | |
59 | 67 |
if formdata: |
60 | 68 |
error.formdata_id = str(formdata.id) |
... | ... | |
74 | 82 | |
75 | 83 |
if status_item: |
76 | 84 |
error.status_item_id = status_item.id |
77 |
if status_item.parent:
|
|
85 |
if getattr(status_item, 'parent', None):
|
|
78 | 86 |
error.status_id = status_item.parent.id |
79 | 87 |
if status: |
80 | 88 |
error.status_id = status.id |
... | ... | |
110 | 118 | |
111 | 119 |
@property |
112 | 120 |
def tech_id(self): |
113 |
return ('%s-%s-%s-%s-%s' % (self.formdef_id, self.workflow_id, self.status_id, |
|
114 |
self.status_item_id, simplify(self.summary)))[:200] |
|
121 |
tech_id = '%s-%s-' % (self.formdef_id, self.workflow_id) |
|
122 |
if self.status_id: |
|
123 |
tech_id += '%s-' % self.status_id |
|
124 |
if self.status_item_id: |
|
125 |
tech_id += '%s-' % self.status_item_id |
|
126 |
tech_id += '%s' % simplify(self.summary) |
|
127 |
return tech_id[:200] |
|
115 | 128 | |
116 | 129 |
def get_formdef(self): |
117 | 130 |
return FormDef.get(self.formdef_id, ignore_errors=True) |
wcs/wf/jump.py | ||
---|---|---|
218 | 218 |
must_jump = True |
219 | 219 | |
220 | 220 |
if self.condition: |
221 |
context = {'formdata': formdata, 'status_item': self} |
|
221 | 222 |
try: |
222 |
must_jump = Condition(self.condition).evaluate() |
|
223 |
must_jump = Condition(self.condition, context).evaluate()
|
|
223 | 224 |
except RuntimeError: |
224 | 225 |
must_jump = False |
225 | 226 |
wcs/workflows.py | ||
---|---|---|
1583 | 1583 |
return False |
1584 | 1584 | |
1585 | 1585 |
def check_condition(self, formdata): |
1586 |
context = {'formdata': formdata, 'status_item': self} |
|
1586 | 1587 |
try: |
1587 |
return Condition(self.condition).evaluate() |
|
1588 |
return Condition(self.condition, context).evaluate()
|
|
1588 | 1589 |
except RuntimeError: |
1589 | 1590 |
return False |
1590 | 1591 | |
1591 |
- |