def test_fallback_language(
            self, mock_requests: 'requests_mock._RequestObjectProxy') -> None:
        """Check that translations fallback to available language."""

        mock_requests.post('https://api.mailjet.com/v3/REST/template',
                           json={
                               'Count': 1,
                               'Data': [{
                                   'ID': 5678
                               }],
                               'Total': 1
                           })
        mock_requests.post(
            'https://api.mailjet.com/v3/REST/template/5678/detailcontent')

        translate_mailjet.main(('--lang', 'en_UK', 'reset-password'))

        create_template_req = mock_requests.request_history[0].json()
        self.assertEqual('DO NOT EDIT reset-password (en_UK)',
                         create_template_req['Name'])
        self.assertEqual('en_GB', create_template_req['Locale'])

        detail_content = mock_requests.request_history[1].json()
        self.assertEqual({'Headers', 'Text-part', 'Html-part', 'MJMLContent'},
                         detail_content.keys())
        self.assertFalse(detail_content['Text-part'])
        self.assertEqual(
            {
                'From': 'Bob Emploi <*****@*****.**>',
                'Reply-To': '',
                'Subject': 'Update your Bob password',
            }, detail_content['Headers'])
    def test_not_translatable(
            self,
            unused_requests: 'requests_mock._RequestObjectProxy') -> None:
        """Error when trying to translate an untranslatable template."""

        with self.assertRaises(ValueError):
            translate_mailjet.main(('--lang', 'en', 'not-translatable'))
    def test_existing(
            self, mock_requests: 'requests_mock._RequestObjectProxy') -> None:
        """Translate all existing i18n templates in one call."""

        expected_requests = [
            mock_requests.get(
                'https://api.mailjet.com/v3/REST/template/123456/detailcontent',
                json={
                    'Count': 1,
                    'Data': [{}],
                    'Total': 1
                }),
            mock_requests.get(
                'https://api.mailjet.com/v3/REST/template/123/detailcontent',
                json={
                    'Count': 1,
                    'Data': [{}],
                    'Total': 1
                }),
            mock_requests.post(
                'https://api.mailjet.com/v3/REST/template/123456/detailcontent'
            ),
            mock_requests.post(
                'https://api.mailjet.com/v3/REST/template/123/detailcontent'),
        ]

        translate_mailjet.main(('existing', ))

        self.assertEqual([True] * 4, [r.called for r in expected_requests])
    def test_space_checks(
            self,
            unused_mock_requests: 'requests_mock._RequestObjectProxy') -> None:
        """Check that translations are checked for whitespaces before being used."""

        client = airtable.Airtable('appkEc8N0Bw4Uok43', '')
        record_id = next(record['id']
                         for record in client.iterate('tblQL7A5EgRJWhQFo')
                         if record['fields']['string'].startswith(
                             'Ce message vous a été envoyé'))
        client.delete('tblQL7A5EgRJWhQFo', record_id)
        client.create(
            'tblQL7A5EgRJWhQFo', {
                'string':
                'Ce message vous a été envoyé à la suite du recueil de vos données sur BOB '
                'EMPLOI. La fréquence des envois est limitée, mais peut être plus importante en '
                'fonction de l’action de l’association. Vous avez la possibilité d’exercer vos droits '
                'd’accès, de rectification et de suppression en écrivant à l’adresse suivante : '
                '[email protected].',
                'en':
                'This message contains a space before an exclamation mark !',
                'fr@tu':
                "Ce message t'a été envoyé",
            })

        with self.assertRaises(ValueError) as error:
            translate_mailjet.main(('--lang', 'en', 'reset-password'))

        self.assertIn('a space before an exclamation mark** **!',
                      str(error.exception) + str(error.exception.__cause__))
    def test_multiple(
            self, mock_requests: 'requests_mock._RequestObjectProxy') -> None:
        """Translate several templates to several languages in one call."""

        create_template_request = mock_requests.post(
            'https://api.mailjet.com/v3/REST/template',
            json={
                'Count': 1,
                'Data': [{
                    'ID': 5678
                }],
                'Total': 1
            })
        update_template_request = mock_requests.post(
            'https://api.mailjet.com/v3/REST/template/5678/detailcontent')

        translate_mailjet.main(('--lang', 'en', '--lang', 'fr@tu',
                                'reset-password', 'reset-password2'))

        self.assertEqual(4, create_template_request.call_count,
                         [(r.url, r.body and r.body[:30])
                          for r in create_template_request.request_history])

        self.assertEqual(4, update_template_request.call_count,
                         [(r.url, r.body and r.body[:30])
                          for r in create_template_request.request_history])
    def test_template_exists_map(
            self, mock_requests: 'requests_mock._RequestObjectProxy') -> None:
        """Trying the tool when the template already exists in the map and has changes."""

        get_template_request = mock_requests.get(
            'https://api.mailjet.com/v3/REST/template/123456/detailcontent',
            json={
                'Count':
                1,
                'Total':
                1,
                'Data': [{
                    'Headers': {},
                    'Text-part': '',
                    'Html-part': '<html/>',
                    'MJMLContent': {},
                }]
            })
        update_template_request = mock_requests.post(
            'https://api.mailjet.com/v3/REST/template/123456/detailcontent')

        translate_mailjet.main(('--lang', 'es', 'reset-password'))

        self.assertTrue(get_template_request.called)
        self.assertTrue(update_template_request.called)
    def test_too_many_requests(
            self, mock_requests: 'requests_mock._RequestObjectProxy') -> None:
        """Mailjet API rate is limiting our ability to get an existing template."""

        get_template_request = mock_requests.get(
            'https://api.mailjet.com/v3/REST/template/123456/detailcontent',
            status_code=429,
            text='Too Many Requests',
        )
        with self.assertRaises(requests.exceptions.HTTPError):
            translate_mailjet.main(('--lang', 'es', 'reset-password'))

        self.assertTrue(get_template_request.called)
    def test_mjml_as_xml(self, mock_requests: requests_mock.Mocker) -> None:
        """Test with a source file where the MJML is in XML format."""

        client = airtable.Airtable('appkEc8N0Bw4Uok43', '')
        for string, en_string in self._english_translations.items():
            client.create('tblQL7A5EgRJWhQFo', {
                'string': string,
                'en': en_string,
            })

        mock_requests.post('https://api.mailjet.com/v3/REST/template',
                           json={
                               'Count': 1,
                               'Data': [{
                                   'ID': 5678
                               }],
                               'Total': 1
                           })
        mock_requests.post(
            'https://api.mailjet.com/v3/REST/template/5678/detailcontent')

        translate_mailjet.main(('--lang', 'en', 'mjml-as-xml'))

        create_template_req = mock_requests.request_history[0].json()
        self.assertEqual('DO NOT EDIT mjml-as-xml (en)',
                         create_template_req['Name'])
        self.assertEqual('en_US', create_template_req['Locale'])

        detail_content = mock_requests.request_history[1].json()
        self.assertEqual({'Headers', 'Text-part', 'Html-part', 'MJMLContent'},
                         detail_content.keys())
        self.assertFalse(detail_content['Text-part'])
        self.assertEqual(
            {
                'From': 'Joanna <*****@*****.**>',
                'Reply-To': '',
                'ReplyEmail': '',
                'SenderName': 'Joanna',
                'SenderEmail': '*****@*****.**',
                'Subject': 'A video that I wanted to show you',
            }, detail_content['Headers'])
        mjml_content = detail_content['MJMLContent']
        self.assertIsInstance(mjml_content, str)
        self.assertRegex(
            mjml_content,
            '<mj-image [^>]* src="https://t.bob-emploi.fr/tplimg/6u2u/b/oirn/2ugx1-english.png"'
        )
        self.assertNotIn("Joanna et l'équipe de Bob", mjml_content)
        self.assertIn('>Joanna and the Bob team<', mjml_content)
    def test_template_exists(
            self, mock_requests: 'requests_mock._RequestObjectProxy') -> None:
        """Trying the tool when the template already exists but is not in the map."""

        mock_requests.post(
            'https://api.mailjet.com/v3/REST/template',
            status_code=400,
            json={
                'ErrorCode': 'ps-0015',
                'StatusCode': 400,
                'ErrorMessage':
                'A template with "name": "reset-password (en)" already exists.',
                'ErrorRelatedTo': ['template'],
            })

        with self.assertRaises(requests.exceptions.HTTPError):
            translate_mailjet.main(('--lang', 'en', 'reset-password'))
    def test_mustache_error_in_translation(
            self, unused_mock_requests: requests_mock.Mocker) -> None:
        """Check that the translation fails if a string translation breaks mustaches."""

        client = airtable.Airtable('appkEc8N0Bw4Uok43', '')
        translations = self._english_translations | {
            # pylint: disable=line-too-long
            '<0>Vous recevez cet email car vous êtes inscrit{1}e{2}{3}{2}·e{4}{4} sur </0><5><6><7><8><7>Bob</7></8></7></6></5><0> et avez demandé des emails de coaching. </0><9><6><7><8><7>Cliquez ici</7></8></7></6></9><0> si vous ne souhaitez plus en recevoir ou moins souvent. Vous avez arrêté de chercher du travail et souhaitez mettre votre compte Bob en veille ? </0><10><6><7><8><7>cliquez ici</7></8></7></6></10><0>.</0>':
            '<0>Vous recevez cet email car vous êtes inscrit{1}e{2}{3}{2}·e{4}</0>',
        }
        for string, en_string in translations.items():
            client.create('tblQL7A5EgRJWhQFo', {
                'string': string,
                'en': en_string,
            })

        with self.assertRaises(ValueError) as error:
            translate_mailjet.main(('--lang', 'en', 'mjml-as-xml'))

        self.assertIn('Missing "endif"', str(error.exception))
    def test_existing_dry_run(
            self, mock_requests: 'requests_mock._RequestObjectProxy',
            mock_logging: mock.MagicMock) -> None:
        """Dry run for translating existing i18n templates."""

        expected_requests = [
            mock_requests.get(
                'https://api.mailjet.com/v3/REST/template/123456/detailcontent',
                json={
                    'Count': 1,
                    'Data': [{}],
                    'Total': 1
                }),
            mock_requests.get(
                'https://api.mailjet.com/v3/REST/template/123/detailcontent',
                json={
                    'Count': 1,
                    'Data': [{}],
                    'Total': 1
                }),
            mock_requests.post(
                'https://api.mailjet.com/v3/REST/template/123456/detailcontent'
            ),
            mock_requests.post(
                'https://api.mailjet.com/v3/REST/template/123/detailcontent'),
        ]

        translate_mailjet.main(('existing', '--dry-run'))

        self.assertEqual([True] * 2 + [False] * 2,
                         [r.called for r in expected_requests])

        mock_logging.assert_any_call('Data would be updated for "%s" in "%s".',
                                     'reset-password', 'es')
        mock_logging.assert_any_call('Data would be updated for "%s" in "%s".',
                                     'reset-password', 'it')
    def test_simple_usage(
            self, mock_requests: 'requests_mock._RequestObjectProxy') -> None:
        """Basic usage of the translate tool."""

        mock_requests.post('https://api.mailjet.com/v3/REST/template',
                           json={
                               'Count': 1,
                               'Data': [{
                                   'ID': 5678
                               }],
                               'Total': 1
                           })
        mock_requests.post(
            'https://api.mailjet.com/v3/REST/template/5678/detailcontent')

        translate_mailjet.main(('--lang', 'en', 'reset-password'))

        create_template_req = mock_requests.request_history[0].json()
        self.assertEqual('DO NOT EDIT reset-password (en)',
                         create_template_req['Name'])
        self.assertEqual('en_US', create_template_req['Locale'])

        detail_content = mock_requests.request_history[1].json()
        self.assertEqual({'Headers', 'Text-part', 'Html-part', 'MJMLContent'},
                         detail_content.keys())
        self.assertFalse(detail_content['Text-part'])
        self.assertEqual(
            {
                'From': 'Bob Emploi <*****@*****.**>',
                'Reply-To': '',
                'Subject': 'Update your Bob password',
            }, detail_content['Headers'])
        self.assertIn(
            '<p style="margin: 10px 0;color:#000000;font-family:Arial; font-size:14.6667px">'
            'Hi\xa0{{var:firstname}},</p><p style="margin: 10px 0;color:#000000;font-family:Arial; '
            'font-size:14.6667px">You have requested a password change, you can now modify it by '
            'clicking on the button below.</p>',
            detail_content['Html-part'],
        )
        self.assertIn(
            '<a href=\"{{var:resetLink}}\" style=\"background:#58BBFB;color:#ffffff;font-family:Ubu'
            'ntu, Helvetica, Arial, sans-serif;font-size:13px;font-weight:normal;line-height:120%;'
            'Margin:0;text-decoration:none;text-transform:none;\" target=\"_blank\">Update the '
            'password</a>',
            detail_content['Html-part'],
        )
        self.assertIn('src="https://t.bob-emploi.fr/en-image.png"',
                      detail_content['Html-part'])
        self.assertIn('a href="https://twitter.com/hellobob"',
                      detail_content['Html-part'])
        self.assertIn(
            '>Update the password<',
            detail_content['MJMLContent'],
        )
        self.assertIn(
            'src="https://t.bob-emploi.fr/en-image.png"',
            detail_content['MJMLContent'],
        )
        self.assertIn(
            'href="https://twitter.com/hellobob"',
            detail_content['MJMLContent'],
        )

        with open(self._mailjet_json_path, 'rt', encoding='utf-8') as map_file:
            map_content = map_file.read()
        self.assertEqual(
            textwrap.dedent('''\
            [
              {
                "i18n": {
                  "en": 5678
                },
                "mailjetTemplate": 71254,
                "name": "reset-password"
              }
            ]
        '''), map_content)