def test_lettermetadata_unquotes_special_keys():
    metadata = LetterMetadata({
        "filename": "%C2%A3hello",
        "recipient": "%C2%A3hi"
    })
    assert metadata.get("filename") == "£hello"
    assert metadata.get("recipient") == "£hi"
def test_post_upload_letter_shows_letter_preview_for_invalid_file(
        mocker, client_request, fake_uuid):
    letter_template = {
        'template_type': 'letter',
        'reply_to_text': '',
        'postage': 'first',
        'subject': 'hi',
        'content': 'my letter'
    }

    mocker.patch('uuid.uuid4', return_value=fake_uuid)
    mocker.patch('app.main.views.uploads.antivirus_client.scan',
                 return_value=True)
    mocker.patch('app.main.views.uploads.upload_letter_to_s3')
    mock_sanitise_response = Mock()
    mock_sanitise_response.raise_for_status.side_effect = RequestException(
        response=Mock(status_code=400))
    mock_sanitise_response.json = lambda: {
        "message": "template preview error",
        "recipient_address": "The Queen"
    }
    mocker.patch('app.main.views.uploads.sanitise_letter',
                 return_value=mock_sanitise_response)
    mocker.patch(
        'app.main.views.uploads.service_api_client.get_precompiled_template',
        return_value=letter_template)
    mocker.patch('app.main.views.uploads.get_letter_metadata',
                 return_value=LetterMetadata({
                     'filename':
                     'tests/test_pdf_files/one_page_pdf.pdf',
                     'page_count':
                     '1',
                     'status':
                     'invalid',
                     'message':
                     'template-preview-error'
                 }))

    with open('tests/test_pdf_files/one_page_pdf.pdf', 'rb') as file:
        page = client_request.post(
            'main.upload_letter',
            service_id=SERVICE_ONE_ID,
            _data={'file': file},
            _follow_redirects=True,
        )

    assert 'The Queen' not in page.text
    assert len(page.select('.letter-postage')) == 0

    assert page.find(
        "a", {"class": "govuk-back-link"
              })["href"] == "/services/{}/upload-letter".format(SERVICE_ONE_ID)
    assert page.find("label", {"class": "file-upload-button"})

    letter_images = page.select('main img')
    assert len(letter_images) == 1
    assert letter_images[0]['src'] == url_for('.view_letter_upload_as_preview',
                                              service_id=SERVICE_ONE_ID,
                                              file_id=fake_uuid,
                                              page=1)
def test_send_uploaded_letter_when_metadata_states_pdf_is_invalid(
    mocker,
    service_one,
    client_request,
    fake_uuid,
):
    mock_send = mocker.patch(
        'app.main.views.uploads.notification_api_client.send_precompiled_letter'
    )
    mocker.patch('app.main.views.uploads.get_letter_metadata',
                 return_value=LetterMetadata({
                     'filename': 'my_file.pdf',
                     'page_count': '3',
                     'status': 'invalid',
                     'message': 'error',
                     'invalid_pages': '[1]'
                 }))

    service_one['permissions'] = ['letter', 'upload_letters']

    client_request.post('main.send_uploaded_letter',
                        service_id=SERVICE_ONE_ID,
                        file_id=fake_uuid,
                        _data={'filename': 'my_file.pdf'},
                        _expected_status=403)
    assert not mock_send.called
def test_uploaded_letter_preview_does_not_show_send_button_if_service_in_trial_mode(
    mocker,
    client_request,
    fake_uuid,
):
    mocker.patch('app.main.views.uploads.service_api_client')
    mocker.patch('app.main.views.uploads.get_letter_metadata',
                 return_value=LetterMetadata({
                     'filename': 'my_letter.pdf',
                     'page_count': '1',
                     'status': 'valid',
                     'recipient': 'The Queen'
                 }))

    # client_request uses service_one, which is in trial mode
    page = client_request.get(
        'main.uploaded_letter_preview',
        service_id=SERVICE_ONE_ID,
        file_id=fake_uuid,
        original_filename='my_letter.pdf',
        page_count=1,
        status='valid',
        error={},
        _test_page_title=False,
    )

    assert normalize_spaces(
        page.find('h1').text) == 'You cannot send this letter'
    assert page.find('div', class_='letter-sent')
    assert normalize_spaces(
        page.select_one('.js-stick-at-bottom-when-scrolling p').text) == (
            'Recipient: The Queen')
    assert not page.find('form')
    assert len(page.select('main button[type=submit]')) == 0
