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.

<details>
<summary>Commit message history for reference</summary>

```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`.
```

</details>
This commit is contained in:
Brennan Kinney 2023-01-07 11:36:20 +13:00 committed by GitHub
parent 89352ce363
commit 1024e0ccf2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 435 additions and 291 deletions

View File

@ -1,6 +1,5 @@
require ["fileinto", "copy"];
if address :contains ["From"] "spam@spam.com" {
fileinto :copy "INBOX";
fileinto :copy "INBOX";
}

View File

@ -0,0 +1,7 @@
require ["fileinto"];
if address :contains ["From"] "spam@spam.com" {
fileinto "INBOX.spam";
} else {
keep;
}

View File

@ -1,2 +1 @@
mail_max_userip_connections = 69
recipient_delimiter = ~

View File

@ -1,4 +1,3 @@
recipient_delimiter = ~
max_idle = 600s
# this is a comment
# this is also a comment

View File

@ -1,8 +0,0 @@
require ["fileinto", "reject"];
if address :contains ["From"] "spam@spam.com" {
fileinto "INBOX.spam";
} else {
keep;
}

View File

@ -0,0 +1 @@
recipient_delimiter = ~

View File

@ -0,0 +1 @@
recipient_delimiter = ~

View File

@ -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
}

View File

@ -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'
}

View File

@ -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'
}

View File

@ -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 <spam@spam.com>' -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
}

View File

@ -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=</</g' | sed 's/, relay.*//g' | sort | uniq -c | tr -s \" \""
assert_success
assert_output --partial '1 <added@localhost.localdomain>'
assert_output --partial '6 <user1@localhost.localdomain>'
assert_output --partial '1 <user1@localhost.localdomain>, orig_to=<root>'
assert_output --partial '1 <user1~test@localhost.localdomain>'
assert_output --partial '2 <user2@otherdomain.tld>'
assert_output --partial '1 <user3@localhost.localdomain>'
_should_output_number_of_lines 6
# NOTE: Requires ClamAV enabled and to send `amavis-virus` template:
# assert_output --partial '1 <user1@localhost.localdomain>, orig_to=<postmaster@example.test>'
# _should_output_number_of_lines 7
}
@test "${TEST_NAME_PREFIX} delivers mail to existing alias" {
_run_in_container grep 'to=<user1@localhost.localdomain>, orig_to=<alias1@localhost.localdomain>' /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=<user1~test@localhost.localdomain>, orig_to=<alias1~test@localhost.localdomain>' /var/log/mail/mail.log
assert_success
assert_output --partial 'status=sent'
_should_output_number_of_lines 1
_run_in_container grep 'to=<user1~test@localhost.localdomain>' /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=<user1@localhost.localdomain>, orig_to=<wildcard@localdomain2.com>' /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=<user1@localhost.localdomain>, orig_to=<test123@localhost.localdomain>' /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 '<nouser@localhost.localdomain>: 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 -- '-> <external1@otherdomain.tld>'"
assert_success
assert_output --partial '<user@external.tld> -> <external1@otherdomain.tld>'
_should_output_number_of_lines 2
# assert_output --partial 'external.tld=user@example.test> -> <external1@otherdomain.tld>'
}
# 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 '<spam@external.tld> -> <user1@localhost.localdomain>'
_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:
# <spam@external.tld> -> <user1@localhost.localdomain>
# Amavis log line with ENABLE_SRS=1 changes the domain-part to match in a log:
# <SRS0=g+ca=5C=external.tld=spam@example.test> -> <user1@localhost.localdomain>
# assert_output --partial 'external.tld=spam@example.test> -> <user1@localhost.localdomain>'
}
@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
}

View File

@ -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=</</g' | sed 's/, relay.*//g' | sort | uniq -c | tr -s \" \""
assert_success
assert_output <<'EOF'
1 <added@localhost.localdomain>
6 <user1@localhost.localdomain>
1 <user1@localhost.localdomain>, orig_to=<postmaster@my-domain.com>
1 <user1@localhost.localdomain>, orig_to=<root>
1 <user1~test@localhost.localdomain>
2 <user2@otherdomain.tld>
1 <user3@localhost.localdomain>
EOF
}
@test "checking smtp: delivers mail to existing alias" {
run docker exec mail /bin/sh -c "grep 'to=<user1@localhost.localdomain>, orig_to=<alias1@localhost.localdomain>' /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=<user1~test@localhost.localdomain>, orig_to=<alias1~test@localhost.localdomain>' /var/log/mail/mail.log | grep 'status=sent' | wc -l"
assert_success
assert_output 1
run docker exec mail /bin/sh -c "grep 'to=<user1~test@localhost.localdomain>' /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=<user1@localhost.localdomain>, orig_to=<wildcard@localdomain2.com>' /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=<user1@localhost.localdomain>, orig_to=<test123@localhost.localdomain>' /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 '<nouser@localhost.localdomain>: 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 -- '-> <external1@otherdomain.tld>' /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 <spam@spam.com>' -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
}