async def test_inline_attachment(client: AsyncClient, aws: DummyServer): ses = SesClient(client, SesConfig('test_access_key', 'test_secret_key', 'testing-region-1')) await ses.send_email( '*****@*****.**', 'the subject', ['*****@*****.**'], 'this is a test email', html_body='this is the <b>html body</b>.', attachments=[SesAttachment(file=b'some attachment', name='foobar.txt', content_id='<testing-content-id>')], ) assert len(aws.app['emails']) == 1 assert aws.app['emails'][0]['email']['payload'] == [ { 'Content-Type': 'text/plain', 'payload': 'this is a test email\n', }, { 'Content-Type': 'text/html', 'payload': 'this is the <b>html body</b>.\n', }, { 'Content-Type': 'text/plain', 'payload': 'some attachment', 'Content-Disposition': 'inline; filename="foobar.txt"', 'Content-ID': '<testing-content-id>', }, ]
async def test_send(client: AsyncClient, aws: DummyServer): ses = SesClient(client, SesConfig('test_access_key', 'test_secret_key', 'testing-region-1')) message_id = await ses.send_email( '*****@*****.**', 'test email', ['*****@*****.**'], 'this is a test email', html_body='This is a <b>test</b> email.', ) assert message_id == '123-message-id' assert len(aws.app['emails']) == 1 eml = aws.app['emails'][0] assert eml['body'] == { 'Action': 'SendRawEmail', 'Source': '*****@*****.**', 'RawMessage.Data': RegexStr('.+'), 'Destination.ToAddresses.member.1': '*****@*****.**', } assert eml['email'] == { 'Subject': 'test email', 'From': '*****@*****.**', 'MIME-Version': '1.0', 'To': '*****@*****.**', 'payload': [ {'Content-Type': 'text/plain', 'payload': 'this is a test email\n'}, {'Content-Type': 'text/html', 'payload': 'This is a <b>test</b> email.\n'}, ], }
async def test_custom_headers(client: AsyncClient, aws: DummyServer): ses = SesClient(client, SesConfig('test_access_key', 'test_secret_key', 'testing-region-1')) await ses.send_email( '*****@*****.**', 'test email', ['*****@*****.**'], 'this is a test email', unsubscribe_link='https://example.com/unsubscribe', configuration_set='testing-set', message_tags={'foo': 'bar', 'another': 1}, smtp_headers={'Spam': 'Cake'}, ) assert len(aws.app['emails']) == 1 eml = aws.app['emails'][0] assert eml['email'] == { 'Subject': 'test email', 'From': '*****@*****.**', 'To': '*****@*****.**', 'List-Unsubscribe': '<https://example.com/unsubscribe>', 'X-SES-CONFIGURATION-SET': 'testing-set', 'X-SES-MESSAGE-TAGS': 'foo=bar, another=1', 'Content-Transfer-Encoding': '7bit', 'MIME-Version': '1.0', 'Spam': 'Cake', 'payload': [{'Content-Type': 'text/plain', 'payload': 'this is a test email\n'}], }
async def test_attachment_path(client: AsyncClient, aws: DummyServer, tmp_path): ses = SesClient(client, SesConfig('test_access_key', 'test_secret_key', 'testing-region-1')) p = tmp_path / 'testing.txt' p.write_text('hello') await ses.send_email( '*****@*****.**', 'test with attachment', ['*****@*****.**'], html_body='<b>html body</b>', attachments=[SesAttachment(file=p)], ) assert len(aws.app['emails']) == 1 email = aws.app['emails'][0]['email'] assert email == { 'Subject': 'test with attachment', 'From': '*****@*****.**', 'To': '*****@*****.**', 'MIME-Version': '1.0', 'payload': [ {'Content-Type': 'text/plain', 'payload': '\n'}, {'Content-Type': 'text/html', 'payload': '<b>html body</b>\n'}, { 'Content-Type': 'text/plain', 'payload': 'hello', 'Content-Disposition': 'attachment; filename="testing.txt"', }, ], }
async def test_send_email_attachment(client: AsyncClient, aws: DummyServer): ses = SesClient(client, SesConfig('test_access_key', 'test_secret_key', 'testing-region-1')) await ses.send_email( '*****@*****.**', 'test with attachment £££ more', ['*****@*****.**'], 'this is a test email', attachments=[SesAttachment(file=b'some binary data', name='testing.txt', mime_type='text/plain')], ) assert len(aws.app['emails']) == 1 eml = aws.app['emails'][0] assert eml['email'] == { 'Subject': 'test with attachment £££ more', 'From': '*****@*****.**', 'MIME-Version': '1.0', 'To': '*****@*****.**', 'payload': [ {'Content-Type': 'text/plain', 'payload': 'this is a test email\n'}, { 'Content-Type': 'text/plain', 'payload': 'some binary data', 'Content-Disposition': 'attachment; filename="testing.txt"', }, ], }
async def test_no_recipients(client: AsyncClient, aws: DummyServer): ses = SesClient( client, SesConfig('test_access_key', 'test_secret_key', 'testing-region-1')) with pytest.raises(TypeError, match='either "to", "cc", or "bcc" must be provided'): await ses.send_email('*****@*****.**', 'test email', None, 'xx') assert aws.log == []
async def test_real_send(real_aws: AWS): async with AsyncClient() as client: ses = SesClient(client, SesConfig(real_aws.access_key, real_aws.secret_key, 'eu-west-1')) message_id = await ses.send_email( '*****@*****.**', 'test email', [SesRecipient('*****@*****.**', 'Test', 'Person')], 'this is a test email', html_body='This is a <b>test</b> email.', ) assert len(message_id) > 20
async def test_encoded_unsub(client: AsyncClient, aws: DummyServer): ses = SesClient(client, SesConfig('test_access_key', 'test_secret_key', 'testing-region-1')) unsub_link = 'https://www.example.com/unsubscrible?blob=?blob=$MzMgMTYwMTY3MDEyOCBMMzcbN_nhcDZNg-6D==' await ses.send_email( '*****@*****.**', 'test email', ['*****@*****.**'], 'this is a test email', unsubscribe_link=unsub_link, ) email = aws.app['emails'][0]['email'] assert email['List-Unsubscribe'] == f'<{unsub_link}>'
async def test_send_names(client: AsyncClient, aws: DummyServer): ses = SesClient( client, SesConfig('test_access_key', 'test_secret_key', 'testing-region-1')) await ses.send_email( '*****@*****.**', 'test email', [SesRecipient('*****@*****.**', 'John', 'Doe')], 'this is a test email', cc=[ SesRecipient('*****@*****.**'), SesRecipient('*****@*****.**', 'CC2'), SesRecipient('*****@*****.**', None, 'CC3'), SesRecipient('*****@*****.**', 'Anna, Bob', 'CC4'), ], bcc=['*****@*****.**'], ) assert len(aws.app['emails']) == 1 eml = aws.app['emails'][0] assert eml['body'] == { 'Action': 'SendRawEmail', 'Source': '*****@*****.**', 'RawMessage.Data': RegexStr('.+'), 'Destination.ToAddresses.member.1': '*****@*****.**', 'Destination.CcAddresses.member.1': '*****@*****.**', 'Destination.CcAddresses.member.2': '*****@*****.**', 'Destination.CcAddresses.member.3': '*****@*****.**', 'Destination.CcAddresses.member.4': '*****@*****.**', 'Destination.BccAddresses.member.1': '*****@*****.**', } assert eml['email'] == { 'Subject': 'test email', 'From': '*****@*****.**', 'Content-Transfer-Encoding': '7bit', 'MIME-Version': '1.0', 'To': 'John Doe <*****@*****.**>', 'Cc': '[email protected], CC2 <*****@*****.**>, CC3 <*****@*****.**>,\n "Anna, Bob CC4" <*****@*****.**>', 'Bcc': '*****@*****.**', 'payload': [{ 'Content-Type': 'text/plain', 'payload': 'this is a test email\n' }], }
async def test_send_email_attachment(client: AsyncClient, aws: DummyServer): ses = SesClient(client, SesConfig('test_access_key', 'test_secret_key', 'testing-region-1')) await ses.send_email( '*****@*****.**', 'test with attachment £££ more', ['*****@*****.**'], 'this is a test email', attachments=[SesAttachment(file=b'some binary data', name='testing.txt', mime_type='text/plain')], ) assert len(aws.app['emails']) == 1 eml = aws.app['emails'][0] assert eml['email'] == { 'Subject': 'test with attachment £££ more', 'From': '*****@*****.**', 'MIME-Version': '1.0', 'To': '*****@*****.**', 'payload': [ {'Content-Type': 'text/plain', 'payload': 'this is a test email\n'}, { 'Content-Type': 'text/plain', 'payload': 'some binary data', 'Content-Disposition': 'attachment; filename="testing.txt"', }, ], } raw_body = base64.b64decode(eml['body']['RawMessage.Data'].encode()) assert re.fullmatch( ( br'Subject: test with attachment =\?utf-8\?b\?wqPCo8Kj\?= more\n' br'From: testing@sender\.com\n' br'To: testing@recipient\.com\n' br'MIME-Version: 1\.0\n' br'Content-Type: multipart/mixed; boundary="===============(\d+)=="\n\n' br'--===============\1==\n' br'Content-Type: text/plain; charset="utf-8"\n' br'Content-Transfer-Encoding: 7bit\n\n' br'this is a test email\n\n' br'--===============(\d+)==\n' br'Content-Type: text/plain\n' br'MIME-Version: 1\.0\n' br'Content-Transfer-Encoding: base64\n' br'Content-Disposition: attachment; filename="testing\.txt"\n\n' br'c29tZSBiaW5hcnkgZGF0YQ==\n\n' br'--===============\2==--\n' ), raw_body, )
async def test_attachment_email_with_html(client: AsyncClient, aws: DummyServer): ses = SesClient(client, SesConfig('test_access_key', 'test_secret_key', 'testing-region-1')) await ses.send_email( '*****@*****.**', 'the subject', ['*****@*****.**'], 'this is a test email', html_body='this is the <b>html body</b>.', attachments=[SesAttachment(file=b'some attachment', name='testing.txt', mime_type='application/pdf')], ) assert len(aws.app['emails']) == 1 eml = aws.app['emails'][0] assert eml['email']['Subject'] == 'the subject' raw_body = base64.b64decode(eml['body']['RawMessage.Data'].encode()) assert re.fullmatch( ( br'Subject: the subject\n' br'From: testing@sender\.com\n' br'To: testing@recipient\.com\n' br'MIME-Version: 1\.0\n' br'Content-Type: multipart/mixed; boundary="===============(\d+)=="\n\n' br'--===============\1==\n' br'Content-Type: multipart/alternative;\n' br' boundary="===============(\d+)=="\n\n' br'--===============\2==\n' br'Content-Type: text/plain; charset="utf-8"\n' br'Content-Transfer-Encoding: 7bit\n\n' br'this is a test email\n\n' br'--===============\2==\n' br'Content-Type: text/html; charset="utf-8"\n' br'Content-Transfer-Encoding: 7bit\n' br'MIME-Version: 1\.0\n\n' br'this is the <b>html body</b>\.\n\n' br'--===============\2==--\n\n' br'--===============\1==\n' br'Content-Type: text/plain\n' br'MIME-Version: 1\.0\n' br'Content-Transfer-Encoding: base64\n' br'Content-Disposition: attachment; filename="testing\.txt"\n\n' br'c29tZSBhdHRhY2htZW50\n\n' br'--===============\1==--\n' ), raw_body, )