def test_uploaded_letter_preview(
    mocker,
    active_user_with_permissions,
    service_one,
    client_request,
    fake_uuid,
):
    mocker.patch('app.main.views.uploads.service_api_client')
    mocker.patch('app.main.views.uploads.get_letter_metadata', return_value=LetterMetadata({
        'filename': 'my_encoded_filename%C2%A3.pdf',
        'page_count': '1',
        'status': 'valid',
        'recipient': 'Bugs Bunny%0A123 Big Hole%0DLooney Town'  # 'Bugs Bunny%0A123 Big Hole\rLooney Town' url encoded
    }))

    service_one['restricted'] = False
    client_request.login(active_user_with_permissions, service=service_one)

    page = client_request.get(
        'main.uploaded_letter_preview',
        service_id=SERVICE_ONE_ID,
        file_id=fake_uuid,
    )

    assert page.find('h1').text == 'my_encoded_filename£.pdf'
    assert page.find('div', class_='letter-sent')
    assert not page.find("label", {"class": "file-upload-button"})
    assert page.find('button', {'class': 'page-footer__button', 'type': 'submit'})
def test_post_upload_letter_redirects_for_valid_file(
    mocker,
    active_user_with_permissions,
    service_one,
    client_request,
    fake_uuid,
    extra_permissions,
    expected_allow_international,
):
    mocker.patch('uuid.uuid4', return_value=fake_uuid)
    antivirus_mock = mocker.patch('app.main.views.uploads.antivirus_client.scan', return_value=True)
    mock_sanitise = mocker.patch(
        'app.main.views.uploads.sanitise_letter',
        return_value=Mock(
            content='The sanitised content',
            json=lambda: {'file': 'VGhlIHNhbml0aXNlZCBjb250ZW50', 'recipient_address': 'The Queen'}
        )
    )
    mock_s3 = mocker.patch('app.main.views.uploads.upload_letter_to_s3')
    mocker.patch('app.main.views.uploads.get_letter_metadata', return_value=LetterMetadata({
        'filename': 'tests/test_pdf_files/one_page_pdf.pdf',
        'page_count': '1',
        'status': 'valid',
        'recipient': 'The Queen'
    }))
    mocker.patch('app.main.views.uploads.service_api_client.get_precompiled_template')

    service_one['restricted'] = False
    service_one['permissions'] += extra_permissions
    client_request.login(active_user_with_permissions, service=service_one)

    with open('tests/test_pdf_files/one_page_pdf.pdf', 'rb') as file:
        page = client_request.post(
            'main.upload_letter',
            service_id=SERVICE_ONE_ID,
            _data={'file': file},
            _follow_redirects=True,
        )
    assert antivirus_mock.called

    mock_s3.assert_called_once_with(
        b'The sanitised content',
        file_location='service-{}/{}.pdf'.format(SERVICE_ONE_ID, fake_uuid),
        status='valid',
        page_count=1,
        filename='tests/test_pdf_files/one_page_pdf.pdf',
        recipient='The Queen',
    )
    mock_sanitise.assert_called_once_with(
        ANY,
        allow_international_letters=expected_allow_international,
    )

    assert 'The Queen' in page.find('div', class_='js-stick-at-bottom-when-scrolling').text
    assert page.find('h1').text == 'tests/test_pdf_files/one_page_pdf.pdf'
    assert not page.find(id='validation-error-message')

    assert not page.find('input', {'name': 'file_id'})
    assert normalize_spaces(page.select('main button[type=submit]')[0].text) == 'Send 1 letter'
