Bug - SMTP mailto:// Notification content-type (HTML/Text) fix and add CI tests (#1660)
parent
2f777ea3bb
commit
37ff5f6d37
@ -1,2 +1,18 @@
|
|||||||
.git
|
.git
|
||||||
.github
|
.github
|
||||||
|
changedetectionio/processors/__pycache__
|
||||||
|
changedetectionio/api/__pycache__
|
||||||
|
changedetectionio/model/__pycache__
|
||||||
|
changedetectionio/blueprint/price_data_follower/__pycache__
|
||||||
|
changedetectionio/blueprint/tags/__pycache__
|
||||||
|
changedetectionio/blueprint/__pycache__
|
||||||
|
changedetectionio/blueprint/browser_steps/__pycache__
|
||||||
|
changedetectionio/fetchers/__pycache__
|
||||||
|
changedetectionio/tests/visualselector/__pycache__
|
||||||
|
changedetectionio/tests/restock/__pycache__
|
||||||
|
changedetectionio/tests/__pycache__
|
||||||
|
changedetectionio/tests/fetchers/__pycache__
|
||||||
|
changedetectionio/tests/unit/__pycache__
|
||||||
|
changedetectionio/tests/proxy_list/__pycache__
|
||||||
|
changedetectionio/__pycache__
|
||||||
|
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
import smtpd
|
||||||
|
import asyncore
|
||||||
|
|
||||||
|
# Accept a SMTP message and offer a way to retrieve the last message via TCP Socket
|
||||||
|
|
||||||
|
last_received_message = b"Nothing"
|
||||||
|
|
||||||
|
|
||||||
|
class CustomSMTPServer(smtpd.SMTPServer):
|
||||||
|
|
||||||
|
def process_message(self, peer, mailfrom, rcpttos, data, **kwargs):
|
||||||
|
global last_received_message
|
||||||
|
last_received_message = data
|
||||||
|
print('Receiving message from:', peer)
|
||||||
|
print('Message addressed from:', mailfrom)
|
||||||
|
print('Message addressed to :', rcpttos)
|
||||||
|
print('Message length :', len(data))
|
||||||
|
print(data.decode('utf8'))
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
# Just print out the last message received on plain TCP socket server
|
||||||
|
class EchoServer(asyncore.dispatcher):
|
||||||
|
|
||||||
|
def __init__(self, host, port):
|
||||||
|
asyncore.dispatcher.__init__(self)
|
||||||
|
self.create_socket()
|
||||||
|
self.set_reuse_addr()
|
||||||
|
self.bind((host, port))
|
||||||
|
self.listen(5)
|
||||||
|
|
||||||
|
def handle_accepted(self, sock, addr):
|
||||||
|
global last_received_message
|
||||||
|
print('Incoming connection from %s' % repr(addr))
|
||||||
|
sock.send(last_received_message)
|
||||||
|
last_received_message = b''
|
||||||
|
|
||||||
|
|
||||||
|
server = CustomSMTPServer(('0.0.0.0', 11025), None) # SMTP mail goes here
|
||||||
|
server2 = EchoServer('0.0.0.0', 11080) # Echo back last message received
|
||||||
|
asyncore.loop()
|
@ -0,0 +1,165 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import re
|
||||||
|
from flask import url_for
|
||||||
|
from changedetectionio.tests.util import set_original_response, set_modified_response, set_more_modified_response, live_server_setup, \
|
||||||
|
wait_for_all_checks, \
|
||||||
|
set_longer_modified_response
|
||||||
|
from changedetectionio.tests.util import extract_UUID_from_client
|
||||||
|
import logging
|
||||||
|
import base64
|
||||||
|
|
||||||
|
# NOTE - RELIES ON mailserver as hostname running, see github build recipes
|
||||||
|
smtp_test_server = 'mailserver'
|
||||||
|
|
||||||
|
from changedetectionio.notification import (
|
||||||
|
default_notification_body,
|
||||||
|
default_notification_format,
|
||||||
|
default_notification_title,
|
||||||
|
valid_notification_formats,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_setup(live_server):
|
||||||
|
live_server_setup(live_server)
|
||||||
|
|
||||||
|
def get_last_message_from_smtp_server():
|
||||||
|
import socket
|
||||||
|
global smtp_test_server
|
||||||
|
port = 11080 # socket server port number
|
||||||
|
|
||||||
|
client_socket = socket.socket() # instantiate
|
||||||
|
client_socket.connect((smtp_test_server, port)) # connect to the server
|
||||||
|
|
||||||
|
data = client_socket.recv(50024).decode() # receive response
|
||||||
|
client_socket.close() # close the connection
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
# Requires running the test SMTP server
|
||||||
|
|
||||||
|
def test_check_notification_email_formats_default_HTML(client, live_server):
|
||||||
|
# live_server_setup(live_server)
|
||||||
|
set_original_response()
|
||||||
|
|
||||||
|
global smtp_test_server
|
||||||
|
notification_url = f'mailto://changedetection@{smtp_test_server}:11025/?to=fff@home.com'
|
||||||
|
|
||||||
|
#####################
|
||||||
|
# Set this up for when we remove the notification from the watch, it should fallback with these details
|
||||||
|
res = client.post(
|
||||||
|
url_for("settings_page"),
|
||||||
|
data={"application-notification_urls": notification_url,
|
||||||
|
"application-notification_title": "fallback-title " + default_notification_title,
|
||||||
|
"application-notification_body": "fallback-body<br> " + default_notification_body,
|
||||||
|
"application-notification_format": 'HTML',
|
||||||
|
"requests-time_between_check-minutes": 180,
|
||||||
|
'application-fetch_backend': "html_requests"},
|
||||||
|
follow_redirects=True
|
||||||
|
)
|
||||||
|
assert b"Settings updated." in res.data
|
||||||
|
|
||||||
|
# Add a watch and trigger a HTTP POST
|
||||||
|
test_url = url_for('test_endpoint', _external=True)
|
||||||
|
res = client.post(
|
||||||
|
url_for("form_quick_watch_add"),
|
||||||
|
data={"url": test_url, "tags": 'nice one'},
|
||||||
|
follow_redirects=True
|
||||||
|
)
|
||||||
|
|
||||||
|
assert b"Watch added" in res.data
|
||||||
|
|
||||||
|
wait_for_all_checks(client)
|
||||||
|
set_longer_modified_response()
|
||||||
|
client.get(url_for("form_watch_checknow"), follow_redirects=True)
|
||||||
|
wait_for_all_checks(client)
|
||||||
|
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
msg = get_last_message_from_smtp_server()
|
||||||
|
assert len(msg) >= 1
|
||||||
|
|
||||||
|
# The email should have two bodies, and the text/html part should be <br>
|
||||||
|
assert 'Content-Type: text/plain' in msg
|
||||||
|
assert '(added) So let\'s see what happens.\n' in msg # The plaintext part with \n
|
||||||
|
assert 'Content-Type: text/html' in msg
|
||||||
|
assert '(added) So let\'s see what happens.<br>' in msg # the html part
|
||||||
|
res = client.get(url_for("form_delete", uuid="all"), follow_redirects=True)
|
||||||
|
assert b'Deleted' in res.data
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_notification_email_formats_default_Text_override_HTML(client, live_server):
|
||||||
|
# live_server_setup(live_server)
|
||||||
|
|
||||||
|
# HTML problems? see this
|
||||||
|
# https://github.com/caronc/apprise/issues/633
|
||||||
|
|
||||||
|
set_original_response()
|
||||||
|
global smtp_test_server
|
||||||
|
notification_url = f'mailto://changedetection@{smtp_test_server}:11025/?to=fff@home.com'
|
||||||
|
|
||||||
|
#####################
|
||||||
|
# Set this up for when we remove the notification from the watch, it should fallback with these details
|
||||||
|
res = client.post(
|
||||||
|
url_for("settings_page"),
|
||||||
|
data={"application-notification_urls": notification_url,
|
||||||
|
"application-notification_title": "fallback-title " + default_notification_title,
|
||||||
|
"application-notification_body": default_notification_body,
|
||||||
|
"application-notification_format": 'Text',
|
||||||
|
"requests-time_between_check-minutes": 180,
|
||||||
|
'application-fetch_backend': "html_requests"},
|
||||||
|
follow_redirects=True
|
||||||
|
)
|
||||||
|
assert b"Settings updated." in res.data
|
||||||
|
|
||||||
|
# Add a watch and trigger a HTTP POST
|
||||||
|
test_url = url_for('test_endpoint', _external=True)
|
||||||
|
res = client.post(
|
||||||
|
url_for("form_quick_watch_add"),
|
||||||
|
data={"url": test_url, "tags": 'nice one'},
|
||||||
|
follow_redirects=True
|
||||||
|
)
|
||||||
|
|
||||||
|
assert b"Watch added" in res.data
|
||||||
|
|
||||||
|
wait_for_all_checks(client)
|
||||||
|
set_longer_modified_response()
|
||||||
|
client.get(url_for("form_watch_checknow"), follow_redirects=True)
|
||||||
|
wait_for_all_checks(client)
|
||||||
|
|
||||||
|
time.sleep(3)
|
||||||
|
msg = get_last_message_from_smtp_server()
|
||||||
|
assert len(msg) >= 1
|
||||||
|
# with open('/tmp/m.txt', 'w') as f:
|
||||||
|
# f.write(msg)
|
||||||
|
|
||||||
|
# The email should not have two bodies, should be TEXT only
|
||||||
|
|
||||||
|
assert 'Content-Type: text/plain' in msg
|
||||||
|
assert '(added) So let\'s see what happens.\n' in msg # The plaintext part with \n
|
||||||
|
|
||||||
|
set_original_response()
|
||||||
|
# Now override as HTML format
|
||||||
|
res = client.post(
|
||||||
|
url_for("edit_page", uuid="first"),
|
||||||
|
data={
|
||||||
|
"url": test_url,
|
||||||
|
"notification_format": 'HTML',
|
||||||
|
'fetch_backend': "html_requests"},
|
||||||
|
follow_redirects=True
|
||||||
|
)
|
||||||
|
assert b"Updated watch." in res.data
|
||||||
|
wait_for_all_checks(client)
|
||||||
|
|
||||||
|
time.sleep(3)
|
||||||
|
msg = get_last_message_from_smtp_server()
|
||||||
|
assert len(msg) >= 1
|
||||||
|
|
||||||
|
# The email should have two bodies, and the text/html part should be <br>
|
||||||
|
assert 'Content-Type: text/plain' in msg
|
||||||
|
assert '(removed) So let\'s see what happens.\n' in msg # The plaintext part with \n
|
||||||
|
assert 'Content-Type: text/html' in msg
|
||||||
|
assert '(removed) So let\'s see what happens.<br>' in msg # the html part
|
||||||
|
|
||||||
|
res = client.get(url_for("form_delete", uuid="all"), follow_redirects=True)
|
||||||
|
assert b'Deleted' in res.data
|
Loading…
Reference in new issue