wcs/tests/test_testdef.py

1576 lines
49 KiB
Python

import datetime
import decimal
import io
import json
import time
import xml.etree.ElementTree as ET
from unittest import mock
import pytest
import responses
from django.utils.timezone import make_aware, now
from wcs import fields, workflow_tests
from wcs.blocks import BlockDef
from wcs.carddef import CardDef
from wcs.formdef import FormDef
from wcs.qommon.http_request import HTTPRequest
from wcs.qommon.storage import Equal
from wcs.qommon.upload_storage import PicklableUpload
from wcs.testdef import TestDef, TestDefXmlProxy, TestError, TestResult, WebserviceResponse
from wcs.wscalls import NamedWsCall
from .utilities import clean_temporary_pub, create_temporary_pub
@pytest.fixture
def pub():
pub = create_temporary_pub()
req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'})
pub.set_app_dir(req)
pub.write_cfg()
FormDef.wipe()
BlockDef.wipe()
WebserviceResponse.wipe()
NamedWsCall.wipe()
TestResult.wipe()
TestDef.wipe()
return pub
def teardown_module(module):
clean_temporary_pub()
def test_testdef_export_to_xml(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.ItemsField(id='1', label='Test', items=['foo', 'bar', 'baz']),
fields.BoolField(id='2', label='Check', varname='check'),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = datetime.datetime(2021, 1, 1, 0, 0).timetuple()
formdata.data['1'] = ['foo', 'baz']
formdata.data['2'] = True
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.workflow_tests.actions = [
workflow_tests.AssertStatus(status_name='End status'),
]
testdef.name = 'test'
testdef.expected_error = 'xxx'
testdef.store()
response = WebserviceResponse()
response.testdef_id = testdef.id
response.name = 'Fake response'
response.store()
testdef_xml = ET.tostring(testdef.export_to_xml())
TestDef.wipe()
workflow_tests.WorkflowTests.wipe()
WebserviceResponse.wipe()
testdef2 = TestDef.import_from_xml(io.BytesIO(testdef_xml), formdef)
testdef2.store()
assert testdef2.name == 'test'
assert testdef2.object_type == 'formdefs'
assert testdef2.object_id == str(formdef.id)
assert testdef2.data == {'fields': {'1': ['foo', 'baz'], '2': True}}
assert testdef2.expected_error == 'xxx'
assert testdef2.is_in_backoffice is False
assert len(testdef2.workflow_tests.actions) == 1
assert testdef2.workflow_tests.actions[0].status_name == 'End status'
assert len(testdef2.get_webservice_responses()) == 1
assert testdef2.get_webservice_responses()[0].name == 'Fake response'
# check storage of temporary object used during import is forbidden
testdef_xml = TestDefXmlProxy()
with pytest.raises(AssertionError):
testdef_xml.store()
def test_testdef_result_migrate_legacy_json(pub):
test_result = TestResult()
test_result.object_type = 'formdef'
test_result.object_id = '1'
test_result.timestamp = datetime.datetime(2021, 1, 1, 0, 0)
test_result.success = False
test_result.reason = 'xxx'
test_result.results = [
{
'id': '1',
'name': 'xxx',
'error': 'xxx',
'recorded_errors': ['a', 'b'],
'missing_required_fields': ['c', 'd'],
},
{
'id': '2',
'name': 'xxx',
'error': 'xxx',
},
]
test_result.store()
test_result.store()
TestResult.migrate_legacy()
test_result = TestResult.get(test_result.id)
assert test_result.results == [
{
'id': '1',
'name': 'xxx',
'error': 'xxx',
'details': {
'form_status': None,
'recorded_errors': ['a', 'b'],
'missing_required_fields': ['c', 'd'],
'workflow_test_action_uuid': None,
},
},
{
'id': '2',
'name': 'xxx',
'error': 'xxx',
'details': {
'form_status': None,
'recorded_errors': [],
'missing_required_fields': [],
'workflow_test_action_uuid': None,
},
},
]
def test_testdef_result_clean(pub, freezer):
def make_result(formdef_id, success):
test_result = TestResult()
test_result.object_type = 'formdef'
test_result.object_id = formdef_id
test_result.timestamp = now()
test_result.success = success
test_result.reason = 'xxx'
test_result.store()
# FormDef 1
freezer.move_to('2024-01-25 12:00')
for i in range(20):
make_result(formdef_id='1', success=True)
# FormDef 2
freezer.move_to('2024-01-10 12:00')
for i in range(15):
make_result(formdef_id='2', success=True)
for i in range(15):
freezer.move_to('2024-01-15 12:%s' % i)
make_result(formdef_id='2', success=True)
# FormDef 3
freezer.move_to('2024-01-10 12:00')
for i in range(15):
make_result(formdef_id='3', success=False)
freezer.move_to('2024-01-11 12:00')
make_result(formdef_id='3', success=True)
freezer.move_to('2024-01-12 12:00')
for i in range(5):
make_result(formdef_id='3', success=False)
freezer.move_to('2024-01-25 12:00')
for i in range(10):
make_result(formdef_id='3', success=False)
freezer.move_to('2024-02-01 12:00')
TestResult.clean()
# no deletion for FormDef 1
results_formdef1 = TestResult.select(clause=[Equal('object_id', '1')])
assert len(results_formdef1) == 20
# 10 most recent results were kept for FormDef 2
results_formdef2 = TestResult.select(clause=[Equal('object_id', '2')])
assert len(results_formdef2) == 10
assert all(x.timestamp.day == 15 for x in results_formdef2)
# all recently failed results were kept for FormDef 3, including last success
results_formdef3 = TestResult.select(clause=[Equal('object_id', '3')])
assert len(results_formdef3) == 16
assert len([x for x in results_formdef3 if x.success]) == 1
assert len([x for x in results_formdef3 if x.timestamp.day == 12]) == 5
assert len([x for x in results_formdef3 if x.timestamp.day == 25]) == 10
def test_testdef_create_from_formdata_boolean(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [fields.BoolField(id='1', label='Check', varname='check')]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
testdef = TestDef.create_from_formdata(formdef, formdata)
assert testdef.build_formdata(formdef, include_fields=True).data == {}
formdata.data['1'] = None
testdef = TestDef.create_from_formdata(formdef, formdata)
assert testdef.build_formdata(formdef, include_fields=True).data['1'] is None
formdata.data['1'] = True
testdef = TestDef.create_from_formdata(formdef, formdata)
assert testdef.build_formdata(formdef, include_fields=True).data['1'] is True
formdata.data['1'] = False
testdef = TestDef.create_from_formdata(formdef, formdata)
assert testdef.build_formdata(formdef, include_fields=True).data['1'] is False
def test_testdef_create_from_formdata_computed_field(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.ComputedField(id='1', label='Computed', varname='foo', value_template='{{ 1.2|decimal }}')
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.data['1'] = decimal.Decimal('1.2')
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.store()
assert testdef.data['fields'] == {}
def test_page_post_conditions(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.TitleField(id='0', label='Title'),
fields.PageField(
id='1',
label='1st page',
post_conditions=[
{'condition': {'type': 'django', 'value': 'form_var_text == "a"'}, 'error_message': 'Error'}
],
),
fields.StringField(id='2', label='Text', varname='text'),
fields.PageField(
id='3',
label='2nd page',
post_conditions=[
{'condition': {'type': 'django', 'value': 'form_var_text2 == "a"'}, 'error_message': 'Error'}
],
),
fields.StringField(id='4', label='Text', varname='text2'),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
formdata.data['2'] = 'a'
formdata.data['4'] = 'a'
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
formdata.data['4'] = 'b'
testdef = TestDef.create_from_formdata(formdef, formdata)
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert str(excinfo.value) == 'Page 2 post condition was not met (form_var_text2 == "a").'
formdata.data['2'] = 'b'
testdef = TestDef.create_from_formdata(formdef, formdata)
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert str(excinfo.value) == 'Page 1 post condition was not met (form_var_text == "a").'
def test_page_post_condition_invalid(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.PageField(
id='0',
label='1st page',
post_conditions=[{'condition': {'type': 'django', 'value': '{{}'}, 'error_message': 'Error'}],
),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
testdef = TestDef.create_from_formdata(formdef, formdata)
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert str(excinfo.value) == 'Failed to evaluate page 1 post condition.'
def test_session_variables(pub):
user = pub.user_class(name='test user')
user.email = 'test@example.com'
user.store()
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.PageField(
id='1',
label='1st page',
post_conditions=[
{
'condition': {'type': 'django', 'value': 'session_hash_id|length == 40'},
'error_message': 'Error',
}
],
),
fields.PageField(
id='2',
label='2nd page',
post_conditions=[
{
'condition': {'type': 'django', 'value': 'session_user_email == "test@example.com"'},
'error_message': 'Error',
}
],
),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.user = user
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
formdata.user = None
testdef = TestDef.create_from_formdata(formdef, formdata)
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert (
str(excinfo.value) == 'Page 2 post condition was not met (session_user_email == "test@example.com").'
)
def test_field_conditions(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.StringField(id='1', label='Text', varname='text'),
fields.StringField(
id='2',
label='Text with condition',
varname='text_cond',
required=False,
condition={'type': 'django', 'value': 'form_var_text == "a"'},
),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
formdata.data['1'] = 'a'
formdata.data['2'] = 'xxx'
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
formdata.data['1'] = 'b'
testdef = TestDef.create_from_formdata(formdef, formdata)
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert str(excinfo.value) == 'Tried to fill field "Text with condition" on page 1 but it is hidden.'
formdata.data['2'] = None
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
formdata.data['1'] = 'a'
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
def test_field_conditions_boolean(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.BoolField(id='1', label='Check', varname='check'),
fields.StringField(
id='2',
label='Text with condition',
varname='text_cond',
condition={'type': 'django', 'value': 'form_var_check == True'},
),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
formdata.data['1'] = False
formdata.data['2'] = None
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
formdata.data['2'] = 'xxx'
testdef = TestDef.create_from_formdata(formdef, formdata)
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert str(excinfo.value) == 'Tried to fill field "Text with condition" on page 1 but it is hidden.'
formdata.data['1'] = True
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
def test_multi_page_condition(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.PageField(id='0', label='1st page'),
fields.StringField(id='1', label='Text', varname='text'),
fields.PageField(
id='2',
label='2nd page',
condition={'type': 'django', 'value': 'form_var_text == "a"'},
post_conditions=[
{'condition': {'type': 'django', 'value': 'form_var_text2'}, 'error_message': ''}
],
),
fields.StringField(id='3', label='Text 2', varname='text2', required=False),
fields.PageField(id='4', label='3rd page', condition={'type': 'django', 'value': 'form_var_text'}),
fields.StringField(id='5', label='Text 3', varname='text3'),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
formdata.data['1'] = 'a'
formdata.data['3'] = 'xxx'
formdata.data['5'] = 'yyy'
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
formdata.data['1'] = 'b'
testdef = TestDef.create_from_formdata(formdef, formdata)
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert str(excinfo.value) == 'Tried to fill field "Text 2" on page 2 but page was not shown.'
formdata.data['3'] = None
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
formdata.data['1'] = 'a'
testdef = TestDef.create_from_formdata(formdef, formdata)
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert str(excinfo.value) == 'Page 2 post condition was not met (form_var_text2).'
def test_validation_string_field(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.StringField(id='1', label='String field digits', validation={'type': 'digits'}),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
formdata.data['1'] = '1'
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
formdata.data['1'] = 'xxx'
testdef = TestDef.create_from_formdata(formdef, formdata)
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert (
str(excinfo.value)
== 'Invalid value "xxx" for field "String field digits": You should enter digits only, for example: 123.'
)
def test_validation_required_field(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.StringField(id='1', label='Text', required=False),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
assert testdef.missing_required_fields == []
formdef.fields[0].required = True
testdef.run(formdef)
assert testdef.missing_required_fields == ['Text']
def test_validation_item_field(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [fields.ItemField(id='1', label='Test', items=['foo', 'bar', 'baz'])]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
formdata.data['1'] = 'foo'
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
formdata.data['1'] = 'xxx'
testdef = TestDef.create_from_formdata(formdef, formdata)
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert str(excinfo.value) == 'Invalid value "xxx" for field "Test": invalid value selected'
# no check on invalid value for field with data source
formdef.fields[0].data_source = {'type': 'jsonvalue', 'value': json.dumps({})}
formdef.store()
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
formdata.data['1'] = None
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
assert testdef.missing_required_fields == ['Test']
def test_validation_item_field_inside_block(pub):
block = BlockDef()
block.name = 'foobar'
block.fields = [fields.ItemField(id='1', label='Test', items=['foo', 'bar', 'baz'])]
block.store()
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.BlockField(id='1', label='Block Data', varname='blockdata', block_slug='foobar', max_items=3),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
formdata.data['1'] = {'data': [{'1': 'foo'}]}
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
formdata.data['1'] = {'data': [{'1': 'xxx'}]}
testdef = TestDef.create_from_formdata(formdef, formdata)
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert (
str(excinfo.value) == 'Empty value for field "Test" (of field "Block Data"): invalid value selected'
)
# no check on invalid value for field with data source
block.fields[0].data_source = {'type': 'jsonvalue', 'value': json.dumps({})}
block.store()
formdef.refresh_from_storage()
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
# ignore required field errors
formdata.data['1'] = {'data': [{'1': None}]}
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
assert testdef.missing_required_fields == ['foobar']
def test_validation_optional_field_inside_required_block(pub):
block = BlockDef()
block.name = 'foobar'
block.fields = [fields.StringField(id='1', label='Test', required=False)]
block.store()
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.BlockField(id='1', label='Block Data', varname='blockdata', block_slug='foobar', max_items=3),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
formdata.data['1'] = {'data': [{'1': 'foo'}]}
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
assert testdef.missing_required_fields == []
formdata.data['1'] = {'data': [{'1': None}]}
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
assert testdef.missing_required_fields == ['foobar']
def test_item_field_display_value(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.PageField(
id='0',
label='1st page',
post_conditions=[
{'condition': {'type': 'django', 'value': 'form_var_item == "foo"'}, 'error_message': ''}
],
),
fields.ItemField(id='1', label='Test', items=['foo', 'bar', 'baz'], varname='item'),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
formdata.data['1'] = 'foo'
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
def test_item_field_structured_value(pub):
data_source = {
'type': 'jsonvalue',
'value': json.dumps(
[{'id': '1', 'text': 'un', 'more': 'foo'}, {'id': '2', 'text': 'deux', 'more': 'bar'}]
),
}
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.PageField(
id='0',
label='1st page',
post_conditions=[
{'condition': {'type': 'django', 'value': 'form_var_item_more == "bar"'}, 'error_message': ''}
],
),
fields.ItemField(id='1', label='Test', varname='item', data_source=data_source),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
formdata.data = {
'1': '2',
'1_raw': '2',
'1_display': 'deux',
'1_structured': {'id': '2', 'text': 'deux', 'more': 'bar'},
}
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
# change in data source doesn't affect test
formdef.fields[1].data_source = {
'type': 'jsonvalue',
'value': json.dumps([{'id': '2', 'text': 'deux', 'more': 'foo'}]),
}
testdef.run(formdef)
formdef.fields[1].data_source = data_source = {'type': 'jsonvalue', 'value': json.dumps([])}
testdef.run(formdef)
# unavailable remote data source is not called
formdef.fields[1].data_source = {'type': 'json', 'value': 'https://example.net'}
with responses.RequestsMock():
testdef.run(formdef)
def test_item_field_structured_value_inside_block(pub):
data_source = {
'type': 'jsonvalue',
'value': json.dumps(
[{'id': '1', 'text': 'un', 'more': 'foo'}, {'id': '2', 'text': 'deux', 'more': 'bar'}]
),
}
block = BlockDef()
block.name = 'foobar'
block.fields = [
fields.ItemField(id='1', label='Test', varname='item', data_source=data_source),
]
block.store()
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.PageField(
id='0',
label='1st page',
post_conditions=[
{
'condition': {'type': 'django', 'value': 'form_var_blockdata_0_item_more == "bar"'},
'error_message': '',
}
],
),
fields.BlockField(id='1', label='Block Data', varname='blockdata', block_slug='foobar', max_items=3),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
formdata.data['1'] = {
'data': [
{
'1': '2',
'1_display': 'deux',
'1_structured': {'id': '2', 'text': 'deux', 'more': 'bar'},
}
]
}
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
# change in data source doesn't affect test
formdef.fields[1].block.fields[0].data_source = {
'type': 'jsonvalue',
'value': json.dumps([{'id': '2', 'text': 'deux', 'more': 'foo'}]),
}
testdef.run(formdef)
formdef.fields[1].block.fields[0].data_source = {'type': 'jsonvalue', 'value': json.dumps([])}
testdef.run(formdef)
# unavailable remote data source is not called
formdef.fields[1].block.fields[0].data_source = {'type': 'json', 'value': 'https://example.net'}
with responses.RequestsMock():
testdef.run(formdef)
def test_item_field_card_data_source_live(pub):
CardDef.wipe()
carddef = CardDef()
carddef.name = 'card title'
carddef.fields = [fields.StringField(id='0', label='Name', varname='name')]
carddef.store()
carddata = carddef.data_class()()
carddata.data['0'] = 'xxx'
carddata.just_created()
carddata.store()
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.PageField(
id='0',
label='1st page',
post_conditions=[
{
'condition': {'type': 'django', 'value': 'form_var_foo_live_var_name == "abc"'},
'error_message': 'Error',
},
],
),
fields.ItemField(
id='1',
label='Foo',
varname='foo',
data_source={'type': 'carddef:card-title'},
display_mode='autocomplete',
),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
formdata.data['1'] = str(carddata.id)
testdef = TestDef.create_from_formdata(formdef, formdata)
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert str(excinfo.value) == 'Page 1 post condition was not met (form_var_foo_live_var_name == "abc").'
carddata.data['0'] = 'abc'
carddata.just_created()
carddata.store()
testdef.run(formdef)
def test_validation_items_field(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [fields.ItemsField(id='1', label='Test', items=['foo', 'bar', 'baz'])]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
formdata.data['1'] = ['foo', 'baz']
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
# no check on invalid value
formdata.data['1'] = ['foo', 'xxx']
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
formdata.data['1'] = []
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
assert testdef.missing_required_fields == ['Test']
def test_validation_email_field(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.EmailField(id='1', label='Test'),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
formdata.data['1'] = 'test@entrouvert.com'
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
formdata.data['1'] = 'xxx'
testdef = TestDef.create_from_formdata(formdef, formdata)
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert (
str(excinfo.value)
== 'Invalid value "xxx" for field "Test": You should enter a valid email address, for example name@example.com.'
)
def test_validation_boolean_field(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.BoolField(id='1', label='Test'),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
formdata.data['1'] = False
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
formdef.fields[0].required = True
testdef.run(formdef)
assert testdef.missing_required_fields == ['Test']
formdata.data['1'] = True
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
def test_validation_date_field(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.DateField(id='1', label='Test'),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
formdata.data['1'] = time.strptime('2022-07-19', '%Y-%m-%d')
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
formdata.data['1'] = time.strptime('1312-01-01', '%Y-%m-%d')
testdef = TestDef.create_from_formdata(formdef, formdata)
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert str(excinfo.value) == 'Invalid value "1312-01-01" for field "Test": You should enter a valid date.'
def test_validation_map_field(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.PageField(
id='0',
label='1st page',
post_conditions=[
{
'condition': {'type': 'django', 'value': 'form_var_map == "1.0;2.0"'},
'error_message': 'Error',
}
],
),
fields.MapField(id='1', label='Map', varname='map'),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
formdata.data['1'] = '1.0;2.0'
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
def test_validation_file_field(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.FileField(id='1', label='File', varname='foo', max_file_size='1ko'),
fields.StringField(
id='2',
label='Text',
varname='text',
condition={'type': 'django', 'value': 'form_var_foo == "hop.pdf"'},
),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
upload = PicklableUpload('test.pdf', 'application/pdf', 'ascii')
upload.receive([b'first line', b'second line'])
formdata.data['1'] = upload
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
# test against empty value
formdata.data['1'] = None
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
assert testdef.missing_required_fields == ['File']
# test with filename that will negate next field condition
upload = PicklableUpload('hop.pdf', 'application/pdf', 'ascii')
upload.receive([b'first line', b'second line'])
formdata.data['1'] = upload
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
assert testdef.missing_required_fields == ['Text']
formdata.data['2'] = 'xxx'
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
assert testdef.missing_required_fields == []
pub.site_options.set('options', 'blacklisted-file-types', 'application/pdf')
testdef = TestDef.create_from_formdata(formdef, formdata)
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert str(excinfo.value) == 'Invalid value "hop.pdf" for field "File": forbidden file type'
def test_validation_block_field(pub):
block = BlockDef()
block.name = 'foobar'
block.fields = [
fields.StringField(id='1', label='Text', varname='text'),
fields.StringField(id='2', label='Hop'),
]
block.store()
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.PageField(
id='0',
label='1st page',
post_conditions=[
{
'condition': {'type': 'django', 'value': 'form_var_blockdata_1_text == "a"'},
'error_message': '',
}
],
),
fields.BlockField(id='1', label='Block Data', varname='blockdata', block_slug='foobar', max_items=3),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
formdata.data['1'] = {'data': [{'1': 'b'}, {'1': 'a'}]}
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
assert testdef.missing_required_fields == ['Hop', 'Hop']
formdata.data['1'] = {'data': [{'1': 'a', '2': 'z'}, {'1': 'b', '2': 'z'}]}
testdef = TestDef.create_from_formdata(formdef, formdata)
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert str(excinfo.value) == 'Page 1 post condition was not met (form_var_blockdata_1_text == "a").'
formdata.data['1'] = {'data': [{'1': 'b', '2': 'z'}, {'1': 'a', '2': 'z'}]}
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
def test_computed_field_support(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.PageField(
id='0',
label='1st page',
post_conditions=[
{
'condition': {
'type': 'django',
'value': 'form_var_foo == "xxx" and form_var_bar == "hop"',
},
'error_message': '',
}
],
),
fields.ComputedField(
id='1',
label='Computed (frozen)',
varname='foo',
value_template='{% firstof form_var_text "xxx" %}',
freeze_on_initial_value=True,
),
fields.ComputedField(
id='2', label='Computed (live)', varname='bar', value_template='{% firstof form_var_text "xxx" %}'
),
fields.StringField(id='3', label='Text', varname='text'),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
formdata.data['1'] = 'zzz'
formdata.data['3'] = 'hop'
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
formdef.fields[1].value_template = '{% for %}'
testdef = TestDef.create_from_formdata(formdef, formdata)
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert (
str(excinfo.value)
== 'Page 1 post condition was not met (form_var_foo == "xxx" and form_var_bar == "hop").'
)
def test_computed_field_support_complex_data(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.PageField(
id='0',
label='1st page',
post_conditions=[
{'condition': {'type': 'django', 'value': 'form_var_foo|length == 3'}, 'error_message': ''}
],
),
fields.ComputedField(
id='1',
label='Computed',
varname='foo',
value_template='{{form_objects|first|get:"form_var_items_raw"}}',
),
fields.ItemsField(id='2', label='Items', varname='items', required=False),
]
formdef.store()
submitted_formdata = formdef.data_class()()
submitted_formdata.just_created()
submitted_formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
submitted_formdata.data['2'] = ['a', 'bc']
submitted_formdata.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
testdef = TestDef.create_from_formdata(formdef, formdata)
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert str(excinfo.value) == 'Page 1 post condition was not met (form_var_foo|length == 3).'
submitted_formdata.data['2'] = ['a', 'b', 'c']
submitted_formdata.store()
testdef.run(formdef)
def test_computed_field_support_webservice(pub, http_requests):
wscall = NamedWsCall()
wscall.name = 'Hello world'
wscall.request = {'url': 'http://remote.example.net/json'}
wscall.store()
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.PageField(
id='0',
label='1st page',
post_conditions=[
{
'condition': {'type': 'django', 'value': 'form_var_computed_foo == "bar"'},
'error_message': '',
}
],
),
fields.ComputedField(
id='1',
label='Computed',
varname='computed',
value_template='{{ webservice.hello_world }}',
freeze_on_initial_value=True,
),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.store()
testdef.run(formdef)
assert len(testdef.sent_requests) == 1
assert testdef.sent_requests[0]['method'] == 'GET'
assert testdef.sent_requests[0]['url'] == 'http://remote.example.net/json'
response = WebserviceResponse()
response.testdef_id = testdef.id
response.name = 'Fake response'
response.url = 'http://remote.example.net/json'
response.payload = '{"foo": "bar"}'
response.store()
testdef.run(formdef)
assert len(testdef.sent_requests) == 1
assert testdef.sent_requests[0]['url'] == 'http://remote.example.net/json'
assert testdef.sent_requests[0]['webservice_response_id'] == response.id
response.payload = '{"foo": "baz"}'
response.store()
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert str(excinfo.value) == 'Page 1 post condition was not met (form_var_computed_foo == "bar").'
assert len(testdef.sent_requests) == 1
assert testdef.sent_requests[0]['url'] == 'http://remote.example.net/json'
assert testdef.sent_requests[0]['webservice_response_id'] == response.id
response.url = 'http://example.com/json'
response.store()
testdef.run(formdef)
assert len(testdef.sent_requests) == 1
assert testdef.sent_requests[0]['url'] == 'http://remote.example.net/json'
assert testdef.sent_requests[0]['webservice_response_id'] is None
response.url = None
response.store()
testdef.run(formdef)
assert len(testdef.sent_requests) == 1
assert testdef.sent_requests[0]['url'] == 'http://remote.example.net/json'
assert testdef.sent_requests[0]['webservice_response_id'] is None
testdef = TestDef.create_from_formdata(formdef, formdata)
with mock.patch('wcs.testdef.MockWebserviceResponseAdapter._send', side_effect=KeyError('missing key')):
with pytest.raises(TestError):
testdef.run(formdef)
assert len(testdef.sent_requests) == 0
assert testdef.recorded_errors == [
"Unexpected error when mocking webservice call for url http://remote.example.net/json: 'missing key'."
]
def test_computed_field_value_too_long(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.PageField(
id='0',
label='1st page',
post_conditions=[
{
'condition': {'type': 'django', 'value': 'not form_var_computed'},
'error_message': '',
}
],
),
fields.ComputedField(
id='1',
label='Computed',
varname='computed',
value_template='{% token_decimal length=9999 %}',
),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
testdef = TestDef.create_from_formdata(formdef, formdata)
with pytest.raises(TestError):
testdef.run(formdef)
formdef.fields[1].value_template = '{% token_decimal length=100000 %}'
formdef.store()
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
def test_computed_field_forms_template_access(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.PageField(
id='0',
label='1st page',
post_conditions=[
{
'condition': {'type': 'django', 'value': 'form_var_computed == 1'},
'error_message': 'Not enough chars.',
}
],
),
fields.ComputedField(
id='1',
label='Computed',
varname='computed',
value_template='{{ forms|objects:"test-title"|count }}',
freeze_on_initial_value=True,
),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
formdata.store()
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
formdef.fields[
1
].value_template = '{{ forms|objects:"test-title"|filter_by:"unknown"|filter_value:"xxx"|count }}'
formdef.store()
testdef = TestDef.create_from_formdata(formdef, formdata)
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert str(excinfo.value) == 'Page 1 post condition was not met (form_var_computed == 1).'
assert testdef.recorded_errors == ['Invalid filter "unknown"']
def test_numeric_field_support(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.PageField(
id='0',
label='1st page',
post_conditions=[
{'condition': {'type': 'django', 'value': 'form_var_foo == 13.12'}, 'error_message': ''}
],
),
fields.NumericField(
id='1', label='Numeric', varname='foo', restrict_to_integers=False, min_value=decimal.Decimal(10)
),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.data['1'] = decimal.Decimal(13.12)
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.store()
testdef.run(formdef)
formdata.data['1'] = decimal.Decimal(9)
testdef = TestDef.create_from_formdata(formdef, formdata)
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert (
str(excinfo.value)
== 'Invalid value "9" for field "Numeric": You should enter a number greater than or equal to 10.'
)
formdata.data['1'] = decimal.Decimal(42)
testdef = TestDef.create_from_formdata(formdef, formdata)
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert str(excinfo.value) == 'Page 1 post condition was not met (form_var_foo == 13.12).'
def test_expected_error(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.PageField(
id='0',
label='1st page',
post_conditions=[
{
'condition': {'type': 'django', 'value': 'form_var_text|length > 5'},
'error_message': 'Not enough chars.',
}
],
),
fields.StringField(id='1', label='Text', varname='text', validation={'type': 'digits'}),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
formdata.data['1'] = '123456'
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
formdata.data['1'] = '1'
testdef = TestDef.create_from_formdata(formdef, formdata)
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert str(excinfo.value) == 'Page 1 post condition was not met (form_var_text|length > 5).'
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.expected_error = 'Not enough chars.'
testdef.run(formdef)
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.expected_error = 'Other error.'
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert str(excinfo.value) == 'Expected error "Other error." but got error "Not enough chars." instead.'
formdata.data['1'] = 'abcdef'
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.expected_error = 'You should enter digits only, for example: 123.'
testdef.run(formdef)
def test_expected_error_conditional_field(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.StringField(id='1', label='Text', varname='text'),
fields.StringField(
id='2',
label='Text 2',
varname='text2',
validation={'type': 'digits'},
condition={'type': 'django', 'value': 'form_var_text == "a"'},
),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
formdata.data['1'] = 'a'
formdata.data['2'] = 'b'
testdef = TestDef.create_from_formdata(formdef, formdata)
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert (
str(excinfo.value)
== 'Invalid value "b" for field "Text 2": You should enter digits only, for example: 123.'
)
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.expected_error = 'You should enter digits only, for example: 123.'
testdef.run(formdef)
formdata.data['1'] = 'b'
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.expected_error = 'You should enter digits only, for example: 123.'
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert str(excinfo.value) == (
'Expected error "You should enter digits only, for example: 123." but got error "Tried to fill field "Text 2" on '
'page 1 but it is hidden." instead.'
)
def test_is_in_backoffice(pub):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.PageField(
id='0',
label='1st page',
post_conditions=[
{
'condition': {'type': 'django', 'value': 'not is_in_backoffice'},
'error_message': 'Must not be in backoffice',
}
],
),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = make_aware(datetime.datetime(2021, 1, 1, 0, 0))
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.run(formdef)
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.is_in_backoffice = True
with pytest.raises(TestError) as excinfo:
testdef.run(formdef)
assert str(excinfo.value) == 'Page 1 post condition was not met (not is_in_backoffice).'
testdef.is_in_backoffice = False
testdef.run(formdef)
def test_webservice_response_match_request(pub, http_requests):
formdef = FormDef()
formdef.name = 'test title'
formdef.fields = [
fields.ComputedField(
id='1',
label='Computed',
varname='computed',
value_template='{{ webservice.hello_world }}',
freeze_on_initial_value=True,
),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.store()
wscall = NamedWsCall()
wscall.name = 'Hello world'
wscall.request = {
'url': 'http://remote.example.net/json',
'method': 'POST',
'qs_data': {'foo': 'bar'},
'post_data': {'foo2': 'bar2'},
}
wscall.store()
response = WebserviceResponse()
response.testdef_id = testdef.id
response.name = 'Fake response'
response.url = 'http://remote.example.net/json'
response.payload = '{}'
response.store()
testdef.run(formdef)
assert testdef.sent_requests[0]['webservice_response_id'] == response.id
# method restriction
response.method = 'GET'
response.store()
testdef.run(formdef)
assert testdef.sent_requests[0]['webservice_response_id'] is None
response.method = 'POST'
response.store()
testdef.run(formdef)
assert testdef.sent_requests[0]['webservice_response_id'] == response.id
# query string restriction
response.qs_data = {
'foo': 'bar',
'xxx': 'yyy',
}
response.store()
testdef.run(formdef)
assert testdef.sent_requests[0]['webservice_response_id'] is None
del response.qs_data['xxx']
response.store()
testdef.run(formdef)
assert testdef.sent_requests[0]['webservice_response_id'] == response.id
# post data restriction
response.post_data = {
'foo2': 'bar2',
'xxx': 'yyy',
}
response.store()
testdef.run(formdef)
assert testdef.sent_requests[0]['webservice_response_id'] is None
del response.post_data['xxx']
response.store()
testdef.run(formdef)
assert testdef.sent_requests[0]['webservice_response_id'] == response.id