From 1024e0ccf28c69ac1b19a09098c3c643749d5ee6 Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Sat, 7 Jan 2023 11:36:20 +1300 Subject: [PATCH] tests: Extract some test cases out from `tests.bats` (#2980) While working on tests, I noticed that some of the configs being mounted were adding a few seconds to the start-up time of each container. Notably `postfix-*` and `dovecot.conf` config files, which have been extracted out into their own tests with those files moved into a separate config folder. `tests.bats` has been adapted to the common setup helper, and removed ENV no longer required to run those tests. Future PRs will extract out more tests. Review may be easier via individual commit diffs and their associated commit messages describing relevant changes.
Commit message history for reference ```markdown tests(chore): `tests.bats` - Remove redundant config === - ONEDIR volume support no longer relevant, this should have been dropped. - ClamAV ENV no longer relevant as related tests have been extracted already. - Same with the some of the SpamAssassin ENV config. - `VIRUSMAILS_DELETE_DELAY` is tested in the file, but doesn't use this ENV at all? (runs a separate instance to test the ENV instead) - Hostname updated in preparation for migrating to new test helpers. Relevant test lines referencing the hostname have likewise been updated. ``` ```markdown tests(chore): `tests.bats` - Convert to common setup === ENV remains the same, but required adding `ENABLE_AMAVIS=1` to bring that back, while the following became redundant as they're now defaulting to explicitly disabled in the helper method: - `ENABLE_CLAMAV=0` - `LOG_LEVEL=debug` - `ENABLE_UPDATE_CHECK=0` - `--hostname` + `--tty` + standard `--volume` lines - `-e` option expanded to long-name `--env`, and all `\` dropped as no longer necessary. `wait_for_finished_setup_in_container` is now redundant thanks to `common_container_setup`. ``` ```markdown tests(refactor): `tests.bats` - Extract out Dovecot Sieve tests === Sieve test files relocated into `test/config/dovecot-sieve/` for better isolation. `dovecot.sieve` was not using the `reject` import, and we should not encourage it? (docs still do): https://support.tigertech.net/sieve#the-sieve-reject-jmp ``` ```markdown tests: `tests.bats` - Extract out `checking smtp` tests === Migrated to the standard template and copied over the original test cases with `_run_in_container` adjustment only. Identified minimum required ENV along with which mail is required for each test case. ``` ```markdown tests(refactor): `smtp-delivery.bats` === - Disabled `ENABLE_SRS=1`, not necessary for these tests. - Added a SpamAssassin related test (X-SPAM headers) which requires `SA_TAG` to properly pass (or `ENABLE_SRS=1` to deliver into inbox). - Many lines with double quotes changed to single quote wrapping, and moving out `grep` filters into `assert_output --partial` lines instead. - Instead of `wc -l` making failures less helpful, switch to the helper method `_should_output_number_of_lines` - x2 `assert_output` with different EOF style of usage was not actually failing on tests when it should. Changed to assert partial output of each expected line, and count the number of lines instead. - Added additional comments related to the test cases with a `TODO` note about `SPAMASSASSIN_SPAM_TO_INBOX=1`. - Revised test case names, including using the common prefix var. - `tests.bats` no longer needs to send all these emails, no other test cases require them. This affects a test checking a `/mail` folder exists which has been corrected, and a quotas test case adjusted to expect an empty quota size output. ``` ```markdown tests: `tests.bats` - Extract out test cases for config overrides === Slight improvement by additionally matching `postconf` output to verify the setting is properly applied. ``` ```markdown tests: `tests.bats` - Extract out Amavis SpamAssassin test case === Removes the need for SpamAssassin ENV in `tests.bats`. ```
--- .../{ => dovecot-sieve}/before.dovecot.sieve | 3 +- test/config/dovecot-sieve/dovecot.sieve | 7 + .../sieve-pipe/pipe_to_tmp | 0 .../user2@otherdomain.tld.dovecot.sieve | 0 test/config/{ => override-configs}/dovecot.cf | 1 - .../{ => override-configs}/postfix-main.cf | 1 - .../{ => override-configs}/postfix-master.cf | 0 test/config/sieve/dovecot.sieve | 8 - test/config/smtp-delivery/dovecot.cf | 1 + test/config/smtp-delivery/postfix-main.cf | 1 + test/helper/common.bash | 5 + .../parallel/set1/spam_virus/amavis.bats | 25 ++ .../tests/parallel/set3/config-overrides.bats | 47 +++ test/tests/parallel/set3/dovecot-sieve.bats | 63 ++++ test/tests/parallel/set3/smtp-delivery.bats | 256 +++++++++++++++ test/tests/serial/tests.bats | 308 ++---------------- 16 files changed, 435 insertions(+), 291 deletions(-) rename test/config/{ => dovecot-sieve}/before.dovecot.sieve (74%) create mode 100644 test/config/dovecot-sieve/dovecot.sieve rename test/config/{ => dovecot-sieve}/sieve-pipe/pipe_to_tmp (100%) rename test/config/{ => dovecot-sieve}/user2@otherdomain.tld.dovecot.sieve (100%) rename test/config/{ => override-configs}/dovecot.cf (57%) rename test/config/{ => override-configs}/postfix-main.cf (78%) rename test/config/{ => override-configs}/postfix-master.cf (100%) delete mode 100644 test/config/sieve/dovecot.sieve create mode 100644 test/config/smtp-delivery/dovecot.cf create mode 100644 test/config/smtp-delivery/postfix-main.cf create mode 100644 test/tests/parallel/set1/spam_virus/amavis.bats create mode 100644 test/tests/parallel/set3/config-overrides.bats create mode 100644 test/tests/parallel/set3/dovecot-sieve.bats create mode 100644 test/tests/parallel/set3/smtp-delivery.bats diff --git a/test/config/before.dovecot.sieve b/test/config/dovecot-sieve/before.dovecot.sieve similarity index 74% rename from test/config/before.dovecot.sieve rename to test/config/dovecot-sieve/before.dovecot.sieve index 188412ba..eed64237 100644 --- a/test/config/before.dovecot.sieve +++ b/test/config/dovecot-sieve/before.dovecot.sieve @@ -1,6 +1,5 @@ require ["fileinto", "copy"]; if address :contains ["From"] "spam@spam.com" { - fileinto :copy "INBOX"; + fileinto :copy "INBOX"; } - diff --git a/test/config/dovecot-sieve/dovecot.sieve b/test/config/dovecot-sieve/dovecot.sieve new file mode 100644 index 00000000..412db33f --- /dev/null +++ b/test/config/dovecot-sieve/dovecot.sieve @@ -0,0 +1,7 @@ +require ["fileinto"]; + +if address :contains ["From"] "spam@spam.com" { + fileinto "INBOX.spam"; +} else { + keep; +} diff --git a/test/config/sieve-pipe/pipe_to_tmp b/test/config/dovecot-sieve/sieve-pipe/pipe_to_tmp similarity index 100% rename from test/config/sieve-pipe/pipe_to_tmp rename to test/config/dovecot-sieve/sieve-pipe/pipe_to_tmp diff --git a/test/config/user2@otherdomain.tld.dovecot.sieve b/test/config/dovecot-sieve/user2@otherdomain.tld.dovecot.sieve similarity index 100% rename from test/config/user2@otherdomain.tld.dovecot.sieve rename to test/config/dovecot-sieve/user2@otherdomain.tld.dovecot.sieve diff --git a/test/config/dovecot.cf b/test/config/override-configs/dovecot.cf similarity index 57% rename from test/config/dovecot.cf rename to test/config/override-configs/dovecot.cf index 47e76619..35279f28 100644 --- a/test/config/dovecot.cf +++ b/test/config/override-configs/dovecot.cf @@ -1,2 +1 @@ mail_max_userip_connections = 69 -recipient_delimiter = ~ diff --git a/test/config/postfix-main.cf b/test/config/override-configs/postfix-main.cf similarity index 78% rename from test/config/postfix-main.cf rename to test/config/override-configs/postfix-main.cf index 38f0bb26..77c2d787 100644 --- a/test/config/postfix-main.cf +++ b/test/config/override-configs/postfix-main.cf @@ -1,4 +1,3 @@ -recipient_delimiter = ~ max_idle = 600s # this is a comment # this is also a comment diff --git a/test/config/postfix-master.cf b/test/config/override-configs/postfix-master.cf similarity index 100% rename from test/config/postfix-master.cf rename to test/config/override-configs/postfix-master.cf diff --git a/test/config/sieve/dovecot.sieve b/test/config/sieve/dovecot.sieve deleted file mode 100644 index cce44e99..00000000 --- a/test/config/sieve/dovecot.sieve +++ /dev/null @@ -1,8 +0,0 @@ -require ["fileinto", "reject"]; - -if address :contains ["From"] "spam@spam.com" { - fileinto "INBOX.spam"; -} else { - keep; -} - diff --git a/test/config/smtp-delivery/dovecot.cf b/test/config/smtp-delivery/dovecot.cf new file mode 100644 index 00000000..b26e77ea --- /dev/null +++ b/test/config/smtp-delivery/dovecot.cf @@ -0,0 +1 @@ +recipient_delimiter = ~ diff --git a/test/config/smtp-delivery/postfix-main.cf b/test/config/smtp-delivery/postfix-main.cf new file mode 100644 index 00000000..b26e77ea --- /dev/null +++ b/test/config/smtp-delivery/postfix-main.cf @@ -0,0 +1 @@ +recipient_delimiter = ~ diff --git a/test/helper/common.bash b/test/helper/common.bash index 26c66137..bed96c38 100644 --- a/test/helper/common.bash +++ b/test/helper/common.bash @@ -240,3 +240,8 @@ function wait_for_empty_mail_queue_in_container() { # shellcheck disable=SC2016 repeat_in_container_until_success_or_timeout "${TIMEOUT}" "${CONTAINER_NAME}" bash -c '[[ $(mailq) == *"Mail queue is empty"* ]]' } + +# `lines` is a special BATS variable updated via `run`: +function _should_output_number_of_lines() { + assert_equal "${#lines[@]}" $1 +} diff --git a/test/tests/parallel/set1/spam_virus/amavis.bats b/test/tests/parallel/set1/spam_virus/amavis.bats new file mode 100644 index 00000000..d2e23a2c --- /dev/null +++ b/test/tests/parallel/set1/spam_virus/amavis.bats @@ -0,0 +1,25 @@ +load "${REPOSITORY_ROOT}/test/helper/common" +load "${REPOSITORY_ROOT}/test/helper/setup" + +TEST_NAME_PREFIX='Amavis:' +CONTAINER_NAME='dms-test_amavis' + +function setup_file() { + init_with_defaults + + local CUSTOM_SETUP_ARGUMENTS=( + --env ENABLE_AMAVIS=1 + --env AMAVIS_LOGLEVEL=2 + --env ENABLE_SPAMASSASSIN=1 + ) + + common_container_setup 'CUSTOM_SETUP_ARGUMENTS' +} + +function teardown_file() { _default_teardown ; } + +@test "${TEST_NAME_PREFIX} Amavis integration should be active" { + _run_in_container grep 'ANTI-SPAM-SA' /var/log/mail/mail.log + assert_output --partial 'loaded' + refute_output --partial 'NOT loaded' +} diff --git a/test/tests/parallel/set3/config-overrides.bats b/test/tests/parallel/set3/config-overrides.bats new file mode 100644 index 00000000..2a42ed55 --- /dev/null +++ b/test/tests/parallel/set3/config-overrides.bats @@ -0,0 +1,47 @@ +load "${REPOSITORY_ROOT}/test/helper/common" +load "${REPOSITORY_ROOT}/test/helper/setup" + +TEST_NAME_PREFIX='Override Configs:' +CONTAINER_NAME='dms-test_config-overrides' + +function setup_file() { + init_with_defaults + + # Move override configs into main `/tmp/docker-mailserver` config location: + mv "${TEST_TMP_CONFIG}/override-configs/"* "${TEST_TMP_CONFIG}/" + + common_container_setup +} + +function teardown_file() { _default_teardown ; } + +@test "${TEST_NAME_PREFIX}: Postfix - 'postfix-main.cf' overrides applied to '/etc/postfix/main.cf'" { + _run_in_container grep -q 'max_idle = 600s' /tmp/docker-mailserver/postfix-main.cf + assert_success + + _run_in_container grep -q 'readme_directory = /tmp' /tmp/docker-mailserver/postfix-main.cf + assert_success + + _run_in_container postconf + assert_success + assert_output --partial 'max_idle = 600s' + assert_output --partial 'readme_directory = /tmp' +} + +@test "${TEST_NAME_PREFIX}: Postfix - 'postfix-master.cf' overrides applied to '/etc/postfix/master.cf'" { + _run_in_container grep -q 'submission/inet/smtpd_sasl_security_options=noanonymous' /tmp/docker-mailserver/postfix-master.cf + assert_success + + _run_in_container postconf -M + assert_success + assert_output --partial '-o smtpd_sasl_security_options=noanonymous' +} + +@test "${TEST_NAME_PREFIX}: Dovecot - 'dovecot.cf' overrides applied to '/etc/dovecot/local.conf'" { + _run_in_container grep -q 'mail_max_userip_connections = 69' /tmp/docker-mailserver/dovecot.cf + assert_success + + _run_in_container doveconf + assert_success + assert_output --partial 'mail_max_userip_connections = 69' +} diff --git a/test/tests/parallel/set3/dovecot-sieve.bats b/test/tests/parallel/set3/dovecot-sieve.bats new file mode 100644 index 00000000..e465e623 --- /dev/null +++ b/test/tests/parallel/set3/dovecot-sieve.bats @@ -0,0 +1,63 @@ +load "${REPOSITORY_ROOT}/test/helper/common" +load "${REPOSITORY_ROOT}/test/helper/setup" + +# Docs: +# https://docker-mailserver.github.io/docker-mailserver/edge/config/advanced/mail-sieve/ + +TEST_NAME_PREFIX='Dovecot [Sieve Support]:' +CONTAINER_NAME='dms-test_dovecot-sieve' + +function setup_file() { + init_with_defaults + + # Move sieve configs into main `/tmp/docker-mailserver` config location: + mv "${TEST_TMP_CONFIG}/dovecot-sieve/"* "${TEST_TMP_CONFIG}/" + + local CONTAINER_ARGS_ENV_CUSTOM=( + --env ENABLE_MANAGESIEVE=1 + # Required for mail delivery via nc: + --env PERMIT_DOCKER=container + # Mount into mail dir for user1 to treat as a user-sieve: + # NOTE: Cannot use ':ro', 'start-mailserver.sh' attempts to 'chown -R' /var/mail: + --volume "${TEST_TMP_CONFIG}/dovecot.sieve:/var/mail/localhost.localdomain/user1/.dovecot.sieve" + ) + common_container_setup 'CONTAINER_ARGS_ENV_CUSTOM' + + wait_for_smtp_port_in_container "${CONTAINER_NAME}" + + # Single mail sent from 'spam@spam.com' that is handled by User (relocate) and Global (copy) sieves for user1: + _run_in_container bash -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/sieve-spam-folder.txt" + # Mail for user2 triggers the sieve-pipe: + _run_in_container bash -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/sieve-pipe.txt" + + wait_for_empty_mail_queue_in_container "${CONTAINER_NAME}" +} + +function teardown_file() { _default_teardown ; } + +# dovecot-sieve/dovecot.sieve +@test "${TEST_NAME_PREFIX} User Sieve - should store mail from 'spam@spam.com' into recipient (user1) mailbox 'INBOX.spam'" { + _run_in_container bash -c 'ls -A /var/mail/localhost.localdomain/user1/.INBOX.spam/new' + assert_success + _should_output_number_of_lines 1 +} + +# dovecot-sieve/before.dovecot.sieve +@test "${TEST_NAME_PREFIX} Global Sieve - should have copied mail from 'spam@spam.com' to recipient (user1) inbox" { + _run_in_container grep 'Spambot ' -R /var/mail/localhost.localdomain/user1/new/ + assert_success +} + +# dovecot-sieve/sieve-pipe + dovecot-sieve/user2@otherdomain.tld.dovecot.sieve +@test "${TEST_NAME_PREFIX} Sieve Pipe - should pipe mail received for user2 into '/tmp/pipe-test.out'" { + _run_in_container bash -c 'ls -A /tmp/pipe-test.out' + assert_success + _should_output_number_of_lines 1 +} + +# Only test coverage for feature is to check that the service is listening on the expected port: +# https://doc.dovecot.org/admin_manual/pigeonhole_managesieve_server/ +@test "${TEST_NAME_PREFIX} ENV 'ENABLE_MANAGESIEVE' - should have enabled service on port 4190" { + _run_in_container bash -c 'nc -z 0.0.0.0 4190' + assert_success +} diff --git a/test/tests/parallel/set3/smtp-delivery.bats b/test/tests/parallel/set3/smtp-delivery.bats new file mode 100644 index 00000000..bea3127d --- /dev/null +++ b/test/tests/parallel/set3/smtp-delivery.bats @@ -0,0 +1,256 @@ +load "${REPOSITORY_ROOT}/test/helper/common" +load "${REPOSITORY_ROOT}/test/helper/setup" + +TEST_NAME_PREFIX='SMTP Delivery:' +CONTAINER_NAME='dms-test_smtp-delivery' + +function setup_file() { + init_with_defaults + + local CONTAINER_ARGS_ENV_CUSTOM=( + # Required not only for authentication, but delivery in these tests (via nc): + # TODO: Properly test with DNS records configured and separate container for + # handling delivery (without nc). This would remove the need for this ENV: + --env PERMIT_DOCKER=container + # NOTE: Authentication is rejected due to default POSTSCREEN_ACTION=enforce and PERMIT_DOCKER=none + # Non-issue when PERMIT_DOCKER is not the default `none` for these nc 0.0.0.0 tests: + # --env POSTSCREEN_ACTION=ignore + + # Required for test 'rejects spam': + --env ENABLE_SPAMASSASSIN=1 + --env SPAMASSASSIN_SPAM_TO_INBOX=0 + # Either SA_TAG or ENABLE_SRS=1 will pass the spamassassin X-SPAM headers test case: + --env SA_TAG=-5.0 + + # Only relevant for tests expecting to match `external.tld=`?: + # NOTE: Disabling support in tests as it doesn't seem relevant to the test, but misleading.. + # `spam@external.tld` and `user@external.tld` are delivered with with the domain-part changed to `example.test` + # https://github.com/roehling/postsrsd + # --env ENABLE_SRS=1 + # Required for ENABLE_SRS=1: + # --ulimit "nofile=$(ulimit -Sn):$(ulimit -Hn)" + + # Required for tests: 'redirects mail to external aliases' + 'rejects spam': + --env ENABLE_AMAVIS=1 + + # TODO: Relocate relevant tests to the separated clamav test file: + # Originally relevant, but tests expecting ClamAV weren't properly implemented and didn't raise a failure. + # --env ENABLE_CLAMAV=1 + ) + + # Required for 'delivers mail to existing alias with recipient delimiter': + mv "${TEST_TMP_CONFIG}/smtp-delivery/postfix-main.cf" "${TEST_TMP_CONFIG}/postfix-main.cf" + mv "${TEST_TMP_CONFIG}/smtp-delivery/dovecot.cf" "${TEST_TMP_CONFIG}/dovecot.cf" + + common_container_setup 'CONTAINER_ARGS_ENV_CUSTOM' + + _run_in_container setup email add 'added@localhost.localdomain' 'mypassword' + assert_success + wait_until_change_detection_event_completes "${CONTAINER_NAME}" + + wait_for_smtp_port_in_container "${CONTAINER_NAME}" + + # TODO: Move to clamav tests (For use when ClamAV is enabled): + # repeat_in_container_until_success_or_timeout 60 "${CONTAINER_NAME}" test -e /var/run/clamav/clamd.ctl + # _run_in_container bash -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/amavis-virus.txt" + + # Required for 'delivers mail to existing alias': + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-alias-external.txt' + # Required for 'delivers mail to existing alias with recipient delimiter': + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-alias-recipient-delimiter.txt' + # Required for 'delivers mail to existing catchall': + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-catchall-local.txt' + # Required for 'delivers mail to regexp alias': + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-regexp-alias-local.txt' + + # Required for 'rejects mail to unknown user': + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/non-existing-user.txt' + # Required for 'redirects mail to external aliases': + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-regexp-alias-external.txt' + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-alias-local.txt' + # Required for 'rejects spam': + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/amavis-spam.txt' + + # Required for 'delivers mail to existing account': + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user1.txt' + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user2.txt' + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user3.txt' + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-added.txt' + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user-and-cc-local-alias.txt' + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/sieve-spam-folder.txt' + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/sieve-pipe.txt' + _run_in_container bash -c 'sendmail root < /tmp/docker-mailserver-test/email-templates/root-email.txt' + + wait_for_empty_mail_queue_in_container "${CONTAINER_NAME}" +} + +function teardown_file() { _default_teardown ; } + +@test "${TEST_NAME_PREFIX} should successfully authenticate with good password (plain)" { + _run_in_container bash -c 'nc -w 5 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/smtp-auth-plain.txt' + assert_success + assert_output --partial 'Authentication successful' +} + +@test "${TEST_NAME_PREFIX} should fail to authenticate with wrong password (plain)" { + _run_in_container bash -c 'nc -w 20 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/smtp-auth-plain-wrong.txt' + assert_output --partial 'authentication failed' + assert_success +} + +@test "${TEST_NAME_PREFIX} should successfully authenticate with good password (login)" { + _run_in_container bash -c 'nc -w 5 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/smtp-auth-login.txt' + assert_success + assert_output --partial 'Authentication successful' +} + +@test "${TEST_NAME_PREFIX} should fail to authenticate with wrong password (login)" { + _run_in_container bash -c 'nc -w 20 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/smtp-auth-login-wrong.txt' + assert_output --partial 'authentication failed' + assert_success +} + +@test "${TEST_NAME_PREFIX} [user: 'added'] should successfully authenticate with good password (plain)" { + _run_in_container bash -c 'nc -w 5 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/added-smtp-auth-plain.txt' + assert_success + assert_output --partial 'Authentication successful' +} + +@test "${TEST_NAME_PREFIX} [user: 'added'] should fail to authenticate with wrong password (plain)" { + _run_in_container bash -c 'nc -w 20 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/added-smtp-auth-plain-wrong.txt' + assert_success + assert_output --partial 'authentication failed' +} + +@test "${TEST_NAME_PREFIX} [user: 'added'] should successfully authenticate with good password (login)" { + _run_in_container bash -c 'nc -w 5 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/added-smtp-auth-login.txt' + assert_success + assert_output --partial 'Authentication successful' +} + +@test "${TEST_NAME_PREFIX} [user: 'added'] should fail to authenticate with wrong password (login)" { + _run_in_container bash -c 'nc -w 20 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/added-smtp-auth-login-wrong.txt' + assert_success + assert_output --partial 'authentication failed' +} + +# TODO: Add a test covering case SPAMASSASSIN_SPAM_TO_INBOX=1 (default) +@test "${TEST_NAME_PREFIX} delivers mail to existing account" { + _run_in_container bash -c "grep 'postfix/lmtp' /var/log/mail/mail.log | grep 'status=sent' | grep ' Saved)' | sed 's/.* to=' + assert_output --partial '6 ' + assert_output --partial '1 , orig_to=' + assert_output --partial '1 ' + assert_output --partial '2 ' + assert_output --partial '1 ' + _should_output_number_of_lines 6 + + # NOTE: Requires ClamAV enabled and to send `amavis-virus` template: + # assert_output --partial '1 , orig_to=' + # _should_output_number_of_lines 7 +} + +@test "${TEST_NAME_PREFIX} delivers mail to existing alias" { + _run_in_container grep 'to=, orig_to=' /var/log/mail/mail.log + assert_success + assert_output --partial 'status=sent' + _should_output_number_of_lines 1 +} + +@test "${TEST_NAME_PREFIX} delivers mail to existing alias with recipient delimiter" { + _run_in_container grep 'to=, orig_to=' /var/log/mail/mail.log + assert_success + assert_output --partial 'status=sent' + _should_output_number_of_lines 1 + + _run_in_container grep 'to=' /var/log/mail/mail.log + assert_success + refute_output --partial 'status=bounced' +} + +@test "${TEST_NAME_PREFIX} delivers mail to existing catchall" { + _run_in_container grep 'to=, orig_to=' /var/log/mail/mail.log + assert_success + assert_output --partial 'status=sent' + _should_output_number_of_lines 1 +} + +@test "${TEST_NAME_PREFIX} delivers mail to regexp alias" { + _run_in_container grep 'to=, orig_to=' /var/log/mail/mail.log + assert_success + assert_output --partial 'status=sent' + _should_output_number_of_lines 1 +} + +@test "${TEST_NAME_PREFIX} user1 should have received 8 mails" { + _run_in_container bash -c "grep Subject /var/mail/localhost.localdomain/user1/new/* | sed 's/.*Subject: //g' | sed 's/\.txt.*//g' | sed 's/VIRUS.*/VIRUS/g' | sort" + assert_success + + assert_output --partial 'Root Test Message' + assert_output --partial 'Test Message existing-alias-external' + assert_output --partial 'Test Message existing-alias-recipient-delimiter' + assert_output --partial 'Test Message existing-catchall-local' + assert_output --partial 'Test Message existing-regexp-alias-local' + assert_output --partial 'Test Message existing-user-and-cc-local-alias' + assert_output --partial 'Test Message existing-user1' + assert_output --partial 'Test Message sieve-spam-folder' + _should_output_number_of_lines 8 + + # The virus mail has three subject lines + # NOTE: Requires ClamAV enabled and to send amavis-virus: + # assert_output --partial 'Test Message amavis-virus' # Should verify two lines expected with this content + # assert_output --partial 'VIRUS' + # _should_output_number_of_lines 11 +} + +@test "${TEST_NAME_PREFIX} rejects mail to unknown user" { + _run_in_container grep ': Recipient address rejected: User unknown in virtual mailbox table' /var/log/mail/mail.log + assert_success + _should_output_number_of_lines 1 +} + +@test "${TEST_NAME_PREFIX} redirects mail to external aliases" { + _run_in_container bash -c "grep 'Passed CLEAN {RelayedInbound}' /var/log/mail/mail.log | grep -- '-> '" + assert_success + assert_output --partial ' -> ' + _should_output_number_of_lines 2 + # assert_output --partial 'external.tld=user@example.test> -> ' +} + +# TODO: Add a test covering case SPAMASSASSIN_SPAM_TO_INBOX=1 (default) +@test "${TEST_NAME_PREFIX} rejects spam" { + _run_in_container grep 'Blocked SPAM {NoBounceInbound,Quarantined}' /var/log/mail/mail.log + assert_success + assert_output --partial ' -> ' + _should_output_number_of_lines 1 + + # Amavis log line with SPAMASSASSIN_SPAM_TO_INBOX=0 + grep 'Passed SPAM {RelayedTaggedInbound,Quarantined}' /var/log/mail/mail.log: + # Amavis log line with SPAMASSASSIN_SPAM_TO_INBOX=1 + grep 'Blocked SPAM {NoBounceInbound,Quarantined}' /var/log/mail/mail.log: + # -> + # Amavis log line with ENABLE_SRS=1 changes the domain-part to match in a log: + # -> + # assert_output --partial 'external.tld=spam@example.test> -> ' +} + +@test "${TEST_NAME_PREFIX} SA - All registered domains should receive mail with spam headers (X-Spam)" { + _run_in_container grep -ir 'X-Spam-' /var/mail/localhost.localdomain/user1/new + assert_success + + _run_in_container grep -ir 'X-Spam-' /var/mail/otherdomain.tld/user2/new + assert_success +} + +# Dovecot does not support SMTPUTF8, so while we can send we cannot receive +# Better disable SMTPUTF8 support entirely if we can't handle it correctly +@test "${TEST_NAME_PREFIX} not advertising smtputf8" { + _run_in_container bash -c 'nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/smtp-ehlo.txt' + assert_success + refute_output --partial 'SMTPUTF8' +} + +@test "${TEST_NAME_PREFIX} mail for root was delivered" { + _run_in_container grep -R 'Subject: Root Test Message' /var/mail/localhost.localdomain/user1/new/ + assert_success +} diff --git a/test/tests/serial/tests.bats b/test/tests/serial/tests.bats index 71c70d47..2dfd890c 100644 --- a/test/tests/serial/tests.bats +++ b/test/tests/serial/tests.bats @@ -1,77 +1,37 @@ -load "${REPOSITORY_ROOT}/test/test_helper/common" +load "${REPOSITORY_ROOT}/test/helper/common" +load "${REPOSITORY_ROOT}/test/helper/setup" + +CONTAINER_NAME='mail' setup_file() { - local PRIVATE_CONFIG - PRIVATE_CONFIG=$(duplicate_config_for_container . mail) - mv "${PRIVATE_CONFIG}/user-patches/user-patches.sh" "${PRIVATE_CONFIG}/user-patches.sh" + init_with_defaults - # `LOG_LEVEL=debug` required for using `wait_until_change_detection_event_completes()` - docker run --rm -d --name mail \ - -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ - -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ - -v "$(pwd)/test/onedir":/var/mail-state \ - -e AMAVIS_LOGLEVEL=2 \ - -e CLAMAV_MESSAGE_SIZE_LIMIT=30M \ - -e ENABLE_CLAMAV=0 \ - -e ENABLE_MANAGESIEVE=1 \ - -e ENABLE_QUOTAS=1 \ - -e ENABLE_SPAMASSASSIN=1 \ - -e ENABLE_SRS=1 \ - -e ENABLE_UPDATE_CHECK=0 \ - -e LOG_LEVEL='debug' \ - -e PERMIT_DOCKER=host \ - -e PFLOGSUMM_TRIGGER=logrotate \ - -e REPORT_RECIPIENT=user1@localhost.localdomain \ - -e REPORT_SENDER=report1@mail.my-domain.com \ - -e SA_KILL=3.0 \ - -e SA_SPAM_SUBJECT="SPAM: " \ - -e SA_TAG=-5.0 \ - -e SA_TAG2=2.0 \ - -e SPAMASSASSIN_SPAM_TO_INBOX=0 \ - -e SPOOF_PROTECTION=1 \ - -e SSL_TYPE='snakeoil' \ - -e VIRUSMAILS_DELETE_DELAY=7 \ - --hostname mail.my-domain.com \ - --tty \ - --ulimit "nofile=$(ulimit -Sn):$(ulimit -Hn)" \ - --health-cmd "ss --listening --tcp | grep -P 'LISTEN.+:smtp' || exit 1" \ - "${NAME}" + mv "${TEST_TMP_CONFIG}/user-patches/user-patches.sh" "${TEST_TMP_CONFIG}/user-patches.sh" - wait_for_finished_setup_in_container mail + local CONTAINER_ARGS_ENV_CUSTOM=( + --env ENABLE_AMAVIS=1 + --env AMAVIS_LOGLEVEL=2 + --env ENABLE_QUOTAS=1 + --env ENABLE_SRS=1 + --env PERMIT_DOCKER=host + --env PFLOGSUMM_TRIGGER=logrotate + --env REPORT_RECIPIENT=user1@localhost.localdomain + --env REPORT_SENDER=report1@mail.example.test + --env SPOOF_PROTECTION=1 + --env SSL_TYPE='snakeoil' + --ulimit "nofile=$(ulimit -Sn):$(ulimit -Hn)" + --health-cmd "ss --listening --tcp | grep -P 'LISTEN.+:smtp' || exit 1" + ) + common_container_setup 'CONTAINER_ARGS_ENV_CUSTOM' # generate accounts after container has been started docker exec mail setup email add 'added@localhost.localdomain' 'mypassword' docker exec mail setup email add 'pass@localhost.localdomain' 'may be \a `p^a.*ssword' - # setup sieve - docker cp "${PRIVATE_CONFIG}/sieve/dovecot.sieve" mail:/var/mail/localhost.localdomain/user1/.dovecot.sieve - # this relies on the checksum file being updated after all changes have been applied wait_until_change_detection_event_completes mail wait_for_service mail postfix wait_for_smtp_port_in_container mail - - # The first mail sent leverages an assert for better error output if a failure occurs: - run docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/amavis-spam.txt" - assert_success - - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-alias-external.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-alias-local.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-alias-recipient-delimiter.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user1.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user2.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user3.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-added.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user-and-cc-local-alias.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-regexp-alias-external.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-regexp-alias-local.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-catchall-local.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/sieve-spam-folder.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/sieve-pipe.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/non-existing-user.txt" - docker exec mail /bin/sh -c "sendmail root < /tmp/docker-mailserver-test/email-templates/root-email.txt" - - wait_for_empty_mail_queue_in_container mail } teardown_file() { @@ -197,140 +157,6 @@ teardown_file() { assert_success } -# -# smtp -# - -@test "checking smtp: authentication works with good password (plain)" { - run docker exec mail /bin/sh -c "nc -w 5 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/smtp-auth-plain.txt | grep 'Authentication successful'" - assert_success -} - -@test "checking smtp: authentication fails with wrong password (plain)" { - run docker exec mail /bin/sh -c "nc -w 20 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/smtp-auth-plain-wrong.txt" - assert_output --partial 'authentication failed' - assert_success -} - -@test "checking smtp: authentication works with good password (login)" { - run docker exec mail /bin/sh -c "nc -w 5 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/smtp-auth-login.txt | grep 'Authentication successful'" - assert_success -} - -@test "checking smtp: authentication fails with wrong password (login)" { - run docker exec mail /bin/sh -c "nc -w 20 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/smtp-auth-login-wrong.txt" - assert_output --partial 'authentication failed' - assert_success -} - -@test "checking smtp: added user authentication works with good password (plain)" { - run docker exec mail /bin/sh -c "nc -w 5 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/added-smtp-auth-plain.txt | grep 'Authentication successful'" - assert_success -} - -@test "checking smtp: added user authentication fails with wrong password (plain)" { - run docker exec mail /bin/sh -c "nc -w 20 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/added-smtp-auth-plain-wrong.txt | grep 'authentication failed'" - assert_success -} - -@test "checking smtp: added user authentication works with good password (login)" { - run docker exec mail /bin/sh -c "nc -w 5 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/added-smtp-auth-login.txt | grep 'Authentication successful'" - assert_success -} - -@test "checking smtp: added user authentication fails with wrong password (login)" { - run docker exec mail /bin/sh -c "nc -w 20 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/added-smtp-auth-login-wrong.txt | grep 'authentication failed'" - assert_success -} - -# TODO add a test covering case SPAMASSASSIN_SPAM_TO_INBOX=1 (default) -@test "checking smtp: delivers mail to existing account" { - run docker exec mail /bin/sh -c "grep 'postfix/lmtp' /var/log/mail/mail.log | grep 'status=sent' | grep ' Saved)' | sed 's/.* to= - 6 - 1 , orig_to= - 1 , orig_to= - 1 - 2 - 1 -EOF -} - -@test "checking smtp: delivers mail to existing alias" { - run docker exec mail /bin/sh -c "grep 'to=, orig_to=' /var/log/mail/mail.log | grep 'status=sent' | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: delivers mail to existing alias with recipient delimiter" { - run docker exec mail /bin/sh -c "grep 'to=, orig_to=' /var/log/mail/mail.log | grep 'status=sent' | wc -l" - assert_success - assert_output 1 - - run docker exec mail /bin/sh -c "grep 'to=' /var/log/mail/mail.log | grep 'status=bounced'" - assert_failure -} - -@test "checking smtp: delivers mail to existing catchall" { - run docker exec mail /bin/sh -c "grep 'to=, orig_to=' /var/log/mail/mail.log | grep 'status=sent' | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: delivers mail to regexp alias" { - run docker exec mail /bin/sh -c "grep 'to=, orig_to=' /var/log/mail/mail.log | grep 'status=sent' | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: user1 should have received 9 mails" { - run docker exec mail /bin/sh -c "grep Subject /var/mail/localhost.localdomain/user1/new/* | sed 's/.*Subject: //g' | sed 's/\.txt.*//g' | sed 's/VIRUS.*/VIRUS/g' | sort" - assert_success - # 9 messages, the virus mail has three subject lines - cat <<'EOF' | assert_output -Root Test Message -Test Message amavis-virus -Test Message amavis-virus -Test Message existing-alias-external -Test Message existing-alias-recipient-delimiter -Test Message existing-catchall-local -Test Message existing-regexp-alias-local -Test Message existing-user-and-cc-local-alias -Test Message existing-user1 -Test Message sieve-spam-folder -VIRUS -EOF -} - -@test "checking smtp: rejects mail to unknown user" { - run docker exec mail /bin/sh -c "grep ': Recipient address rejected: User unknown in virtual mailbox table' /var/log/mail/mail.log | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: redirects mail to external aliases" { - run docker exec mail /bin/sh -c "grep -- '-> ' /var/log/mail/mail.log* | grep RelayedInbound | wc -l" - assert_success - assert_output 2 -} - -# TODO add a test covering case SPAMASSASSIN_SPAM_TO_INBOX=1 (default) -@test "checking smtp: rejects spam" { - run docker exec mail /bin/sh -c "grep 'Blocked SPAM' /var/log/mail/mail.log | grep external.tld=spam@my-domain.com | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: not advertising smtputf8" { - # Dovecot does not support SMTPUTF8, so while we can send we cannot receive - # Better disable SMTPUTF8 support entirely if we can't handle it correctly - run docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/smtp-ehlo.txt | grep SMTPUTF8 | wc -l" - assert_success - assert_output 0 -} - # # accounts # @@ -355,7 +181,7 @@ EOF } @test "checking accounts: user mail folder for user3" { - run docker exec mail /bin/bash -c "ls -d /var/mail/localhost.localdomain/user3/mail" + run docker exec mail /bin/bash -c "ls -d /var/mail/localhost.localdomain/user3" assert_success } @@ -381,46 +207,6 @@ EOF assert_line --index 2 "otherdomain.tld" } -@test "checking postfix: main.cf overrides" { - run docker exec mail grep -q 'max_idle = 600s' /tmp/docker-mailserver/postfix-main.cf - assert_success - run docker exec mail grep -q 'readme_directory = /tmp' /tmp/docker-mailserver/postfix-main.cf - assert_success -} - -@test "checking postfix: master.cf overrides" { - run docker exec mail grep -q 'submission/inet/smtpd_sasl_security_options=noanonymous' /tmp/docker-mailserver/postfix-master.cf - assert_success -} - -# -# dovecot -# - -@test "checking dovecot: config additions" { - run docker exec mail grep -q 'mail_max_userip_connections = 69' /tmp/docker-mailserver/dovecot.cf - assert_success - run docker exec mail /bin/sh -c "doveconf | grep 'mail_max_userip_connections = 69'" - assert_success - assert_output 'mail_max_userip_connections = 69' -} - -# -# spamassassin -# - -@test "checking spamassassin: should be listed in amavis when enabled" { - run docker exec mail /bin/sh -c "grep -i 'ANTI-SPAM-SA code' /var/log/mail/mail.log | grep 'NOT loaded'" - assert_failure -} - -@test "checking spamassassin: all registered domains should see spam headers" { - run docker exec mail /bin/sh -c "grep -ir 'X-Spam-' /var/mail/localhost.localdomain/user1/new" - assert_success - run docker exec mail /bin/sh -c "grep -ir 'X-Spam-' /var/mail/otherdomain.tld/user2/new" - assert_success -} - # # postsrsd # @@ -443,7 +229,7 @@ EOF @test "checking SRS: fallback to hostname is handled correctly" { - run docker exec mail grep "SRS_DOMAIN=my-domain.com" /etc/default/postsrsd + run docker exec mail grep "SRS_DOMAIN=example.test" /etc/default/postsrsd assert_success } @@ -510,13 +296,13 @@ EOF @test "checking system: sets the server fqdn" { run docker exec mail hostname assert_success - assert_output "mail.my-domain.com" + assert_output "mail.example.test" } @test "checking system: sets the server domain name in /etc/mailname" { run docker exec mail cat /etc/mailname assert_success - assert_output "my-domain.com" + assert_output "example.test" } @test "checking system: postfix should not log to syslog" { @@ -558,33 +344,6 @@ zip EOF } - -# -# sieve -# - -@test "checking sieve: user1 should have received 1 email in folder INBOX.spam" { - run docker exec mail /bin/sh -c "ls -A /var/mail/localhost.localdomain/user1/.INBOX.spam/new | wc -l" - assert_success - assert_output 1 -} - -@test "checking manage sieve: server is ready when ENABLE_MANAGESIEVE has been set" { - run docker exec mail /bin/bash -c "nc -z 0.0.0.0 4190" - assert_success -} - -@test "checking sieve: user2 should have piped 1 email to /tmp/" { - run docker exec mail /bin/sh -c "ls -A /tmp/pipe-test.out | wc -l" - assert_success - assert_output 1 -} - -@test "checking sieve global: user1 should have gotten a copy of his spam mail" { - run docker exec mail /bin/sh -c "grep 'Spambot ' -R /var/mail/localhost.localdomain/user1/new/" - assert_success -} - # # accounts # @@ -656,7 +415,7 @@ EOF @test "checking accounts: listmailuser (quotas enabled)" { run docker exec mail /bin/sh -c "sed -i '/ENABLE_QUOTAS=0/d' /etc/dms-settings; listmailuser | head -n 1" assert_success - assert_output '* user1@localhost.localdomain ( 10K / ~ ) [0%]' + assert_output '* user1@localhost.localdomain ( 0 / ~ ) [0%]' } @test "checking accounts: no error is generated when deleting a user if /tmp/docker-mailserver/postfix-accounts.cf is missing" { @@ -914,7 +673,7 @@ EOF # postfix @test "checking dovecot: postmaster address" { - run docker exec mail /bin/sh -c "grep 'postmaster_address = postmaster@my-domain.com' /etc/dovecot/conf.d/15-lda.conf" + run docker exec mail /bin/sh -c "grep 'postmaster_address = postmaster@example.test' /etc/dovecot/conf.d/15-lda.conf" assert_success } @@ -941,10 +700,10 @@ EOF run docker exec mail grep "Subject: Postfix Summary for " /var/mail/localhost.localdomain/user1/new/ -R assert_success # check sender is the one specified in REPORT_SENDER - run docker exec mail grep "From: report1@mail.my-domain.com" /var/mail/localhost.localdomain/user1/new/ -R + run docker exec mail grep "From: report1@mail.example.test" /var/mail/localhost.localdomain/user1/new/ -R assert_success # check sender is not the default one. - run docker exec mail grep "From: mailserver-report@mail.my-domain.com" /var/mail/localhost.localdomain/user1/new/ -R + run docker exec mail grep "From: mailserver-report@mail.example.test" /var/mail/localhost.localdomain/user1/new/ -R assert_failure } @@ -971,12 +730,3 @@ EOF run docker exec mail /bin/bash -c "pkill opendmarc && sleep 10 && ps aux --forest | grep -v grep | grep '/usr/sbin/opendmarc'" assert_success } - -# -# root mail delivery -# - -@test "checking that mail for root was delivered" { - run docker exec mail grep "Subject: Root Test Message" /var/mail/localhost.localdomain/user1/new/ -R - assert_success -}