def test_post_upload_letter_shows_letter_preview_for_valid_file(
    mocker,
    active_user_with_permissions,
    service_one,
    client_request,
    fake_uuid,
):
    letter_template = {'template_type': 'letter',
                       'reply_to_text': '',
                       'postage': 'second',
                       'subject': 'hi',
                       'content': 'my letter'}

    mocker.patch('uuid.uuid4', return_value=fake_uuid)
    mocker.patch('app.main.views.uploads.antivirus_client.scan', return_value=True)
    mocker.patch(
        'app.main.views.uploads.sanitise_letter',
        return_value=Mock(
            content='The sanitised content',
            json=lambda: {'file': 'VGhlIHNhbml0aXNlZCBjb250ZW50', 'recipient_address': 'The Queen'}
        )
    )
    mocker.patch('app.main.views.uploads.upload_letter_to_s3')
    mocker.patch('app.main.views.uploads.pdf_page_count', return_value=3)
    mocker.patch('app.main.views.uploads.get_letter_metadata', return_value=LetterMetadata({
        'filename': 'tests/test_pdf_files/one_page_pdf.pdf',
        'page_count': '3',
        'status': 'valid',
        'recipient': 'The Queen'
    }))
    mocker.patch('app.main.views.uploads.service_api_client.get_precompiled_template', return_value=letter_template)

    service_one['restricted'] = False
    client_request.login(active_user_with_permissions, service=service_one)

    with open('tests/test_pdf_files/one_page_pdf.pdf', 'rb') as file:
        page = client_request.post(
            'main.upload_letter',
            service_id=SERVICE_ONE_ID,
            _data={'file': file},
            _follow_redirects=True,
        )

    assert page.find('h1').text == 'tests/test_pdf_files/one_page_pdf.pdf'
    assert len(page.select('.letter-postage')) == 0
    # Check postage radios exists and second class is checked by default
    assert page.find('input', id="postage-0", value="first")
    assert page.find('input', id="postage-1", value="second").has_attr('checked')

    letter_images = page.select('main img')
    assert len(letter_images) == 3

    for page_no, img in enumerate(letter_images, start=1):
        assert img['src'] == url_for(
            '.view_letter_upload_as_preview',
            service_id=SERVICE_ONE_ID,
            file_id=fake_uuid,
            page=page_no)
def test_upload_international_letter_shows_preview_with_no_choice_of_postage(
    mocker,
    active_user_with_permissions,
    service_one,
    client_request,
    fake_uuid,
):
    letter_template = {
        'template_type': 'letter',
        'reply_to_text': '',
        'postage': 'second',
        'subject': 'hi',
        'content': 'my letter',
    }

    mocker.patch('uuid.uuid4', return_value=fake_uuid)
    mocker.patch('app.main.views.uploads.antivirus_client.scan', return_value=True)
    mocker.patch('app.main.views.uploads.sanitise_letter', return_value=Mock(
        content='The sanitised content',
        json=lambda: {'file': 'VGhlIHNhbml0aXNlZCBjb250ZW50', 'recipient_address': 'The Queen'}
    ))
    mocker.patch('app.main.views.uploads.upload_letter_to_s3')
    mocker.patch('app.main.views.uploads.pdf_page_count', return_value=3)
    mocker.patch('app.main.views.uploads.get_letter_metadata', return_value=LetterMetadata({
        'filename': 'tests/test_pdf_files/one_page_pdf.pdf',
        'page_count': '3',
        'status': 'valid',
        'recipient': (
            '123 Example Street\n'
            'Andorra la Vella\n'
            'Andorra'
        ),
    }))
    mocker.patch('app.main.views.uploads.service_api_client.get_precompiled_template', return_value=letter_template)

    service_one['restricted'] = False
    client_request.login(active_user_with_permissions, service=service_one)

    with open('tests/test_pdf_files/one_page_pdf.pdf', 'rb') as file:
        page = client_request.post(
            'main.upload_letter',
            service_id=SERVICE_ONE_ID,
            _data={'file': file},
            _follow_redirects=True,
        )

    assert page.find('h1').text == 'tests/test_pdf_files/one_page_pdf.pdf'
    assert not page.select('.letter-postage')
    assert not page.select('input[type=radio]')
    assert normalize_spaces(
        page.select_one('.js-stick-at-bottom-when-scrolling').text
    ) == (
        'Recipient: 123 Example Street, Andorra la Vella, Andorra '
        'Postage: international '
        'Send 1 letter'
    )
