def update(service_id, section): s3_uploader = S3(bucket_name=main.config["S3_DOCUMENT_BUCKET"]) service_data = data_api_client.get_service(service_id)["services"] posted_data = dict(list(request.form.items()) + list(request.files.items())) content = service_content.get_builder().filter(service_data) # Turn responses which have multiple parts into lists for key in request.form: item_as_list = request.form.getlist(key) list_types = ["list", "checkboxes", "pricing"] if key != "csrf_token" and service_content.get_question(key)["type"] in list_types: posted_data[key] = item_as_list posted_data.pop("csrf_token", None) form = Validate(service_content, service_data, posted_data, main.config["DOCUMENTS_URL"], s3_uploader) form.validate() update_data = {} for question_id in form.clean_data: if question_id not in form.errors: update_data[question_id] = form.clean_data[question_id] if update_data: try: data_api_client.update_service(service_data["id"], update_data, current_user.email_address, "admin app") except HTTPError as e: return e.message if form.errors: service_data.update(form.dirty_data) return render_template( "edit_section.html", **get_template_data( section=content.get_section(section), service_data=service_data, service_id=service_id, errors=form.errors, ) ) else: return redirect(url_for(".view", service_id=service_id))
class TestValidate(unittest.TestCase): def setUp(self): self.service = { 'id': 1, 'supplierId': 2, } self.data = {} self.content = {} self.uploader = mock.Mock() self.default_suffix_patch = mock.patch( 'dmutils.validation.default_file_suffix', return_value='2015-01-01-1200' ).start() self.validate = Validate( content=mock.Mock(), doc_url="https://assets.test.digitalmarketplace.service.gov.uk", service=self.service, posted_data=self.data, uploader=self.uploader ) self.validate.content.get_question = lambda key: self.content[key] def tearDown(self): self.default_suffix_patch.stop() def set_question(self, question_id, data, content, **kwargs): self.data[question_id] = data if 'value' in kwargs: self.service[question_id] = kwargs['value'] self.content[question_id] = content self.validate.validate() def test_validate_empty(self): self.validate.validate() self.assertEquals(self.validate.errors, {}) def test_validate_empty_question(self): self.set_question('q1', mock_file('a', 0), {'validations': []}) self.assertEquals(self.validate.errors, {}) def test_validate_non_empty_answer_required(self): self.set_question( 'q1', mock_file('a', 1), { 'type': 'upload', 'validations': [{ 'name': 'answer_required', 'message': 'failed' }] } ) self.assertEquals(self.validate.errors, {}) def test_validate_required_boolean_gets_type(self): self.set_question( 'q1', 'False', { 'type': 'boolean', 'validations': [{ 'name': 'answer_required', 'message': '' }] } ) self.set_question( 'q2', 'True', { 'type': 'boolean', 'validations': [{ 'name': 'answer_required', 'message': '' }] } ) self.assertEquals(self.validate.clean_data, {'q1': False, 'q2': True}) def test_validate_text_without_validators(self): self.set_question( 'q1', 'value', { 'optional': True, 'validations': [] } ) self.assertEquals(self.validate.errors, {}) self.assertEquals(self.validate.clean_data['q1'], 'value') def test_validate_empty_upload_optional(self): self.set_question( 'q1', mock_file('a.pdf', 0), { 'type': 'upload', 'optional': True, 'validations': [{ 'name': 'answer_required', 'message': 'failed' }] } ) self.assertEquals(self.validate.errors, {}) self.assertNotIn('q1', self.validate.clean_data) def test_validate_empty_upload_optional_with_previous_value(self): self.set_question( 'q2', mock_file('a.pdf', 0), { 'type': 'upload', 'optional': True, 'validations': [{ 'name': 'answer_required', 'message': 'failed' }] }, value='b.pdf', ) self.assertEquals(self.validate.errors, {}) self.assertNotIn('q1', self.validate.clean_data) def test_validate_empty_upload_not_optional(self): self.set_question( 'q1', mock_file('a.pdf', 0), { 'type': 'upload', 'validations': [{ 'name': 'answer_required', 'message': 'failed' }] } ) self.assertEquals(self.validate.errors, {'q1': 'failed'}) self.assertNotIn('q1', self.validate.clean_data) def test_validate_empty_upload_not_optional_with_previous_value(self): self.set_question( 'q2', mock_file('a.pdf', 0), { 'type': 'upload', 'validations': [{ 'name': 'answer_required', 'message': 'failed' }] }, value='b.pdf', ) self.assertEquals(self.validate.errors, {}) self.assertNotIn('q1', self.validate.clean_data) def test_validate_empty_text_optional(self): self.set_question( 'q1', '', { 'optional': True, 'validations': [{ 'name': 'answer_required', 'message': 'failed' }] } ) self.assertEquals(self.validate.errors, {}) self.assertEquals(self.validate.clean_data['q1'], '') def test_validate_empty_text_optional_with_previous_value(self): self.set_question( 'q2', '', { 'optional': True, 'validations': [{ 'name': 'answer_required', 'message': 'failed' }] }, value='Previous value', ) self.assertEquals(self.validate.errors, {}) self.assertEquals(self.validate.clean_data['q2'], '') def test_validate_empty_text_not_optional(self): self.set_question( 'q1', '', { 'validations': [{ 'name': 'answer_required', 'message': 'failed' }] } ) self.assertEquals(self.validate.errors, {'q1': 'failed'}) self.assertNotIn('q1', self.validate.clean_data) def test_validate_empty_text_not_optional_with_previous_value(self): self.set_question( 'q2', '', { 'validations': [{ 'name': 'answer_required', 'message': 'failed' }] }, value='Previous value', ) self.assertEquals(self.validate.errors, {'q2': 'failed'}) self.assertNotIn('q1', self.validate.clean_data) def test_incorrect_document_format(self): self.set_question( 'q1', mock_file('a.txt', 1), { 'type': 'upload', 'validations': [{ 'name': 'file_is_open_document_format', 'message': 'failed' }] } ) self.assertEquals(self.validate.errors, {'q1': 'failed'}) def test_incorrect_file_size(self): self.set_question( 'q1', mock_file('a.pdf', 5400001), { 'type': 'upload', 'validations': [{ 'name': 'file_is_less_than_5mb', 'message': 'failed' }] } ) self.assertEquals(self.validate.errors, {'q1': 'failed'}) def test_list_of_validations(self): self.set_question( 'q1', mock_file('a.pdf', 1), { 'type': 'upload', 'validations': [ { 'name': 'file_is_open_document_format', 'message': 'failed' }, { 'name': 'file_is_less_than_5mb', 'message': 'failed' }, ] }, value='b.pdf' ) self.assertEquals(self.validate.errors, {}) def test_file_save(self): self.set_question( 'pricingDocumentURL', mock_file('a.pdf', 1, 'pricingDocumentURL'), { 'type': 'upload', 'validations': [ {'name': 'file_can_be_saved', 'message': 'failed'}, ] }, value='b.pdf' ) self.assertEquals(self.validate.errors, {}) self.uploader.save.assert_called_once_with( 'documents/2/1-pricing-document-2015-01-01-1200.pdf', self.data['pricingDocumentURL'] ) self.assertEquals(self.validate.clean_data, { 'pricingDocumentURL': 'https://assets.test.digitalmarketplace.service.gov.uk/documents/2/1-pricing-document-2015-01-01-1200.pdf', # noqa }) def test_failed_file_upload(self): self.uploader.save.side_effect = S3ResponseError(403, 'Forbidden') self.set_question( 'pricingDocumentURL', mock_file('a.pdf', 1, 'pricingDocumentURL'), { 'type': 'upload', 'validations': [ {'name': 'file_can_be_saved', 'message': 'failed'}, ] }, value='b.pdf' ) self.assertEquals(self.validate.errors, { 'pricingDocumentURL': 'failed' }) self.assertEquals(self.validate.clean_data, {}) def test_field_with_no_previous_value(self): self.set_question( 'pricingDocumentURL', mock_file('a.pdf', 1), { 'type': 'upload', 'validations': [ {'name': 'answer_required', 'message': 'failed'}, { 'name': 'file_is_open_document_format', 'message': 'failed' }, {'name': 'file_is_less_than_5mb', 'message': 'failed'}, {'name': 'file_can_be_saved', 'message': 'failed'}, ] } ) self.assertEquals(self.validate.errors, {}) def test_multiple_fields_list_of_validations(self): content = { 'type': 'upload', 'validations': [ {'name': 'file_is_open_document_format', 'message': 'format'}, {'name': 'file_is_less_than_5mb', 'message': 'size'} ] } self.set_question('q0', mock_file('a.pdf', 1), content, value='b.pdf') self.set_question('q1', mock_file('a.txt', 1), content, value='b.pdf') self.set_question('q2', mock_file('a.pdf', 5400001), content, value='b.pdf') self.set_question('q3', mock_file('a.txt', 5400001), content, value='b.pdf') self.assertEquals(self.validate.errors, { 'q1': 'format', 'q2': 'size', 'q3': 'format' }) def test_min_price_specified(self): self.set_question( 'q1', ['1'], { 'type': 'pricing', 'validations': [ {'name': 'no_min_price_specified', 'message': 'failed'}, ] } ) self.assertEquals(self.validate.errors, {}) def test_min_price_not_specified(self): self.set_question( 'q1', [''], { 'type': 'pricing', 'validations': [ {'name': 'no_min_price_specified', 'message': 'failed'}, ] } ) self.assertEquals(self.validate.errors, {'q1': 'failed'}) def test_prices_are_numbers(self): self.set_question( 'q1', ['1', '2'], { 'type': 'pricing', 'validations': [ {'name': 'min_price_not_a_number', 'message': 'failed'}, {'name': 'max_price_not_a_number', 'message': 'failed'}, ] } ) self.assertEquals(self.validate.errors, {}) def test_min_price_isnt_a_number(self): self.set_question( 'q1', ['£1', '2'], { 'type': 'pricing', 'validations': [ {'name': 'min_price_not_a_number', 'message': 'failed'}, {'name': 'max_price_not_a_number', 'message': 'failed'}, ] } ) self.assertEquals(self.validate.errors, {'q1': 'failed'}) def test_max_price_isnt_a_number(self): self.set_question( 'q1', ['1', '£2'], { 'type': 'pricing', 'validations': [ {'name': 'min_price_not_a_number', 'message': 'failed'}, {'name': 'max_price_not_a_number', 'message': 'failed'}, ] } ) self.assertEquals(self.validate.errors, {'q1': 'failed'}) def test_max_price_is_less_than_min(self): self.set_question( 'q1', ['2', '1'], { 'type': 'pricing', 'validations': [ {'name': 'max_less_than_min', 'message': 'failed'} ] } ) self.assertEquals(self.validate.errors, {'q1': 'failed'}) def test_max_price_is_greater_than_min(self): self.set_question( 'q1', ['1', '2'], { 'type': 'pricing', 'validations': [ {'name': 'max_less_than_min', 'message': 'failed'} ] } ) self.assertEquals(self.validate.errors, {}) def test_max_price_is_empty(self): self.set_question( 'q1', ['1', ''], { 'type': 'pricing', 'validations': [ {'name': 'max_less_than_min', 'message': 'failed'} ] } ) self.assertEquals(self.validate.errors, {}) def test_price_unit_is_missing(self): self.set_question( 'q1', ['1', '2', ''], { 'type': 'pricing', 'validations': [ {'name': 'no_unit_specified', 'message': 'failed'} ] } ) self.assertEquals(self.validate.errors, {'q1': 'failed'}) def test_price_unit_is_present(self): self.set_question( 'q1', ['1', '2', 'Virtual machine'], { 'type': 'pricing', 'validations': [ {'name': 'no_unit_specified', 'message': 'failed'} ] } ) self.assertEquals(self.validate.errors, {}) def test_price_string_can_be_composed(self): self.set_question( 'q2', ['1', '2', 'Instance', 'Year'], { 'type': 'pricing', 'validations': [ { 'name': 'price_string_can_be_composed', 'message': 'failed' } ] } ) self.assertEquals(self.validate.errors, {}) self.assertEquals( self.validate.clean_data.get('priceString'), u'£1 to £2 per instance per year' ) def test_string_over_100_characters(self): self.set_question( 'q1', " ".join('a' for i in range(0, 101)), { 'type': 'text', 'validations': [ { 'name': 'under_100_characters', 'message': 'failed' } ] } ) self.assertEquals(self.validate.errors, {'q1': 'failed'}) def test_string_under_100_characters(self): self.set_question( 'q1', "".join('a' for i in range(0, 100)), { 'type': 'text', 'validations': [ { 'name': 'under_100_characters', 'message': 'failed' } ] } ) self.assertEquals(self.validate.errors, {}) def test_string_over_50_words(self): self.set_question( 'q1', " ".join(str(i) for i in range(0, 51)), { 'type': 'text', 'validations': [ { 'name': 'under_50_words', 'message': 'failed' } ] } ) self.assertEquals(self.validate.errors, {'q1': 'failed'}) def test_string_under_50_words(self): self.set_question( 'q1', " ".join(str(i) for i in range(0, 50)), { 'type': 'text', 'validations': [ { 'name': 'under_50_words', 'message': 'failed' } ] } ) self.assertEquals(self.validate.errors, {}) def test_list_with_1_item(self): self.set_question( 'q1', ["1"], { 'type': 'list', 'validations': [ { 'name': 'under_10_items', 'message': 'failed' } ] } ) self.assertEquals(self.validate.errors, {}) def test_list_with_10_items(self): self.set_question( 'q1', ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"], { 'type': 'list', 'validations': [ { 'name': 'under_10_items', 'message': 'failed' } ] } ) self.assertEquals(self.validate.errors, {}) def test_list_with_11_items(self): self.set_question( 'q1', ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"], { 'type': 'list', 'validations': [ { 'name': 'under_10_items', 'message': 'failed' } ] } ) self.assertEquals(self.validate.errors, {'q1': 'failed'}) def test_list_with_empty_items(self): self.set_question( 'q1', [ "something", "", "something", "", "something" ], { 'type': 'list', 'validations': [ { 'name': 'under_10_items', 'message': 'failed' } ] } ) errors = self.validate.errors self.assertEquals( self.validate.clean_data['q1'], ["something", "something", "something"] ) def test_list_with_short_items(self): self.set_question( 'q1', [ "one two three four five six seven eight nine ten" for i in range(0, 3) # noqa ], { 'type': 'list', 'validations': [ { 'name': 'items_under_10_words_each', 'message': 'failed' } ] } ) self.assertEquals(self.validate.errors, {}) def test_list_with_long_items(self): self.set_question( 'q1', [ "one two three four five six seven eight nine ten eleven" for i in range(0, 3) # noqa ], { 'type': 'list', 'validations': [ { 'name': 'items_under_10_words_each', 'message': 'failed' } ] } ) self.assertEquals(self.validate.errors, {'q1': 'failed'})