forked from Disroot/gpg-lacre
Implement E2E tests for lacre.daemon
- Add a dedicated configuration file for lacre.daemon. - Implement test/daemon_test.py like test/e2e_test.py, to automate the following procedure: 1. Start up lacre.daemon. 2. For each test case, send test message to the daemon and verify that the output received by test/utils/relay.py contains expected pattern. - Simplify Makefile. - Fix indentation here and there.
This commit is contained in:
parent
a131cd66d3
commit
414f1d5921
4
Makefile
4
Makefile
|
@ -46,9 +46,7 @@ $(TEST_DB):
|
||||||
# Run an e2e test of Advanced Content Filter.
|
# Run an e2e test of Advanced Content Filter.
|
||||||
#
|
#
|
||||||
daemontest:
|
daemontest:
|
||||||
$(PYTHON) test/relay.py 2500
|
$(PYTHON) test/daemon_test.py
|
||||||
PYTHONPATH=`pwd` $(PYTHON) -m lacre.daemon
|
|
||||||
$(PYTHON) test/sendmail.py
|
|
||||||
|
|
||||||
# Before running the crontest goal we need to make sure that the
|
# Before running the crontest goal we need to make sure that the
|
||||||
# database gets regenerated.
|
# database gets regenerated.
|
||||||
|
|
|
@ -17,15 +17,99 @@
|
||||||
# along with gpg-mailgate source code. If not, see <http://www.gnu.org/licenses/>.
|
# along with gpg-mailgate source code. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import configparser
|
||||||
import logging
|
import logging
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
def _spawn(cmd):
|
||||||
|
env_dict = {
|
||||||
|
"PATH": os.getenv("PATH"),
|
||||||
|
"PYTHONPATH": os.getcwd(),
|
||||||
|
"GPG_MAILGATE_CONFIG": "test/gpg-mailgate-daemon-test.conf"
|
||||||
|
}
|
||||||
|
logging.debug(f"Spawning command: {cmd} with environment: {env_dict!r}")
|
||||||
|
return subprocess.Popen(cmd,
|
||||||
|
stdin=None,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
env=env_dict)
|
||||||
|
|
||||||
|
|
||||||
|
def _interrupt(proc):
|
||||||
|
# proc.send_signal(signal.SIGINT)
|
||||||
|
proc.terminate()
|
||||||
|
|
||||||
|
|
||||||
|
def _load(name):
|
||||||
|
logging.debug(f"Loading file {name}")
|
||||||
|
f = open(name, "r")
|
||||||
|
contents = f.read()
|
||||||
|
f.close()
|
||||||
|
return contents
|
||||||
|
|
||||||
|
|
||||||
|
def _send(host, port, mail_from, mail_to, message):
|
||||||
|
logging.debug(f"Sending message to {host}:{port}")
|
||||||
|
_spawn([os.getenv("PYTHON") or "python",
|
||||||
|
"test/utils/sendmail.py",
|
||||||
|
"-f", mail_from,
|
||||||
|
"-t", mail_to,
|
||||||
|
"-m", message])
|
||||||
|
|
||||||
|
|
||||||
|
def _load_test_config():
|
||||||
|
cp = configparser.ConfigParser()
|
||||||
|
cp.read("test/e2e.ini")
|
||||||
|
return cp
|
||||||
|
|
||||||
|
|
||||||
|
def _report_result(message_file, expected, test_output):
|
||||||
|
status = None
|
||||||
|
if expected in test_output:
|
||||||
|
status = "Success"
|
||||||
|
else:
|
||||||
|
status = "Failure"
|
||||||
|
|
||||||
|
print(message_file.ljust(30), status)
|
||||||
|
|
||||||
|
|
||||||
|
def _execute_case(config, case_name):
|
||||||
|
logging.info(f"Executing case {case_name}")
|
||||||
|
python = os.getenv("PYTHON", "python")
|
||||||
|
|
||||||
|
relay_mock = _spawn([python, "test/utils/relay.py", "2500"])
|
||||||
|
time.sleep(1) # Wait for the relay to start up.
|
||||||
|
|
||||||
|
_send("localhost", 10025, "dave@disposlab", "alice@disposlab", "test/msgin/clear2rsa.msg")
|
||||||
|
|
||||||
|
relay_mock.wait()
|
||||||
|
(test_out, _) = relay_mock.communicate()
|
||||||
|
|
||||||
|
test_out = test_out.decode('utf-8')
|
||||||
|
logging.debug(f"Read {len(test_out)} characters of output: '{test_out}'")
|
||||||
|
|
||||||
|
_report_result(config.get(case_name, "in"), config.get(case_name, "out"), test_out)
|
||||||
|
|
||||||
|
|
||||||
def _main():
|
def _main():
|
||||||
|
conf = _load_test_config()
|
||||||
|
|
||||||
logging.basicConfig(filename="test/logs/daemon-test.log",
|
logging.basicConfig(filename="test/logs/daemon-test.log",
|
||||||
format="%(asctime)s %(pathname)s:%(lineno)d %(levelname)s [%(funcName)s] %(message)s",
|
format="%(asctime)s %(pathname)s:%(lineno)d %(levelname)s [%(funcName)s] %(message)s",
|
||||||
datefmt="%Y-%m-%d %H:%M:%S",
|
datefmt="%Y-%m-%d %H:%M:%S",
|
||||||
level=logging.DEBUG)
|
level=logging.DEBUG)
|
||||||
|
|
||||||
logging.info("Starting Lacre Daemon tests...")
|
logging.info("Starting Lacre Daemon tests...")
|
||||||
|
python = os.getenv("PYTHON", "python")
|
||||||
|
|
||||||
|
server = _spawn([python, "-m", "lacre.daemon"])
|
||||||
|
|
||||||
|
for case_no in range(1, conf.getint("tests", "cases")):
|
||||||
|
_execute_case(conf, case_name=f"case-{case_no}")
|
||||||
|
|
||||||
|
_interrupt(server)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -111,17 +111,17 @@ def _execute_e2e_test(case_name, config, config_path):
|
||||||
|
|
||||||
logging.debug(f"Spawning relay: {relay_cmd}")
|
logging.debug(f"Spawning relay: {relay_cmd}")
|
||||||
relay_proc = subprocess.Popen(relay_cmd,
|
relay_proc = subprocess.Popen(relay_cmd,
|
||||||
stdin = None,
|
stdin=None,
|
||||||
stdout = subprocess.PIPE)
|
stdout=subprocess.PIPE)
|
||||||
|
|
||||||
logging.debug(f"Spawning GPG-Lacre: {gpglacre_cmd}, stdin = {config.get(case_name, 'in')}")
|
logging.debug(f"Spawning GPG-Lacre: {gpglacre_cmd}, stdin = {config.get(case_name, 'in')}")
|
||||||
|
|
||||||
# pass PATH because otherwise it would be dropped
|
# pass PATH because otherwise it would be dropped
|
||||||
gpglacre_proc = subprocess.run(gpglacre_cmd,
|
gpglacre_proc = subprocess.run(gpglacre_cmd,
|
||||||
input = _load_file(config.get(case_name, "in")),
|
input=_load_file(config.get(case_name, "in")),
|
||||||
capture_output = True,
|
capture_output=True,
|
||||||
env = {"GPG_MAILGATE_CONFIG": config_path,
|
env={"GPG_MAILGATE_CONFIG": config_path,
|
||||||
"PATH": os.getenv("PATH")})
|
"PATH": os.getenv("PATH")})
|
||||||
|
|
||||||
# Let the relay process the data.
|
# Let the relay process the data.
|
||||||
relay_proc.wait()
|
relay_proc.wait()
|
||||||
|
|
31
test/gpg-mailgate-daemon-test.conf
Normal file
31
test/gpg-mailgate-daemon-test.conf
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
[logging]
|
||||||
|
config = test/gpg-lacre-log.ini
|
||||||
|
file = test/logs/gpg-mailgate.log
|
||||||
|
format = %(asctime)s %(module)s[%(process)d]: %(message)s
|
||||||
|
date_format = ISO
|
||||||
|
|
||||||
|
[gpg]
|
||||||
|
keyhome = test/keyhome
|
||||||
|
|
||||||
|
[smime]
|
||||||
|
cert_path = test/certs
|
||||||
|
|
||||||
|
[database]
|
||||||
|
enabled = yes
|
||||||
|
url = sqlite:///test/lacre.db
|
||||||
|
|
||||||
|
[relay]
|
||||||
|
host = localhost
|
||||||
|
port = 2500
|
||||||
|
|
||||||
|
[daemon]
|
||||||
|
host = localhost
|
||||||
|
port = 10025
|
||||||
|
|
||||||
|
[cron]
|
||||||
|
send_email = no
|
||||||
|
|
||||||
|
[enc_keymap]
|
||||||
|
alice@disposlab = 1CD245308F0963D038E88357973CF4D9387C44D7
|
||||||
|
bob@disposlab = 19CF4B47ECC9C47AFA84D4BD96F39FDA0E31BB67
|
||||||
|
|
|
@ -24,79 +24,79 @@ LAST_LINE = -3
|
||||||
|
|
||||||
|
|
||||||
def welcome(msg):
|
def welcome(msg):
|
||||||
return b"220 %b\r\n" % (msg)
|
return b"220 %b\r\n" % (msg)
|
||||||
|
|
||||||
def ok(msg = b"OK"):
|
def ok(msg = b"OK"):
|
||||||
return b"250 %b\r\n" % (msg)
|
return b"250 %b\r\n" % (msg)
|
||||||
|
|
||||||
def bye():
|
def bye():
|
||||||
return b"251 Bye"
|
return b"251 Bye"
|
||||||
|
|
||||||
def provide_message():
|
def provide_message():
|
||||||
return b"354 Enter a message, ending it with a '.' on a line by itself\r\n"
|
return b"354 Enter a message, ending it with a '.' on a line by itself\r\n"
|
||||||
|
|
||||||
def receive_and_confirm(session):
|
def receive_and_confirm(session):
|
||||||
session.recv(BUFFER_SIZE)
|
session.recv(BUFFER_SIZE)
|
||||||
session.sendall(ok())
|
session.sendall(ok())
|
||||||
|
|
||||||
def localhost_at(port):
|
def localhost_at(port):
|
||||||
return ('127.0.0.1', port)
|
return ('127.0.0.1', port)
|
||||||
|
|
||||||
def serve(port):
|
def serve(port):
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
try:
|
try:
|
||||||
s.bind(localhost_at(port))
|
s.bind(localhost_at(port))
|
||||||
logging.info(f"Listening on localhost, port {port}")
|
logging.info(f"Listening on localhost, port {port}")
|
||||||
s.listen(1)
|
s.listen(1)
|
||||||
logging.info("Listening...")
|
logging.info("Listening...")
|
||||||
except socket.error as e:
|
except socket.error as e:
|
||||||
print("Cannot connect", e)
|
print("Cannot connect", e)
|
||||||
logging.error(f"Cannot connect {e}")
|
logging.error(f"Cannot connect {e}")
|
||||||
sys.exit(EXIT_UNAVAILABLE)
|
sys.exit(EXIT_UNAVAILABLE)
|
||||||
|
|
||||||
logging.debug("About to accept a connection...")
|
logging.debug("About to accept a connection...")
|
||||||
(conn, addr) = s.accept()
|
(conn, addr) = s.accept()
|
||||||
logging.debug(f"Accepting connection from {conn}")
|
logging.debug(f"Accepting connection from {conn}")
|
||||||
conn.sendall(welcome(b"TEST SERVER"))
|
conn.sendall(welcome(b"TEST SERVER"))
|
||||||
|
|
||||||
receive_and_confirm(conn) # Ignore HELO/EHLO
|
receive_and_confirm(conn) # Ignore HELO/EHLO
|
||||||
receive_and_confirm(conn) # Ignore sender address
|
receive_and_confirm(conn) # Ignore sender address
|
||||||
receive_and_confirm(conn) # Ignore recipient address
|
receive_and_confirm(conn) # Ignore recipient address
|
||||||
|
|
||||||
data = conn.recv(BUFFER_SIZE)
|
conn.recv(BUFFER_SIZE)
|
||||||
conn.sendall(provide_message())
|
conn.sendall(provide_message())
|
||||||
|
|
||||||
# Consume until we get <CR><LF>.<CR><LF>, the end-of-message marker.
|
# Consume until we get <CR><LF>.<CR><LF>, the end-of-message marker.
|
||||||
message = ''
|
message = ''
|
||||||
while not message.endswith(EOM):
|
while not message.endswith(EOM):
|
||||||
message += conn.recv(BUFFER_SIZE).decode(ENCODING)
|
message += conn.recv(BUFFER_SIZE).decode(ENCODING)
|
||||||
conn.sendall(ok(b"OK, id=test"))
|
conn.sendall(ok(b"OK, id=test"))
|
||||||
|
|
||||||
conn.recv(BUFFER_SIZE)
|
conn.recv(BUFFER_SIZE)
|
||||||
conn.sendall(bye())
|
conn.sendall(bye())
|
||||||
|
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
logging.debug(f"Received {len(message)} characters of data")
|
logging.debug(f"Received {len(message)} characters of data")
|
||||||
|
|
||||||
# Trim EOM marker as we're only interested in the message body.
|
# Trim EOM marker as we're only interested in the message body.
|
||||||
return message[:-len(EOM)]
|
return message[:-len(EOM)]
|
||||||
|
|
||||||
def error(msg, exit_code):
|
def error(msg, exit_code):
|
||||||
logging.error(msg)
|
logging.error(msg)
|
||||||
print("ERROR: %s" % (msg))
|
print("ERROR: %s" % (msg))
|
||||||
sys.exit(exit_code)
|
sys.exit(exit_code)
|
||||||
|
|
||||||
|
|
||||||
# filename is relative to where we run the tests from, i.e. the project root
|
# filename is relative to where we run the tests from, i.e. the project root
|
||||||
# directory
|
# directory
|
||||||
logging.basicConfig(filename = 'test/logs/relay.log',
|
logging.basicConfig(filename='test/logs/relay.log',
|
||||||
format = '%(asctime)s %(pathname)s:%(lineno)d %(levelname)s [%(funcName)s] %(message)s',
|
format='%(asctime)s %(pathname)s:%(lineno)d %(levelname)s [%(funcName)s] %(message)s',
|
||||||
datefmt = '%Y-%m-%d %H:%M:%S',
|
datefmt='%Y-%m-%d %H:%M:%S',
|
||||||
level = logging.DEBUG)
|
level=logging.DEBUG)
|
||||||
|
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
error("Usage: relay.py PORT_NUMBER", EXIT_UNAVAILABLE)
|
error("Usage: relay.py PORT_NUMBER", EXIT_UNAVAILABLE)
|
||||||
|
|
||||||
port = int(sys.argv[1])
|
port = int(sys.argv[1])
|
||||||
body = serve(port)
|
body = serve(port)
|
||||||
|
|
|
@ -1,27 +1,48 @@
|
||||||
|
import logging
|
||||||
import smtplib
|
import smtplib
|
||||||
import sys
|
import sys
|
||||||
import getopt
|
import getopt
|
||||||
|
|
||||||
|
|
||||||
def _send(host, port, from_addr, recipients, message):
|
def _load_file(name):
|
||||||
smtp = smtplib.SMTP(host, port)
|
f = open(name, 'r')
|
||||||
# smtp.starttls()
|
contents = f.read()
|
||||||
# try:
|
f.close()
|
||||||
# breakpoint()
|
return contents
|
||||||
smtp.sendmail(from_addr, recipients, message)
|
|
||||||
# except smtplib.SMTPDataError as e:
|
|
||||||
# print(f"Couldn't deliver message.\nGot error: {e}\n")
|
|
||||||
|
|
||||||
|
|
||||||
|
def _send(host, port, from_addr, recipients, message):
|
||||||
|
logging.info(f"From {from_addr} to {recipients} at {host}:{port}")
|
||||||
|
try:
|
||||||
|
smtp = smtplib.SMTP(host, port)
|
||||||
|
# smtp.starttls()
|
||||||
|
return smtp.sendmail(from_addr, recipients, message)
|
||||||
|
except smtplib.SMTPDataError as e:
|
||||||
|
logging.error(f"Couldn't deliver message. Got error: {e}")
|
||||||
|
return None
|
||||||
|
except ConnectionRefusedError as e:
|
||||||
|
logging.exception(f"Connection refused: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
logging.basicConfig(filename="test/logs/sendmail.log",
|
||||||
|
format="%(asctime)s %(pathname)s:%(lineno)d %(levelname)s [%(funcName)s] %(message)s",
|
||||||
|
datefmt="%Y-%m-%d %H:%M:%S",
|
||||||
|
level=logging.DEBUG)
|
||||||
|
|
||||||
sender = recipient = message = None
|
sender = recipient = message = None
|
||||||
|
|
||||||
for opt, value in getopt.getopt(sys.argv[1:], "f:t:m:"):
|
opts, _ = getopt.getopt(sys.argv[1:], "f:t:m:")
|
||||||
if opt == "f":
|
for opt, value in opts:
|
||||||
|
if opt == "-f":
|
||||||
sender = value
|
sender = value
|
||||||
if opt == "t":
|
logging.debug(f"Sender is {sender}")
|
||||||
|
if opt == "-t":
|
||||||
recipient = value
|
recipient = value
|
||||||
if opt == "m":
|
logging.debug(f"Recipient is {recipient}")
|
||||||
message = value
|
if opt == "-m":
|
||||||
|
message = _load_file(value)
|
||||||
|
logging.debug(f"Message is {message}")
|
||||||
|
|
||||||
if message is None:
|
if message is None:
|
||||||
message = """\
|
message = """\
|
||||||
|
|
Loading…
Reference in a new issue