def test_post_upload_letter_with_invalid_file(mocker, client_request,
                                              fake_uuid):
    mocker.patch('uuid.uuid4', return_value=fake_uuid)
    mocker.patch('app.main.views.uploads.antivirus_client.scan',
                 return_value=True)
    mock_s3 = mocker.patch('app.main.views.uploads.upload_letter_to_s3')

    mock_sanitise_response = Mock()
    mock_sanitise_response.raise_for_status.side_effect = RequestException(
        response=Mock(status_code=400))
    mock_sanitise_response.json = lambda: {
        "message": "content-outside-printable-area",
        "invalid_pages": [1]
    }
    mocker.patch('app.main.views.uploads.sanitise_letter',
                 return_value=mock_sanitise_response)
    mocker.patch(
        'app.main.views.uploads.service_api_client.get_precompiled_template')
    mocker.patch('app.main.views.uploads.get_letter_metadata',
                 return_value=LetterMetadata({
                     'filename': 'tests/test_pdf_files/one_page_pdf.pdf',
                     'page_count': '1',
                     'status': 'invalid',
                     'message': 'content-outside-printable-area',
                     'invalid_pages': '[1]'
                 }))

    with open('tests/test_pdf_files/one_page_pdf.pdf', 'rb') as file:
        file_contents = file.read()
        file.seek(0)

        page = client_request.post('main.upload_letter',
                                   service_id=SERVICE_ONE_ID,
                                   _data={'file': file},
                                   _follow_redirects=True)

        mock_s3.assert_called_once_with(
            file_contents,
            file_location='service-{}/{}.pdf'.format(SERVICE_ONE_ID,
                                                     fake_uuid),
            status='invalid',
            page_count=1,
            filename='tests/test_pdf_files/one_page_pdf.pdf',
            invalid_pages=[1],
            message='content-outside-printable-area')

    assert page.find('div', class_='banner-dangerous').find(
        'h1', {"data-error-type": 'content-outside-printable-area'})
    assert not page.find('button', {
        'class': 'page-footer__button',
        'type': 'submit'
    })
def test_send_uploaded_letter_sends_letter_and_redirects_to_notification_page(
    mocker,
    service_one,
    client_request,
    fake_uuid,
    address,
    post_data,
    expected_postage,
):
    metadata = LetterMetadata({
        'filename': 'my_file.pdf',
        'page_count': '1',
        'status': 'valid',
        'recipient': address,
    })

    mocker.patch('app.main.views.uploads.get_letter_pdf_and_metadata',
                 return_value=('file', metadata))
    mock_send = mocker.patch(
        'app.main.views.uploads.notification_api_client.send_precompiled_letter'
    )
    mocker.patch('app.main.views.uploads.get_letter_metadata',
                 return_value=metadata)

    service_one['permissions'] = ['letter', 'upload_letters']

    client_request.post('main.send_uploaded_letter',
                        service_id=SERVICE_ONE_ID,
                        file_id=fake_uuid,
                        _data=post_data,
                        _expected_redirect=url_for('main.view_notification',
                                                   service_id=SERVICE_ONE_ID,
                                                   notification_id=fake_uuid,
                                                   _external=True))
    mock_send.assert_called_once_with(
        SERVICE_ONE_ID,
        'my_file.pdf',
        fake_uuid,
        expected_postage,
        address,
    )
def test_lettermetadata_gets_non_special_keys():
    metadata = LetterMetadata({"key": "value", "not_key_to_decode": "%C2%A3"})
    assert metadata.get("key") == "value"
    assert metadata.get("other_key") is None
    assert metadata.get("other_key", "default") == "default"
    assert metadata.get("not_key_to_decode") == "%C2%A3"