diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 2c225fcf..aee54474 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -13,7 +13,7 @@ Here's a quick guide: refactoring and documentation changes require no new tests. If you are adding functionality or fixing a bug, we need tests! -4. Run the tests. `make build-no-cache generate-accounts run generate-accounts-after-run fixtures tests clean` +4. Run the tests. `make build-no-cache generate-accounts run fixtures tests clean` 5. Push to your fork and submit a pull request. If the changes will apply cleanly to the master branch, you will only need to submit one pull request. diff --git a/.gitignore b/.gitignore index 1080888f..827c2548 100644 --- a/.gitignore +++ b/.gitignore @@ -32,7 +32,10 @@ test/config/postfix-send-access.cf test/config/postfix-send-access.cfe test/config/relay-hosts/chksum test/config/relay-hosts/postfix-aliases.cf -test/config/without-accounts/ test/config/without-virtual/ test/config/with-domain/ test/onedir +test/duplicate_configs + +config.bak +testconfig.bak diff --git a/.travis.yml b/.travis.yml index 7faa4755..d5972626 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,7 +39,7 @@ script: - make eclint - make hadolint - make shellcheck - - make generate-accounts run generate-accounts-after-run fixtures tests + - make generate-accounts tests after_script: - make clean diff --git a/Makefile b/Makefile index 0defc0fb..09900387 100644 --- a/Makefile +++ b/Makefile @@ -6,9 +6,9 @@ VCS_VERSION := $(shell git describe --tags --contains --always) SLEEP = 15s -all: build backup generate-accounts run generate-accounts-after-run fixtures tests clean -no-build: backup generate-accounts run generate-accounts-after-run fixtures tests clean -complete_test: lint build generate-accounts run generate-accounts-after-run fixtures tests +all: build backup generate-accounts tests clean +no-build: backup generate-accounts tests clean +complete_test: lint build generate-accounts tests build: docker build \ @@ -28,111 +28,6 @@ generate-accounts: @ echo "# this is a test comment, please don't delete me :'(" >> test/config/postfix-accounts.cf @ echo " # this is also a test comment, :O" >> test/config/postfix-accounts.cf -run: -# run containers - -@ echo "Sleeping $(SLEEP) after each container" - docker run --rm -d --name mail \ - -v "`pwd`/test/config":/tmp/docker-mailserver \ - -v "`pwd`/test/test-files":/tmp/docker-mailserver-test:ro \ - -v "`pwd`/test/onedir":/var/mail-state \ - -v "`pwd`/test/config/user-patches/user-patches.sh":/tmp/docker-mailserver/user-patches.sh \ - -e ENABLE_CLAMAV=1 \ - -e SPOOF_PROTECTION=1 \ - -e ENABLE_SPAMASSASSIN=1 \ - -e REPORT_RECIPIENT=user1@localhost.localdomain \ - -e REPORT_SENDER=report1@mail.my-domain.com \ - -e SA_TAG=-5.0 \ - -e SA_TAG2=2.0 \ - -e SA_KILL=3.0 \ - -e SA_SPAM_SUBJECT="SPAM: " \ - -e VIRUSMAILS_DELETE_DELAY=7 \ - -e ENABLE_SRS=1 \ - -e SASL_PASSWD="external-domain.com username:password" \ - -e ENABLE_MANAGESIEVE=1 \ - --cap-add=SYS_PTRACE \ - -e PERMIT_DOCKER=host \ - -e DMS_DEBUG=0 \ - -h mail.my-domain.com -t $(NAME) - -@ sleep $(SLEEP) - docker run --rm -d --name mail_smtponly_without_config \ - -e SMTP_ONLY=1 \ - -e ENABLE_LDAP=1 \ - -e PERMIT_DOCKER=network \ - -e OVERRIDE_HOSTNAME=mail.mydomain.com \ - -t $(NAME) - -@ sleep $(SLEEP) - docker run --rm -d --name mail_override_hostname \ - -v "`pwd`/test/config":/tmp/docker-mailserver \ - -v "`pwd`/test/test-files":/tmp/docker-mailserver-test:ro \ - -e PERMIT_DOCKER=network \ - -e DMS_DEBUG=0 \ - -e ENABLE_SRS=1 \ - -e OVERRIDE_HOSTNAME=mail.my-domain.com \ - -h unknown.domain.tld \ - -t $(NAME) - -@ sleep $(SLEEP) - docker run --rm -d --name mail_domainname \ - -v "`pwd`/test/config":/tmp/docker-mailserver \ - -v "`pwd`/test/test-files":/tmp/docker-mailserver-test:ro \ - -e PERMIT_DOCKER=network \ - -e DMS_DEBUG=0 \ - -e ENABLE_SRS=1 \ - -e DOMAINNAME=my-domain.com \ - -h unknown.domain.tld \ - -t $(NAME) - -@ sleep $(SLEEP) - docker run --rm -d --name mail_srs_domainname \ - -v "`pwd`/test/config":/tmp/docker-mailserver \ - -v "`pwd`/test/test-files":/tmp/docker-mailserver-test:ro \ - -e PERMIT_DOCKER=network \ - -e DMS_DEBUG=0 \ - -e ENABLE_SRS=1 \ - -e SRS_DOMAINNAME=srs.my-domain.com \ - -e DOMAINNAME=my-domain.com \ - -h unknown.domain.tld \ - -t $(NAME) - -@ sleep $(SLEEP) - docker run --rm -d --name mail_disabled_clamav_spamassassin \ - -v "`pwd`/test/config":/tmp/docker-mailserver \ - -v "`pwd`/test/test-files":/tmp/docker-mailserver-test:ro \ - -e ENABLE_CLAMAV=0 \ - -e ENABLE_SPAMASSASSIN=0 \ - -e DMS_DEBUG=0 \ - -h mail.my-domain.com -t $(NAME) - -@ sleep $(SLEEP) - -generate-accounts-after-run: - @ docker run --rm -e MAIL_USER=added@localhost.localdomain -e MAIL_PASS=mypassword -t $(NAME) /bin/sh -c 'echo "$$MAIL_USER|$$(doveadm pw -s SHA512-CRYPT -u $$MAIL_USER -p $$MAIL_PASS)"' >> test/config/postfix-accounts.cf - @ docker exec mail addmailuser pass@localhost.localdomain 'may be \a `p^a.*ssword' - @ sleep $(SLEEP) - -fixtures: -# setup sieve - docker cp "`pwd`/test/config/sieve/dovecot.sieve" mail:/var/mail/localhost.localdomain/user1/.dovecot.sieve - sleep $(SLEEP) - sleep $(SLEEP) -# sending test mails - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/amavis-spam.txt" - docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/amavis-virus.txt" - 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-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_disabled_clamav_spamassassin /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user1.txt" - docker exec mail /bin/sh -c "sendmail root < /tmp/docker-mailserver-test/email-templates/root-email.txt" -# postfix virtual transport lmtp - docker exec mail_override_hostname /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user1.txt" -# wait for mails to be analyzed - sleep 80 tests: ./test/bats/bin/bats test/*.bats @@ -152,7 +47,7 @@ clean: sudo rm -rf test/config ;\ mv testconfig.bak test/config ;\ fi - -@ sudo rm -rf test/onedir test/alias test/quota test/relay test/config/dovecot-lmtp/userdb test/config/key* test/config/opendkim/keys/domain.tld/ test/config/opendkim/keys/example.com/ test/config/opendkim/keys/localdomain2.com/ test/config/postfix-aliases.cf test/config/postfix-receive-access.cf test/config/postfix-receive-access.cfe test/config/dovecot-quotas.cf test/config/postfix-send-access.cf test/config/postfix-send-access.cfe test/config/relay-hosts/chksum test/config/relay-hosts/postfix-aliases.cf test/config/dhparams.pem test/config/dovecot-lmtp/dh.pem test/config/relay-hosts/dovecot-quotas.cf test/config/user-patches.sh test/alias/config/postfix-virtual.cf test/quota/config/dovecot-quotas.cf test/quota/config/postfix-accounts.cf test/relay/config/postfix-relaymap.cf test/relay/config/postfix-sasl-password.cf + -@ sudo rm -rf test/onedir test/alias test/quota test/relay test/config/dovecot-lmtp/userdb test/config/key* test/config/opendkim/keys/domain.tld/ test/config/opendkim/keys/example.com/ test/config/opendkim/keys/localdomain2.com/ test/config/postfix-aliases.cf test/config/postfix-receive-access.cf test/config/postfix-receive-access.cfe test/config/dovecot-quotas.cf test/config/postfix-send-access.cf test/config/postfix-send-access.cfe test/config/relay-hosts/chksum test/config/relay-hosts/postfix-aliases.cf test/config/dhparams.pem test/config/dovecot-lmtp/dh.pem test/config/relay-hosts/dovecot-quotas.cf test/config/user-patches.sh test/alias/config/postfix-virtual.cf test/quota/config/dovecot-quotas.cf test/quota/config/postfix-accounts.cf test/relay/config/postfix-relaymap.cf test/relay/config/postfix-sasl-password.cf test/duplicate_configs/ lint: eclint hadolint shellcheck diff --git a/target/check-for-changes.sh b/target/check-for-changes.sh index c36c1693..7dc5a200 100755 --- a/target/check-for-changes.sh +++ b/target/check-for-changes.sh @@ -53,7 +53,6 @@ do then echo "${LOG_DATE} Change detected" CHANGED=$(grep -Fxvf "${CHKSUM_FILE}" "${CHKSUM_FILE}.new" | sed 's/^[^ ]\+ //') - mv "${CHKSUM_FILE}.new" "${CHKSUM_FILE}" # Bug alert! This overwrites the alias set by start-mailserver.sh # Take care that changes in one script are propagated to the other @@ -231,6 +230,9 @@ s/$/ regexp:\/etc\/postfix\/regexp/ # prevent restart of dovecot when smtp_only=1 [[ ${SMTP_ONLY} -ne 1 ]] && supervisorctl restart dovecot ) 200.*status\=sent" /var/log/mail/mail.log' [ "$status" -ge 0 ] -} \ No newline at end of file +} diff --git a/test/test_helper.bats b/test/test_helper.bats new file mode 100644 index 00000000..7473241d --- /dev/null +++ b/test/test_helper.bats @@ -0,0 +1,251 @@ +load 'test_helper/bats-support/load' +load 'test_helper/bats-assert/load' +load 'test_helper/common' + +@test "repeat_until_success_or_timeout returns instantly on success" { + SECONDS=0 + repeat_until_success_or_timeout 1 true + [[ ${SECONDS} -le 1 ]] +} + +@test "repeat_until_success_or_timeout waits for timeout on persistent failure" { + SECONDS=0 + run repeat_until_success_or_timeout 2 false + [[ ${SECONDS} -ge 2 ]] + assert_failure + assert_output --partial "Timed out on command" +} + +@test "repeat_until_success_or_timeout aborts immediately on fatal failure" { + SECONDS=0 + run repeat_until_success_or_timeout --fatal-test false 2 false + [[ ${SECONDS} -le 1 ]] + assert_failure + assert_output --partial "early aborting" +} + +@test "repeat_until_success_or_timeout expects integer timeout" { + run repeat_until_success_or_timeout 1 true + assert_success + + run repeat_until_success_or_timeout timeout true + assert_failure + + run repeat_until_success_or_timeout --fatal-test true timeout true + assert_failure +} + +@test "run_until_success_or_timeout returns instantly on success" { + SECONDS=0 + run_until_success_or_timeout 2 true + [[ ${SECONDS} -le 1 ]] + assert_success +} + +@test "run_until_success_or_timeout waits for timeout on persistent failure" { + SECONDS=0 + ! run_until_success_or_timeout 2 false + [[ ${SECONDS} -ge 2 ]] + assert_failure +} + +@test "repeat_in_container_until_success_or_timeout fails immediately for non-running container" { + SECONDS=0 + ! repeat_in_container_until_success_or_timeout 10 name-of-non-existing-container true + [[ ${SECONDS} -le 1 ]] +} + +@test "repeat_in_container_until_success_or_timeout run command in container" { + local CONTAINER_NAME + CONTAINER_NAME=$(docker run --rm -d alpine sleep 100) + SECONDS=0 + ! repeat_in_container_until_success_or_timeout 10 "${CONTAINER_NAME}" sh -c "echo '${CONTAINER_NAME}' > /tmp/marker" + [[ ${SECONDS} -le 1 ]] + run docker exec "${CONTAINER_NAME}" cat /tmp/marker + assert_output "${CONTAINER_NAME}" +} + +@test "container_is_running" { + local CONTAINER_NAME + CONTAINER_NAME=$(docker run --rm -d alpine sleep 100) + container_is_running "${CONTAINER_NAME}" + docker rm -f "${CONTAINER_NAME}" + ! container_is_running "${CONTAINER_NAME}" +} + +@test "wait_for_smtp_port_in_container aborts wait after timeout" { + local CONTAINER_NAME + CONTAINER_NAME=$(docker run --rm -d alpine sleep 100) + SECONDS=0 + TEST_TIMEOUT_IN_SECONDS=2 run wait_for_smtp_port_in_container "${CONTAINER_NAME}" + [[ ${SECONDS} -ge 2 ]] + assert_failure + assert_output --partial "Timed out on command" +} + +@test "wait_for_smtp_port_in_container returns immediately when port found" { + local CONTAINER_NAME + CONTAINER_NAME=$(docker run --rm -d alpine sh -c "sleep 10") + + docker exec "${CONTAINER_NAME}" apk add netcat-openbsd + docker exec "${CONTAINER_NAME}" nc -l 25 & + + SECONDS=0 + TEST_TIMEOUT_IN_SECONDS=5 run wait_for_smtp_port_in_container "${CONTAINER_NAME}" + [[ ${SECONDS} -lt 5 ]] + assert_success +} + +@test "wait_for_finished_setup_in_container" { + # variable not local to make visible to teardown + local PRIVATE_CONFIG + PRIVATE_CONFIG="$(duplicate_config_for_container .)" + CONTAINER_NAME="$(docker run -d --rm \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -h mail.my-domain.com \ + -t "${NAME}")" + teardown() { docker rm -f "${CONTAINER_NAME}"; } + + # the setup should not be finished immediately after starting + ! TEST_TIMEOUT_IN_SECONDS=0 wait_for_finished_setup_in_container "${CONTAINER_NAME}" + + # but it will finish eventually + SECONDS=1 + wait_for_finished_setup_in_container "${CONTAINER_NAME}" + [[ $SECONDS -gt 0 ]] +} + +@test "duplicate_config_for_container" { + local path + path="$(duplicate_config_for_container duplicate_config_test)" + + run cat "$path/marker" + assert_line "This marker file is there to identify the correct config being copied" + + run duplicate_config_for_container non-existant-source-folder "${BATS_TEST_NAME}2" + assert_failure +} + +@test "container_has_service_running/wait_for_service" { + local PRIVATE_CONFIG + PRIVATE_CONFIG="$(duplicate_config_for_container .)" + # variable not local to make visible to teardown + CONTAINER_NAME="$(docker run -d --rm \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -h mail.my-domain.com \ + -t "${NAME}")" + teardown() { docker rm -f "${CONTAINER_NAME}"; } + + # pick a service that was not started + ! container_has_service_running "${CONTAINER_NAME}" clamav + + # wait for a service that should be started + wait_for_service "${CONTAINER_NAME}" postfix + + # shut down the service + docker exec "${CONTAINER_NAME}" supervisorctl stop postfix + + # now it should be off + SECONDS=0 + TEST_TIMEOUT_IN_SECONDS=5 run wait_for_service "${CONTAINER_NAME}" postfix + [[ $SECONDS -ge 5 ]] + assert_failure +} + +@test "wait_for_changes_to_be_detected_in_container fails when timeout is reached" { + local PRIVATE_CONFIG + PRIVATE_CONFIG="$(duplicate_config_for_container .)" + # variable not local to make visible to teardown + CONTAINER_NAME="$(docker run -d --rm \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -h mail.my-domain.com \ + -t "${NAME}")" + teardown() { docker rm -f "${CONTAINER_NAME}"; } + + # wait for the initial checksum detection to complete + repeat_in_container_until_success_or_timeout 60 "${CONTAINER_NAME}" test -e /tmp/docker-mailserver-config-chksum + + # there should be no changes in the beginning + TEST_TIMEOUT_IN_SECONDS=0 wait_for_changes_to_be_detected_in_container "${CONTAINER_NAME}" + + # trigger some change + docker exec "${CONTAINER_NAME}" /bin/sh -c "addmailuser auser3@mail.my-domain.com mypassword" + + # that should be picked up as not yet detected + ! TEST_TIMEOUT_IN_SECONDS=0 wait_for_changes_to_be_detected_in_container "${CONTAINER_NAME}" +} + +@test "wait_for_changes_to_be_detected_in_container succeeds within timeout" { + local PRIVATE_CONFIG + PRIVATE_CONFIG="$(duplicate_config_for_container .)" + # variable not local to make visible to teardown + CONTAINER_NAME="$(docker run -d --rm \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -h mail.my-domain.com \ + -t "${NAME}")" + teardown() { docker rm -f "${CONTAINER_NAME}"; } + + # wait for the initial checksum detection to complete + repeat_in_container_until_success_or_timeout 60 "${CONTAINER_NAME}" test -e /tmp/docker-mailserver-config-chksum + + # trigger some change + docker exec "${CONTAINER_NAME}" /bin/sh -c "addmailuser auser3@mail.my-domain.com mypassword" + + # that should eventually be detected + SECONDS=0 + wait_for_changes_to_be_detected_in_container "${CONTAINER_NAME}" + [[ $SECONDS -gt 0 ]] +} + +@test "wait_for_empty_mail_queue_in_container fails when timeout reached" { + local PRIVATE_CONFIG + PRIVATE_CONFIG="$(duplicate_config_for_container .)" + # variable not local to make visible to teardown + # enable clamav to make message delivery slower, so we can detect it + CONTAINER_NAME="$(docker run -d --rm \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e ENABLE_CLAMAV=1 \ + -h mail.my-domain.com \ + -t "${NAME}")" + + teardown() { docker rm -f "${CONTAINER_NAME}"; } + + wait_for_smtp_port_in_container "${CONTAINER_NAME}" || docker logs "${CONTAINER_NAME}" + + SECONDS=0 + # no mails -> should return immediately + TEST_TIMEOUT_IN_SECONDS=5 wait_for_empty_mail_queue_in_container "${CONTAINER_NAME}" + [[ $SECONDS -lt 5 ]] + + # fill the queue with a message + docker exec "${CONTAINER_NAME}" /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/amavis-virus.txt" + + # that should still be stuck in the queue + ! TEST_TIMEOUT_IN_SECONDS=0 wait_for_empty_mail_queue_in_container "${CONTAINER_NAME}" +} + +@test "wait_for_empty_mail_queue_in_container succeeds within timeout" { + local PRIVATE_CONFIG + PRIVATE_CONFIG="$(duplicate_config_for_container .)" + # variable not local to make visible to teardown + # enable clamav to make message delivery slower, so we can detect it + CONTAINER_NAME="$(docker run -d --rm \ + -v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \ + -v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \ + -e ENABLE_CLAMAV=1 \ + -h mail.my-domain.com \ + -t "${NAME}")" + + teardown() { docker rm -f "${CONTAINER_NAME}"; } + + wait_for_smtp_port_in_container "${CONTAINER_NAME}" || docker logs "${CONTAINER_NAME}" + + # fill the queue with a message + docker exec "${CONTAINER_NAME}" /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/amavis-virus.txt" + + # give it some time to clear the queue + SECONDS=0 + wait_for_empty_mail_queue_in_container "${CONTAINER_NAME}" + [[ $SECONDS -gt 0 ]] +} diff --git a/test/test_helper/common.bash b/test/test_helper/common.bash index eeb27a13..6552945a 100644 --- a/test/test_helper/common.bash +++ b/test/test_helper/common.bash @@ -1,70 +1,136 @@ +#! /bin/bash + load 'test_helper/bats-support/load' load 'test_helper/bats-assert/load' +# shellcheck disable=SC2034 NAME=tvial/docker-mailserver:testing # default timeout is 120 seconds TEST_TIMEOUT_IN_SECONDS=${TEST_TIMEOUT_IN_SECONDS-120} NUMBER_OF_LOG_LINES=${NUMBER_OF_LOG_LINES-10} +# @param ${1} timeout +# @param --fatal-test additional test whose failure aborts immediately +# @param ... test to run function repeat_until_success_or_timeout { - if ! [[ "$1" =~ ^[0-9]+$ ]]; then - echo "First parameter for timeout must be an integer, recieved \"$1\"" + local FATAL_FAILURE_TEST_COMMAND + if [[ "${1}" == "--fatal-test" ]]; then + FATAL_FAILURE_TEST_COMMAND="${2}" + shift 2 + fi + if ! [[ "${1}" =~ ^[0-9]+$ ]]; then + echo "First parameter for timeout must be an integer, recieved \"${1}\"" return 1 fi - TIMEOUT=$1 - STARTTIME=$SECONDS + local TIMEOUT=${1} + local STARTTIME=${SECONDS} shift 1 - until "$@" + until "${@}" do - sleep 5 - if [[ $(($SECONDS - $STARTTIME )) -gt $TIMEOUT ]]; then - echo "Timed out on command: $@" + if [[ -n ${FATAL_FAILURE_TEST_COMMAND} ]] && ! eval "${FATAL_FAILURE_TEST_COMMAND}"; then + echo "\`${FATAL_FAILURE_TEST_COMMAND}\` failed, early aborting repeat_until_success of \`${*}\`" >&2 + return 1 + fi + sleep 1 + if [[ $(( SECONDS - STARTTIME )) -gt ${TIMEOUT} ]]; then + echo "Timed out on command: ${*}" >&2 return 1 fi done } -# @param $1 port -# @param $2 container name -function wait_for_tcp_port_in_container() { - repeat_until_success_or_timeout $TEST_TIMEOUT_IN_SECONDS docker exec $2 /bin/sh -c "nc -z 0.0.0.0 $1" -} - -# @param $1 name of the postfix container -function wait_for_smtp_port_in_container() { - wait_for_tcp_port_in_container 25 $1 -} - -# @param $1 name of the postfix container -function wait_for_amavis_port_in_container() { - wait_for_tcp_port_in_container 10024 $1 -} - -# @param $1 name of the postfix container -function wait_for_finished_setup_in_container() { - local status=0 - repeat_until_success_or_timeout $TEST_TIMEOUT_IN_SECONDS sh -c "docker logs $1 | grep 'is up and running'" || status=1 - if [[ $status -eq 1 ]]; then - echo "Last $NUMBER_OF_LOG_LINES lines of container \`$1\`'s log" - docker logs $1 | tail -n $NUMBER_OF_LOG_LINES +# like repeat_until_success_or_timeout but with wrapping the command to run into `run` for later bats consumption +# @param ${1} timeout +# @param ... test command to run +function run_until_success_or_timeout { + if ! [[ ${1} =~ ^[0-9]+$ ]]; then + echo "First parameter for timeout must be an integer, recieved \"${1}\"" + return 1 fi - return $status + local TIMEOUT=${1} + local STARTTIME=${SECONDS} + shift 1 + until run "${@}" && [[ $status -eq 0 ]] + do + sleep 1 + if (( SECONDS - STARTTIME > TIMEOUT )); then + echo "Timed out on command: ${*}" >&2 + return 1 + fi + done } -SETUP_FILE_MARKER="$BATS_TMPDIR/`basename \"$BATS_TEST_FILENAME\"`.setup_file" +# @param ${1} timeout +# @param ${2} container name +# @param ... test command for container +function repeat_in_container_until_success_or_timeout() { + local TIMEOUT="${1}" + local CONTAINER_NAME="${2}" + shift 2 + repeat_until_success_or_timeout --fatal-test "container_is_running ${CONTAINER_NAME}" "${TIMEOUT}" docker exec "${CONTAINER_NAME}" "${@}" +} + +function container_is_running() { + [[ "$(docker inspect -f '{{.State.Running}}' "${1}")" == "true" ]] +} + +# @param ${1} port +# @param ${2} container name +function wait_for_tcp_port_in_container() { + repeat_until_success_or_timeout --fatal-test "container_is_running ${2}" "${TEST_TIMEOUT_IN_SECONDS}" docker exec "${2}" /bin/sh -c "nc -z 0.0.0.0 ${1}" +} + +# @param ${1} name of the postfix container +function wait_for_smtp_port_in_container() { + wait_for_tcp_port_in_container 25 "${1}" +} + +# @param ${1} name of the postfix container +function wait_for_amavis_port_in_container() { + wait_for_tcp_port_in_container 10024 "${1}" +} + +# @param ${1} name of the postfix container +function wait_for_finished_setup_in_container() { + local STATUS=0 + repeat_until_success_or_timeout --fatal-test "container_is_running ${1}" "${TEST_TIMEOUT_IN_SECONDS}" sh -c "docker logs ${1} | grep 'is up and running'" || STATUS=1 + if [[ ${STATUS} -eq 1 ]]; then + echo "Last ${NUMBER_OF_LOG_LINES} lines of container \`${1}\`'s log" + docker logs "${1}" | tail -n "${NUMBER_OF_LOG_LINES}" + fi + return ${STATUS} +} + +SETUP_FILE_MARKER="${BATS_TMPDIR}/$(basename "${BATS_TEST_FILENAME}").setup_file" + +function native_setup_teardown_file_support() { + local VERSION_REGEX='([0-9]+)\.([0-9]+)\.([0-9]+)' + # bats versions that support setup_file out of the box don't need this + if [[ "${BATS_VERSION}" =~ ${VERSION_REGEX} ]]; then + numeric_version=$(( (BASH_REMATCH[1] * 100 + BASH_REMATCH[2]) * 100 + BASH_REMATCH[3] )) + if [[ ${numeric_version} -ge 10201 ]]; then + if [ "${BATS_TEST_NAME}" == 'test_first' ]; then + skip 'This version natively supports setup/teardown_file' + fi + return 0 + fi + fi + return 1 +} # use in setup() in conjunction with a `@test "first" {}` to trigger setup_file reliably function run_setup_file_if_necessary() { - if [ "$BATS_TEST_NAME" == 'test_first' ]; then + native_setup_teardown_file_support && return 0 + if [ "${BATS_TEST_NAME}" == 'test_first' ]; then # prevent old markers from marking success or get an error if we cannot remove due to permissions - rm -f "$SETUP_FILE_MARKER" + rm -f "${SETUP_FILE_MARKER}" setup_file - touch "$SETUP_FILE_MARKER" + touch "${SETUP_FILE_MARKER}" else - if [ ! -f "$SETUP_FILE_MARKER" ]; then + if [ ! -f "${SETUP_FILE_MARKER}" ]; then skip "setup_file failed" return 1 fi @@ -73,9 +139,53 @@ function run_setup_file_if_necessary() { # use in teardown() in conjunction with a `@test "last" {}` to trigger teardown_file reliably function run_teardown_file_if_necessary() { - if [ "$BATS_TEST_NAME" == 'test_last' ]; then + native_setup_teardown_file_support && return 0 + if [ "${BATS_TEST_NAME}" == 'test_last' ]; then # cleanup setup file marker - rm -f "$SETUP_FILE_MARKER" + rm -f "${SETUP_FILE_MARKER}" teardown_file fi } + +# get the private config path for the given container or test file, if no container name was given +function private_config_path() { + echo "${PWD}/test/duplicate_configs/${1:-$(basename "${BATS_TEST_FILENAME}")}" +} + +# @param ${1} relative source in test/config folder +# @param ${2} (optional) container name, defaults to ${BATS_TEST_FILENAME} +# @return path to the folder where the config is duplicated +function duplicate_config_for_container() { + local OUTPUT_FOLDER + OUTPUT_FOLDER="$(private_config_path "${2}")" || return $? + rm -rf "${OUTPUT_FOLDER:?}/" || return $? # cleanup + mkdir -p "${OUTPUT_FOLDER}" || return $? + cp -r "${PWD}/test/config/${1:?}/." "${OUTPUT_FOLDER}" || return $? + echo "${OUTPUT_FOLDER}" +} + +function container_has_service_running() { + local CONTAINER_NAME="${1}" + local SERVICE_NAME="${2}" + docker exec "${CONTAINER_NAME}" /usr/bin/supervisorctl status "${SERVICE_NAME}" | grep RUNNING >/dev/null +} + +function wait_for_service() { + local CONTAINER_NAME="${1}" + local SERVICE_NAME="${2}" + repeat_until_success_or_timeout --fatal-test "container_is_running ${CONTAINER_NAME}" "${TEST_TIMEOUT_IN_SECONDS}" \ + container_has_service_running "${CONTAINER_NAME}" "${SERVICE_NAME}" +} + +function wait_for_changes_to_be_detected_in_container() { + local CONTAINER_NAME="${1}" + local TIMEOUT=${TEST_TIMEOUT_IN_SECONDS} + # shellcheck disable=SC2016 + repeat_in_container_until_success_or_timeout "${TIMEOUT}" "${CONTAINER_NAME}" bash -c 'source /usr/local/bin/helper_functions.sh; cmp --silent -- <(_monitored_files_checksums) "${CHKSUM_FILE}" >/dev/null' +} + +function wait_for_empty_mail_queue_in_container() { + local CONTAINER_NAME="${1}" + local TIMEOUT=${TEST_TIMEOUT_IN_SECONDS} + repeat_in_container_until_success_or_timeout "${TIMEOUT}" "${CONTAINER_NAME}" bash -c '[[ $(mailq) == *"Mail queue is empty"* ]]' +} diff --git a/test/tests.bats b/test/tests.bats index 5635fecc..803ef294 100644 --- a/test/tests.bats +++ b/test/tests.bats @@ -2,25 +2,88 @@ load 'test_helper/bats-support/load' load 'test_helper/bats-assert/load' load 'test_helper/common' -# -# shared functions -# +export IMAGE_NAME +IMAGE_NAME="${NAME}" -function wait_for_service() { - containerName=$1 - serviceName=$2 - count=0 - while ! (docker exec $containerName /usr/bin/supervisorctl status $serviceName | grep RUNNING >/dev/null) - do - ((count++)) && ((count==30)) && break - sleep 5 - done - return $(docker exec $containerName /usr/bin/supervisorctl status $serviceName | grep RUNNING >/dev/null) +setup() { + run_setup_file_if_necessary } -function count_processed_changes() { - containerName=$1 - docker exec $containerName cat /var/log/supervisor/changedetector.log | grep "Change detected" | wc -l +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" + 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 ENABLE_CLAMAV=1 \ + -e SPOOF_PROTECTION=1 \ + -e ENABLE_SPAMASSASSIN=1 \ + -e REPORT_RECIPIENT=user1@localhost.localdomain \ + -e REPORT_SENDER=report1@mail.my-domain.com \ + -e SA_TAG=-5.0 \ + -e SA_TAG2=2.0 \ + -e SA_KILL=3.0 \ + -e SA_SPAM_SUBJECT="SPAM: " \ + -e VIRUSMAILS_DELETE_DELAY=7 \ + -e ENABLE_SRS=1 \ + -e SASL_PASSWD="external-domain.com username:password" \ + -e ENABLE_MANAGESIEVE=1 \ + --cap-add=SYS_PTRACE \ + -e PERMIT_DOCKER=host \ + -e DMS_DEBUG=0 \ + -h mail.my-domain.com -t "${NAME}" + + wait_for_finished_setup_in_container mail + + # generate accounts after container has been started + docker run --rm -e MAIL_USER=added@localhost.localdomain -e MAIL_PASS=mypassword -t "${NAME}" /bin/sh -c 'echo "${MAIL_USER}|$(doveadm pw -s SHA512-CRYPT -u ${MAIL_USER} -p ${MAIL_PASS})"' >> "${PRIVATE_CONFIG}/postfix-accounts.cf" + docker exec mail addmailuser 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 beeing updated after all changes have been applied + wait_for_changes_to_be_detected_in_container mail + + wait_for_smtp_port_in_container mail + + # wait for clamav to be fully setup or we will get errors on the log + repeat_in_container_until_success_or_timeout 60 mail test -e /var/run/clamav/clamd.ctl + + # sending test mails + docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/amavis-spam.txt" + docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/amavis-virus.txt" + 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-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() { + run_teardown_file_if_necessary +} + +teardown_file() { + docker rm -f mail +} + +# this test must come first to reliably identify when to run setup_file +@test "first" { + skip 'Starting testing of letsencrypt SSL' } # @@ -28,53 +91,15 @@ function count_processed_changes() { # @test "checking configuration: user-patches.sh executed" { - run echo -n "`docker logs mail | grep 'user\-patches\.sh'`" + run docker logs mail assert_output --partial "Default user-patches.sh successfully executed" } @test "checking configuration: hostname/domainname" { - run docker run `docker inspect --format '{{ .Config.Image }}' mail` + run docker run "${IMAGE_NAME:?}" assert_success } -@test "checking configuration: hostname/domainname override: check container hostname is applied correctly" { - run docker exec mail_override_hostname /bin/bash -c "hostname | grep unknown.domain.tld" - assert_success -} - -@test "checking configuration: hostname/domainname override: check overriden hostname is applied to all configs" { - run docker exec mail_override_hostname /bin/bash -c "cat /etc/mailname | grep my-domain.com" - assert_success - run docker exec mail_override_hostname /bin/bash -c "postconf -n | grep mydomain | grep my-domain.com" - assert_success - run docker exec mail_override_hostname /bin/bash -c "postconf -n | grep myhostname | grep mail.my-domain.com" - assert_success - run docker exec mail_override_hostname /bin/bash -c "doveconf | grep hostname | grep mail.my-domain.com" - assert_success - run docker exec mail_override_hostname /bin/bash -c "cat /etc/opendmarc.conf | grep AuthservID | grep mail.my-domain.com" - assert_success - run docker exec mail_override_hostname /bin/bash -c "cat /etc/opendmarc.conf | grep TrustedAuthservIDs | grep mail.my-domain.com" - assert_success - run docker exec mail_override_hostname /bin/bash -c "cat /etc/amavis/conf.d/05-node_id | grep myhostname | grep mail.my-domain.com" - assert_success -} - -@test "checking configuration: hostname/domainname override: check hostname in postfix HELO message" { - run docker exec mail_override_hostname /bin/bash -c "nc -w 1 0.0.0.0 25 | grep mail.my-domain.com" - assert_success -} - -@test "checking configuration: hostname/domainname override: check headers of received mail" { - run docker exec mail_override_hostname /bin/sh -c "ls -A /var/mail/localhost.localdomain/user1/new | wc -l | grep 1" - assert_success - run docker exec mail_override_hostname /bin/sh -c "cat /var/mail/localhost.localdomain/user1/new/* | grep mail.my-domain.com" - assert_success - - # test whether the container hostname is not found in received mail - run docker exec mail_override_hostname /bin/sh -c "cat /var/mail/localhost.localdomain/user1/new/* | grep unknown.domain.tld" - assert_failure -} - # # processes # @@ -114,11 +139,6 @@ function count_processed_changes() { assert_failure } -@test "checking process: clamav (clamav disabled by ENABLED_CLAMAV=0)" { - run docker exec mail_disabled_clamav_spamassassin /bin/bash -c "ps aux --forest | grep -v grep | grep '/usr/sbin/clamd'" - assert_failure -} - # # imap # @@ -182,7 +202,8 @@ function count_processed_changes() { } @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 | grep 'authentication failed'" + 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 } @@ -192,7 +213,8 @@ function count_processed_changes() { } @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 | grep 'authentication failed'" + 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 } @@ -219,7 +241,7 @@ function count_processed_changes() { @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= @@ -314,9 +336,9 @@ EOF @test "checking accounts: user accounts" { run docker exec mail doveadm user '*' assert_success - [ "${lines[0]}" = "user1@localhost.localdomain" ] - [ "${lines[1]}" = "user2@otherdomain.tld" ] - [ "${lines[2]}" = "added@localhost.localdomain" ] + assert_line --index 0 "user1@localhost.localdomain" + assert_line --index 1 "user2@otherdomain.tld" + assert_line --index 2 "added@localhost.localdomain" } @test "checking accounts: user mail folder for user1" { @@ -346,9 +368,9 @@ EOF @test "checking postfix: vhost file is correct" { run docker exec mail cat /etc/postfix/vhost assert_success - [ "${lines[0]}" = "localdomain2.com" ] - [ "${lines[1]}" = "localhost.localdomain" ] - [ "${lines[2]}" = "otherdomain.tld" ] + assert_line --index 0 "localdomain2.com" + assert_line --index 1 "localhost.localdomain" + assert_line --index 2 "otherdomain.tld" } @test "checking postfix: main.cf overrides" { @@ -384,11 +406,6 @@ EOF assert_failure } -@test "checking spamassassin: should not be listed in amavis when disabled" { - run docker exec mail_disabled_clamav_spamassassin /bin/sh -c "grep -i 'ANTI-SPAM-SA code' /var/log/mail/mail.log | grep 'NOT loaded'" - assert_success -} - @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 @@ -406,16 +423,6 @@ EOF assert_success } -@test "checking clamav: should not be listed in amavis when disabled" { - run docker exec mail_disabled_clamav_spamassassin grep -i 'Found secondary av scanner ClamAV-clamscan' /var/log/mail/mail.log - assert_failure -} - -@test "checking clamav: should not be called when disabled" { - run docker exec mail_disabled_clamav_spamassassin grep -i 'connect to /var/run/clamav/clamd.ctl failed' /var/log/mail/mail.log - assert_failure -} - # # opendkim # @@ -427,7 +434,16 @@ EOF } @test "checking opendkim: /etc/opendkim/KeyTable dummy file generated without keys provided" { - run docker exec mail_smtponly_without_config /bin/bash -c "cat /etc/opendkim/KeyTable" + docker run --rm -d --name mail_smtponly_without_config \ + -e SMTP_ONLY=1 \ + -e ENABLE_LDAP=1 \ + -e PERMIT_DOCKER=network \ + -e OVERRIDE_HOSTNAME=mail.mydomain.com \ + -t "${NAME}" + + teardown() { docker rm -f mail_smtponly_without_config; } + + run repeat_in_container_until_success_or_timeout 15 mail_smtponly_without_config /bin/bash -c "cat /etc/opendkim/KeyTable" assert_success } @@ -448,19 +464,23 @@ EOF # Instead it tests the file-size (here 511) - which may differ with a different domain names # This test may be re-used as a global test to provide better test coverage. @test "checking opendkim: generator creates default keys size" { + local PRIVATE_CONFIG + PRIVATE_CONFIG="$(duplicate_config_for_container . mail_default_key_size)" # Prepare default key size 2048 - rm -rf "$(pwd)/test/config/keyDefault" && mkdir -p "$(pwd)/test/config/keyDefault" + rm -rf "${PRIVATE_CONFIG}/keyDefault" + mkdir -p "${PRIVATE_CONFIG}/keyDefault" + run docker run --rm \ - -v "$(pwd)/test/config/keyDefault/":/tmp/docker-mailserver/ \ - -v "$(pwd)/test/config/postfix-accounts.cf":/tmp/docker-mailserver/postfix-accounts.cf \ - -v "$(pwd)/test/config/postfix-virtual.cf":/tmp/docker-mailserver/postfix-virtual.cf \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c 'generate-dkim-config | wc -l' + -v "${PRIVATE_CONFIG}/keyDefault/":/tmp/docker-mailserver/ \ + -v "${PRIVATE_CONFIG}/postfix-accounts.cf":/tmp/docker-mailserver/postfix-accounts.cf \ + -v "${PRIVATE_CONFIG}/postfix-virtual.cf":/tmp/docker-mailserver/postfix-virtual.cf \ + "${IMAGE_NAME:?}" /bin/sh -c 'generate-dkim-config | wc -l' assert_success assert_output 6 run docker run --rm \ - -v "$(pwd)/test/config/keyDefault/opendkim":/etc/opendkim \ - `docker inspect --format '{{ .Config.Image }}' mail` \ + -v "${PRIVATE_CONFIG}/keyDefault/opendkim":/etc/opendkim \ + "${IMAGE_NAME:?}" \ /bin/sh -c 'stat -c%s /etc/opendkim/keys/localhost.localdomain/mail.txt' assert_success @@ -471,19 +491,22 @@ EOF # Instead it tests the file-size (here 511) - which may differ with a different domain names # This test may be re-used as a global test to provide better test coverage. @test "checking opendkim: generator creates key size 2048" { + local PRIVATE_CONFIG + PRIVATE_CONFIG="$(duplicate_config_for_container . mail_key_size_2048)" # Prepare set key size 2048 - rm -rf "$(pwd)/test/config/key2048" && mkdir -p "$(pwd)/test/config/key2048" + rm -rf "${PRIVATE_CONFIG}/key2048" + mkdir -p "${PRIVATE_CONFIG}/config/key2048" run docker run --rm \ - -v "$(pwd)/test/config/key2048/":/tmp/docker-mailserver/ \ - -v "$(pwd)/test/config/postfix-accounts.cf":/tmp/docker-mailserver/postfix-accounts.cf \ - -v "$(pwd)/test/config/postfix-virtual.cf":/tmp/docker-mailserver/postfix-virtual.cf \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c 'generate-dkim-config 2048 | wc -l' + -v "${PRIVATE_CONFIG}/key2048/":/tmp/docker-mailserver/ \ + -v "${PRIVATE_CONFIG}/postfix-accounts.cf":/tmp/docker-mailserver/postfix-accounts.cf \ + -v "${PRIVATE_CONFIG}/postfix-virtual.cf":/tmp/docker-mailserver/postfix-virtual.cf \ + "${IMAGE_NAME:?}" /bin/sh -c 'generate-dkim-config 2048 | wc -l' assert_success assert_output 6 run docker run --rm \ - -v "$(pwd)/test/config/key2048/opendkim":/etc/opendkim \ - `docker inspect --format '{{ .Config.Image }}' mail` \ + -v "${PRIVATE_CONFIG}/key2048/opendkim":/etc/opendkim \ + "${IMAGE_NAME:?}" \ /bin/sh -c 'stat -c%s /etc/opendkim/keys/localhost.localdomain/mail.txt' assert_success @@ -494,19 +517,22 @@ EOF # Instead it tests the file-size (here 329) - which may differ with a different domain names # This test may be re-used as a global test to provide better test coverage. @test "checking opendkim: generator creates key size 1024" { + local PRIVATE_CONFIG + PRIVATE_CONFIG="$(duplicate_config_for_container . mail_key_size_1024)" # Prepare set key size 1024 - rm -rf "$(pwd)/test/config/key1024" && mkdir -p "$(pwd)/test/config/key1024" + rm -rf "${PRIVATE_CONFIG}/key1024" + mkdir -p "${PRIVATE_CONFIG}/key1024" run docker run --rm \ - -v "$(pwd)/test/config/key1024/":/tmp/docker-mailserver/ \ - -v "$(pwd)/test/config/postfix-accounts.cf":/tmp/docker-mailserver/postfix-accounts.cf \ - -v "$(pwd)/test/config/postfix-virtual.cf":/tmp/docker-mailserver/postfix-virtual.cf \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c 'generate-dkim-config 1024 | wc -l' + -v "${PRIVATE_CONFIG}/key1024/":/tmp/docker-mailserver/ \ + -v "${PRIVATE_CONFIG}/postfix-accounts.cf":/tmp/docker-mailserver/postfix-accounts.cf \ + -v "${PRIVATE_CONFIG}/postfix-virtual.cf":/tmp/docker-mailserver/postfix-virtual.cf \ + "${IMAGE_NAME:?}" /bin/sh -c 'generate-dkim-config 1024 | wc -l' assert_success assert_output 6 run docker run --rm \ - -v "$(pwd)/test/config/key1024/opendkim":/etc/opendkim \ - `docker inspect --format '{{ .Config.Image }}' mail` \ + -v "${PRIVATE_CONFIG}/key1024/opendkim":/etc/opendkim \ + "${IMAGE_NAME:?}" \ /bin/sh -c 'stat -c%s /etc/opendkim/keys/localhost.localdomain/mail.txt' assert_success @@ -514,140 +540,151 @@ EOF } @test "checking opendkim: generator creates keys, tables and TrustedHosts" { - rm -rf "$(pwd)/test/config/empty" && mkdir -p "$(pwd)/test/config/empty" + local PRIVATE_CONFIG + PRIVATE_CONFIG="$(duplicate_config_for_container . mail_dkim_generator_creates_keys_tables_TrustedHosts)" + rm -rf "${PRIVATE_CONFIG}/empty" + mkdir -p "${PRIVATE_CONFIG}/empty" run docker run --rm \ - -v "$(pwd)/test/config/empty/":/tmp/docker-mailserver/ \ - -v "$(pwd)/test/config/postfix-accounts.cf":/tmp/docker-mailserver/postfix-accounts.cf \ - -v "$(pwd)/test/config/postfix-virtual.cf":/tmp/docker-mailserver/postfix-virtual.cf \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c 'generate-dkim-config | wc -l' + -v "${PRIVATE_CONFIG}/empty/":/tmp/docker-mailserver/ \ + -v "${PRIVATE_CONFIG}/postfix-accounts.cf":/tmp/docker-mailserver/postfix-accounts.cf \ + -v "${PRIVATE_CONFIG}/postfix-virtual.cf":/tmp/docker-mailserver/postfix-virtual.cf \ + "${IMAGE_NAME:?}" /bin/sh -c 'generate-dkim-config | wc -l' assert_success assert_output 6 # Check keys for localhost.localdomain run docker run --rm \ - -v "$(pwd)/test/config/empty/opendkim":/etc/opendkim \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c 'ls -1 /etc/opendkim/keys/localhost.localdomain/ | wc -l' + -v "${PRIVATE_CONFIG}/empty/opendkim":/etc/opendkim \ + "${IMAGE_NAME:?}" /bin/sh -c 'ls -1 /etc/opendkim/keys/localhost.localdomain/ | wc -l' assert_success assert_output 2 # Check keys for otherdomain.tld run docker run --rm \ - -v "$(pwd)/test/config/empty/opendkim":/etc/opendkim \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c 'ls -1 /etc/opendkim/keys/otherdomain.tld | wc -l' + -v "${PRIVATE_CONFIG}/empty/opendkim":/etc/opendkim \ + "${IMAGE_NAME:?}" /bin/sh -c 'ls -1 /etc/opendkim/keys/otherdomain.tld | wc -l' assert_success assert_output 2 # Check presence of tables and TrustedHosts run docker run --rm \ - -v "$(pwd)/test/config/empty/opendkim":/etc/opendkim \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c "ls -1 /etc/opendkim | grep -E 'KeyTable|SigningTable|TrustedHosts|keys'|wc -l" + -v "${PRIVATE_CONFIG}/empty/opendkim":/etc/opendkim \ + "${IMAGE_NAME:?}" /bin/sh -c "ls -1 /etc/opendkim | grep -E 'KeyTable|SigningTable|TrustedHosts|keys'|wc -l" assert_success assert_output 4 } @test "checking opendkim: generator creates keys, tables and TrustedHosts without postfix-accounts.cf" { - rm -rf "$(pwd)/test/config/without-accounts" && mkdir -p "$(pwd)/test/config/without-accounts" + local PRIVATE_CONFIG + PRIVATE_CONFIG="$(duplicate_config_for_container . )" + rm -rf "${PRIVATE_CONFIG}/without-accounts" + mkdir -p "${PRIVATE_CONFIG}/without-accounts" run docker run --rm \ - -v "$(pwd)/test/config/without-accounts/":/tmp/docker-mailserver/ \ - -v "$(pwd)/test/config/postfix-virtual.cf":/tmp/docker-mailserver/postfix-virtual.cf \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c 'generate-dkim-config | wc -l' + -v "${PRIVATE_CONFIG}/without-accounts/":/tmp/docker-mailserver/ \ + -v "${PRIVATE_CONFIG}/postfix-virtual.cf":/tmp/docker-mailserver/postfix-virtual.cf \ + "${IMAGE_NAME:?}" /bin/sh -c 'generate-dkim-config | wc -l' assert_success assert_output 5 # Check keys for localhost.localdomain run docker run --rm \ - -v "$(pwd)/test/config/without-accounts/opendkim":/etc/opendkim \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c 'ls -1 /etc/opendkim/keys/localhost.localdomain/ | wc -l' + -v "${PRIVATE_CONFIG}/without-accounts/opendkim":/etc/opendkim \ + "${IMAGE_NAME:?}" /bin/sh -c 'ls -1 /etc/opendkim/keys/localhost.localdomain/ | wc -l' assert_success assert_output 2 # Check keys for otherdomain.tld # run docker run --rm \ - # -v "$(pwd)/test/config/without-accounts/opendkim":/etc/opendkim \ - # `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c 'ls -1 /etc/opendkim/keys/otherdomain.tld | wc -l' + # -v "${PRIVATE_CONFIG}/without-accounts/opendkim":/etc/opendkim \ + # "${IMAGE_NAME:?}" /bin/sh -c 'ls -1 /etc/opendkim/keys/otherdomain.tld | wc -l' # assert_success - # [ "$output" -eq 0 ] + # [ "${output}" -eq 0 ] # Check presence of tables and TrustedHosts run docker run --rm \ - -v "$(pwd)/test/config/without-accounts/opendkim":/etc/opendkim \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c "ls -1 /etc/opendkim | grep -E 'KeyTable|SigningTable|TrustedHosts|keys'|wc -l" + -v "${PRIVATE_CONFIG}/without-accounts/opendkim":/etc/opendkim \ + "${IMAGE_NAME:?}" /bin/sh -c "ls -1 /etc/opendkim | grep -E 'KeyTable|SigningTable|TrustedHosts|keys'|wc -l" assert_success assert_output 4 } @test "checking opendkim: generator creates keys, tables and TrustedHosts without postfix-virtual.cf" { - rm -rf "$(pwd)/test/config/without-virtual" && mkdir -p "$(pwd)/test/config/without-virtual" + local PRIVATE_CONFIG + PRIVATE_CONFIG="$(duplicate_config_for_container . "${BATS_TEST_NAME}")" + rm -rf "${PRIVATE_CONFIG}/without-virtual" + mkdir -p "${PRIVATE_CONFIG}/without-virtual" run docker run --rm \ - -v "$(pwd)/test/config/without-virtual/":/tmp/docker-mailserver/ \ - -v "$(pwd)/test/config/postfix-accounts.cf":/tmp/docker-mailserver/postfix-accounts.cf \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c 'generate-dkim-config | wc -l' + -v "${PRIVATE_CONFIG}/without-virtual/":/tmp/docker-mailserver/ \ + -v "${PRIVATE_CONFIG}/postfix-accounts.cf":/tmp/docker-mailserver/postfix-accounts.cf \ + "${IMAGE_NAME:?}" /bin/sh -c 'generate-dkim-config | wc -l' assert_success assert_output 5 # Check keys for localhost.localdomain run docker run --rm \ - -v "$(pwd)/test/config/without-virtual/opendkim":/etc/opendkim \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c 'ls -1 /etc/opendkim/keys/localhost.localdomain/ | wc -l' + -v "${PRIVATE_CONFIG}/without-virtual/opendkim":/etc/opendkim \ + "${IMAGE_NAME:?}" /bin/sh -c 'ls -1 /etc/opendkim/keys/localhost.localdomain/ | wc -l' assert_success assert_output 2 # Check keys for otherdomain.tld run docker run --rm \ - -v "$(pwd)/test/config/without-virtual/opendkim":/etc/opendkim \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c 'ls -1 /etc/opendkim/keys/otherdomain.tld | wc -l' + -v "${PRIVATE_CONFIG}/without-virtual/opendkim":/etc/opendkim \ + "${IMAGE_NAME:?}" /bin/sh -c 'ls -1 /etc/opendkim/keys/otherdomain.tld | wc -l' assert_success assert_output 2 # Check presence of tables and TrustedHosts run docker run --rm \ - -v "$(pwd)/test/config/without-virtual/opendkim":/etc/opendkim \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c "ls -1 /etc/opendkim | grep -E 'KeyTable|SigningTable|TrustedHosts|keys'|wc -l" + -v "${PRIVATE_CONFIG}/without-virtual/opendkim":/etc/opendkim \ + "${IMAGE_NAME:?}" /bin/sh -c "ls -1 /etc/opendkim | grep -E 'KeyTable|SigningTable|TrustedHosts|keys'|wc -l" assert_success assert_output 4 } @test "checking opendkim: generator creates keys, tables and TrustedHosts using domain name" { - rm -rf "$(pwd)/test/config/with-domain" && mkdir -p "$(pwd)/test/config/with-domain" + local PRIVATE_CONFIG + PRIVATE_CONFIG="$(duplicate_config_for_container . "${BATS_TEST_NAME}")" + rm -rf "${PRIVATE_CONFIG}/with-domain" && mkdir -p "${PRIVATE_CONFIG}/with-domain" run docker run --rm \ - -v "$(pwd)/test/config/with-domain/":/tmp/docker-mailserver/ \ - -v "$(pwd)/test/config/postfix-accounts.cf":/tmp/docker-mailserver/postfix-accounts.cf \ - -v "$(pwd)/test/config/postfix-virtual.cf":/tmp/docker-mailserver/postfix-virtual.cf \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c 'generate-dkim-config | wc -l' + -v "${PRIVATE_CONFIG}/with-domain/":/tmp/docker-mailserver/ \ + -v "${PRIVATE_CONFIG}/postfix-accounts.cf":/tmp/docker-mailserver/postfix-accounts.cf \ + -v "${PRIVATE_CONFIG}/postfix-virtual.cf":/tmp/docker-mailserver/postfix-virtual.cf \ + "${IMAGE_NAME:?}" /bin/sh -c 'generate-dkim-config | wc -l' assert_success assert_output 6 # Generate key using domain name run docker run --rm \ - -v "$(pwd)/test/config/with-domain/":/tmp/docker-mailserver/ \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c 'generate-dkim-domain testdomain.tld | wc -l' + -v "${PRIVATE_CONFIG}/with-domain/":/tmp/docker-mailserver/ \ + "${IMAGE_NAME:?}" /bin/sh -c 'generate-dkim-domain testdomain.tld | wc -l' assert_success assert_output 1 # Check keys for localhost.localdomain run docker run --rm \ - -v "$(pwd)/test/config/with-domain/opendkim":/etc/opendkim \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c 'ls -1 /etc/opendkim/keys/localhost.localdomain/ | wc -l' + -v "${PRIVATE_CONFIG}/with-domain/opendkim":/etc/opendkim \ + "${IMAGE_NAME:?}" /bin/sh -c 'ls -1 /etc/opendkim/keys/localhost.localdomain/ | wc -l' assert_success assert_output 2 # Check keys for otherdomain.tld run docker run --rm \ - -v "$(pwd)/test/config/with-domain/opendkim":/etc/opendkim \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c 'ls -1 /etc/opendkim/keys/otherdomain.tld | wc -l' + -v "${PRIVATE_CONFIG}/with-domain/opendkim":/etc/opendkim \ + "${IMAGE_NAME:?}" /bin/sh -c 'ls -1 /etc/opendkim/keys/otherdomain.tld | wc -l' assert_success assert_output 2 # Check keys for testdomain.tld run docker run --rm \ - -v "$(pwd)/test/config/with-domain/opendkim":/etc/opendkim \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c 'ls -1 /etc/opendkim/keys/testdomain.tld | wc -l' + -v "${PRIVATE_CONFIG}/with-domain/opendkim":/etc/opendkim \ + "${IMAGE_NAME:?}" /bin/sh -c 'ls -1 /etc/opendkim/keys/testdomain.tld | wc -l' assert_success assert_output 2 # Check presence of tables and TrustedHosts run docker run --rm \ - -v "$(pwd)/test/config/with-domain/opendkim":/etc/opendkim \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c "ls -1 /etc/opendkim | grep -E 'KeyTable|SigningTable|TrustedHosts|keys' | wc -l" + -v "${PRIVATE_CONFIG}/with-domain/opendkim":/etc/opendkim \ + "${IMAGE_NAME:?}" /bin/sh -c "ls -1 /etc/opendkim | grep -E 'KeyTable|SigningTable|TrustedHosts|keys' | wc -l" assert_success assert_output 4 # Check valid entries actually present in KeyTable run docker run --rm \ - -v "$(pwd)/test/config/with-domain/opendkim":/etc/opendkim \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c \ + -v "${PRIVATE_CONFIG}/with-domain/opendkim":/etc/opendkim \ + "${IMAGE_NAME:?}" /bin/sh -c \ "egrep 'localhost.localdomain|otherdomain.tld|localdomain2.com|testdomain.tld' /etc/opendkim/KeyTable | wc -l" assert_success assert_output 4 # Check valid entries actually present in SigningTable run docker run --rm \ - -v "$(pwd)/test/config/with-domain/opendkim":/etc/opendkim \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c \ + -v "${PRIVATE_CONFIG}/with-domain/opendkim":/etc/opendkim \ + "${IMAGE_NAME:?}" /bin/sh -c \ "egrep 'localhost.localdomain|otherdomain.tld|localdomain2.com|testdomain.tld' /etc/opendkim/SigningTable | wc -l" assert_success assert_output 4 @@ -687,20 +724,7 @@ EOF assert_success } -@test "checking SRS: SRS_DOMAINNAME is used correctly" { - run docker exec mail_srs_domainname grep "SRS_DOMAIN=srs.my-domain.com" /etc/default/postsrsd - assert_success -} -@test "checking SRS: OVERRIDE_HOSTNAME is handled correctly" { - run docker exec mail_override_hostname grep "SRS_DOMAIN=my-domain.com" /etc/default/postsrsd - assert_success -} - -@test "checking SRS: DOMAINNAME is handled correctly" { - run docker exec mail_domainname grep "SRS_DOMAIN=my-domain.com" /etc/default/postsrsd - assert_success -} @test "checking SRS: fallback to hostname is handled correctly" { run docker exec mail grep "SRS_DOMAIN=my-domain.com" /etc/default/postsrsd assert_success @@ -721,8 +745,8 @@ EOF } @test "checking amavis: VIRUSMAILS_DELETE_DELAY override works as expected" { - run docker run --rm -e VIRUSMAILS_DELETE_DELAY=2 `docker inspect --format '{{ .Config.Image }}' mail` /bin/bash -c 'echo $VIRUSMAILS_DELETE_DELAY | grep 2' - assert_success + run docker run --rm -e VIRUSMAILS_DELETE_DELAY=2 "${IMAGE_NAME:?}" /bin/bash -c 'echo "${VIRUSMAILS_DELETE_DELAY}"' + assert_output 2 } @test "checking amavis: old virusmail is wipped by cron" { @@ -857,7 +881,7 @@ EOF run docker exec mail /bin/sh -c "grep '^user3@domain\.tld|' -i /tmp/docker-mailserver/postfix-accounts.cf" assert_success - [ ! -z "$output" ] + [ -n "${output}" ] } @test "checking accounts: auser3 should have been added to /tmp/docker-mailserver/postfix-accounts.cf" { @@ -865,7 +889,7 @@ EOF run docker exec mail /bin/sh -c "grep '^auser3@domain\.tld|' -i /tmp/docker-mailserver/postfix-accounts.cf" assert_success - [ ! -z "$output" ] + [ -n "${output}" ] } @test "checking accounts: a.ser3 should have been added to /tmp/docker-mailserver/postfix-accounts.cf" { @@ -873,7 +897,7 @@ EOF run docker exec mail /bin/sh -c "grep '^a\.ser3@domain\.tld|' -i /tmp/docker-mailserver/postfix-accounts.cf" assert_success - [ ! -z "$output" ] + [ -n "${output}" ] } @test "checking accounts: user3 should have been removed from /tmp/docker-mailserver/postfix-accounts.cf but not auser3" { @@ -881,27 +905,23 @@ EOF run docker exec mail /bin/sh -c "grep '^user3@domain\.tld' -i /tmp/docker-mailserver/postfix-accounts.cf" assert_failure - [ -z "$output" ] + [ -z "${output}" ] run docker exec mail /bin/sh -c "grep '^auser3@domain\.tld' -i /tmp/docker-mailserver/postfix-accounts.cf" assert_success - [ ! -z "$output" ] + [ -n "${output}" ] } @test "checking user updating password for user in /tmp/docker-mailserver/postfix-accounts.cf" { docker exec mail /bin/sh -c "addmailuser user4@domain.tld mypassword" - initialpass=$(run docker exec mail /bin/sh -c "grep '^user4@domain\.tld' -i /tmp/docker-mailserver/postfix-accounts.cf") + initialpass=$(docker exec mail /bin/sh -c "grep '^user4@domain\.tld' -i /tmp/docker-mailserver/postfix-accounts.cf") sleep 2 docker exec mail /bin/sh -c "updatemailuser user4@domain.tld mynewpassword" sleep 2 - changepass=$(run docker exec mail /bin/sh -c "grep '^user4@domain\.tld' -i /tmp/docker-mailserver/postfix-accounts.cf") + changepass=$(docker exec mail /bin/sh -c "grep '^user4@domain\.tld' -i /tmp/docker-mailserver/postfix-accounts.cf") - if [ initialpass != changepass ]; then - status="0" - else - status="1" - fi + [ "$initialpass" != "$changepass" ] docker exec mail /bin/sh -c "delmailuser -y auser3@domain.tld" @@ -916,22 +936,24 @@ EOF @test "checking accounts: no error is generated when deleting a user if /tmp/docker-mailserver/postfix-accounts.cf is missing" { run docker run --rm \ - -v "$(pwd)/test/config/without-accounts/":/tmp/docker-mailserver/ \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c 'delmailuser -y user3@domain.tld' + -v "$(duplicate_config_for_container without-accounts/ without-accounts-deleting-user)":/tmp/docker-mailserver/ \ + "${IMAGE_NAME:?}" /bin/sh -c 'delmailuser -y user3@domain.tld' assert_success - [ -z "$output" ] + [ -z "${output}" ] } @test "checking accounts: user3 should have been added to /tmp/docker-mailserver/postfix-accounts.cf even when that file does not exist" { + local PRIVATE_CONFIG + PRIVATE_CONFIG=$(duplicate_config_for_container without-accounts/ without-accounts_file_does_not_exist) run docker run --rm \ - -v "$(pwd)/test/config/without-accounts/":/tmp/docker-mailserver/ \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c 'addmailuser user3@domain.tld mypassword' + -v "${PRIVATE_CONFIG}/without-accounts/":/tmp/docker-mailserver/ \ + "${IMAGE_NAME:?}" /bin/sh -c 'addmailuser user3@domain.tld mypassword' assert_success run docker run --rm \ - -v "$(pwd)/test/config/without-accounts/":/tmp/docker-mailserver/ \ - `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c 'grep user3@domain.tld -i /tmp/docker-mailserver/postfix-accounts.cf' + -v "${PRIVATE_CONFIG}/without-accounts/":/tmp/docker-mailserver/ \ + "${IMAGE_NAME:?}" /bin/sh -c 'grep user3@domain.tld -i /tmp/docker-mailserver/postfix-accounts.cf' assert_success - [ ! -z "$output" ] + [ -n "${output}" ] } @@ -1026,35 +1048,35 @@ EOF @test "checking quota: dovecot mailbox max size must be equal to postfix mailbox max size" { postfix_mailbox_size=$(docker exec mail sh -c "postconf | grep -Po '(?<=mailbox_size_limit = )[0-9]+'") - run echo "$postfix_mailbox_size" + run echo "${postfix_mailbox_size}" refute_output "" # dovecot relies on virtual_mailbox_size by default postfix_virtual_mailbox_size=$(docker exec mail sh -c "postconf | grep -Po '(?<=virtual_mailbox_limit = )[0-9]+'") - assert_equal "$postfix_virtual_mailbox_size" "$postfix_mailbox_size" + assert_equal "${postfix_virtual_mailbox_size}" "${postfix_mailbox_size}" - postfix_mailbox_size_mb=$(($postfix_mailbox_size / 1000000)) + postfix_mailbox_size_mb=$(( postfix_mailbox_size / 1000000)) dovecot_mailbox_size_mb=$(docker exec mail sh -c "doveconf | grep -oP '(?<=quota_rule \= \*\:storage=)[0-9]+'") - run echo "$dovecot_mailbox_size_mb" + run echo "${dovecot_mailbox_size_mb}" refute_output "" - assert_equal "$postfix_mailbox_size_mb" "$dovecot_mailbox_size_mb" + assert_equal "${postfix_mailbox_size_mb}" "${dovecot_mailbox_size_mb}" } @test "checking quota: dovecot message max size must be equal to postfix messsage max size" { postfix_message_size=$(docker exec mail sh -c "postconf | grep -Po '(?<=message_size_limit = )[0-9]+'") - run echo "$postfix_message_size" + run echo "${postfix_message_size}" refute_output "" - postfix_message_size_mb=$(($postfix_message_size / 1000000)) + postfix_message_size_mb=$(( postfix_message_size / 1000000)) dovecot_message_size_mb=$(docker exec mail sh -c "doveconf | grep -oP '(?<=quota_max_mail_size = )[0-9]+'") - run echo "$dovecot_message_size_mb" + run echo "${dovecot_message_size_mb}" refute_output "" - assert_equal "$postfix_message_size_mb" "$dovecot_message_size_mb" + assert_equal "${postfix_message_size_mb}" "${dovecot_message_size_mb}" } @test "checking quota: quota directive is removed when mailbox is removed" { @@ -1075,43 +1097,24 @@ EOF } @test "checking quota: dovecot applies user quota" { - sleep 15 # wait until any other change has finished + wait_for_changes_to_be_detected_in_container mail + run docker exec mail /bin/sh -c "doveadm quota get -u 'user1@localhost.localdomain' | grep 'User quota STORAGE'" assert_output --partial "- 0" - # set a quota - originalChangesProcessed=$(count_processed_changes mail) run docker exec mail /bin/sh -c "setquota user1@localhost.localdomain 50M" assert_success - # wait until change detector has processed the change - count=0 - while [ "${originalChangesProcessed}" = "$(count_processed_changes mail)" ] - do - ((count++)) && ((count==60)) && break - sleep 1 - done - [ "${originalChangesProcessed}" != "$(count_processed_changes mail)" ] - assert_success + wait_for_changes_to_be_detected_in_container mail # wait until quota has been updated run repeat_until_success_or_timeout 20 sh -c "docker exec mail sh -c 'doveadm quota get -u user1@localhost.localdomain | grep -oP \"(User quota STORAGE\s+[0-9]+\s+)51200(.*)\"'" assert_success - # remove the quota - originalChangesProcessed=$(count_processed_changes mail) run docker exec mail /bin/sh -c "delquota user1@localhost.localdomain" assert_success - # wait until change detector has processed the change - count=0 - while [ "${originalChangesProcessed}" = "$(count_processed_changes mail)" ] - do - ((count++)) && ((count==60)) && break - sleep 1 - done - [ "${originalChangesProcessed}" != "$(count_processed_changes mail)" ] - assert_success + wait_for_changes_to_be_detected_in_container mail # wait until quota has been updated run repeat_until_success_or_timeout 20 sh -c "docker exec mail sh -c 'doveadm quota get -u user1@localhost.localdomain | grep -oP \"(User quota STORAGE\s+[0-9]+\s+)-(.*)\"'" @@ -1119,22 +1122,13 @@ EOF } @test "checking quota: warn message received when quota exceeded" { - sleep 15 # wait until any other change has finished - - originalChangesProcessed=$(count_processed_changes mail) + wait_for_changes_to_be_detected_in_container mail # create user run docker exec mail /bin/sh -c "addmailuser quotauser@otherdomain.tld mypassword && setquota quotauser@otherdomain.tld 10k" assert_success - count=0 - while [ "${originalChangesProcessed}" = "$(count_processed_changes mail)" ] - do - ((count++)) && ((count==60)) && break - sleep 1 - done - [ "${originalChangesProcessed}" != "$(count_processed_changes mail)" ] - assert_success + wait_for_changes_to_be_detected_in_container mail # wait until quota has been updated run repeat_until_success_or_timeout 20 sh -c "docker exec mail sh -c 'doveadm quota get -u quotauser@otherdomain.tld | grep -oP \"(User quota STORAGE\s+[0-9]+\s+)10(.*)\"'" @@ -1159,6 +1153,7 @@ EOF run repeat_until_success_or_timeout 20 sh -c "docker logs mail | grep 'Quota exceeded (mailbox for user is full)'" assert_success + docker exec mail ls -l '/var/mail/otherdomain.tld/quotauser/new/' # ensure only the first big message and the warn message are present (other messages are rejected: mailbox is full) run docker exec mail sh -c 'ls /var/mail/otherdomain.tld/quotauser/new/ | wc -l' assert_success @@ -1206,13 +1201,13 @@ EOF @test "checking setup.sh: Without arguments: status 1, show help text" { run ./setup.sh assert_failure - [ "${lines[1]}" = "Usage: ./setup.sh [-i IMAGE_NAME] [-c CONTAINER_NAME] [args]" ] + assert_line --index 1 "Usage: ./setup.sh [-i IMAGE_NAME] [-c CONTAINER_NAME] [args]" } @test "checking setup.sh: Wrong arguments" { run ./setup.sh lol troll assert_failure - [ "${lines[1]}" = "Usage: ./setup.sh [-i IMAGE_NAME] [-c CONTAINER_NAME] [args]" ] + assert_line --index 1 "Usage: ./setup.sh [-i IMAGE_NAME] [-c CONTAINER_NAME] [args]" } # email @@ -1220,25 +1215,14 @@ EOF wait_for_service mail changedetector assert_success - originalChangesProcessed=$(count_processed_changes mail) - run ./setup.sh -c mail email add setup_email_add@example.com test_password assert_success - value=$(cat ./test/config/postfix-accounts.cf | grep setup_email_add@example.com | awk -F '|' '{print $1}') - [ "$value" = "setup_email_add@example.com" ] + value=$(grep setup_email_add@example.com "$(private_config_path mail)/postfix-accounts.cf" | awk -F '|' '{print $1}') + [ "${value}" = "setup_email_add@example.com" ] assert_success - # wait until change detector has processed the change - count=0 - while [ "${originalChangesProcessed}" = "$(count_processed_changes mail)" ] - do - ((count++)) && ((count==60)) && break - sleep 1 - done - - [ "${originalChangesProcessed}" != "$(count_processed_changes mail)" ] - assert_success + wait_for_changes_to_be_detected_in_container mail # Dovecot has been restarted, but this test often fails so presumably it may not be ready # Add a short sleep to see if that helps to make the test more stable @@ -1260,21 +1244,18 @@ EOF run ./setup.sh -c mail email add lorem@impsum.org test_test assert_success - initialpass=$(cat ./test/config/postfix-accounts.cf | grep lorem@impsum.org | awk -F '|' '{print $2}') - [ "$initialpass" != "" ] + initialpass=$(grep lorem@impsum.org "$(private_config_path mail)/postfix-accounts.cf" | awk -F '|' '{print $2}') + [ "${initialpass}" != "" ] assert_success run ./setup.sh -c mail email update lorem@impsum.org my password assert_success - updatepass=$(cat ./test/config/postfix-accounts.cf | grep lorem@impsum.org | awk -F '|' '{print $2}') - [ "$updatepass" != "" ] - assert_success + updatepass=$(grep lorem@impsum.org "$(private_config_path mail)/postfix-accounts.cf" | awk -F '|' '{print $2}') + [ "${updatepass}" != "" ] + [ "${initialpass}" != "${updatepass}" ] - [ "$initialpass" != "$updatepass" ] - assert_success - - docker exec mail doveadm pw -t "$updatepass" -p 'my password' | grep 'verified' + docker exec mail doveadm pw -t "${updatepass}" -p 'my password' | grep 'verified' assert_success } @@ -1290,7 +1271,7 @@ EOF # # run docker exec mail ls /var/mail/impsum.org/lorem # assert_failure - run grep lorem@impsum.org ./test/config/postfix-accounts.cf + run grep lorem@impsum.org "$(private_config_path mail)/postfix-accounts.cf" assert_failure } @@ -1423,14 +1404,14 @@ EOF # debug @test "checking setup.sh: setup.sh debug fetchmail" { run ./setup.sh -c mail debug fetchmail - [ "$status" -eq 11 ] - [[ "$output" == *"fetchmail: normal termination, status 11"* ]] + assert_failure 11 + assert_output --partial "fetchmail: normal termination, status 11" } @test "checking setup.sh: setup.sh debug inspect" { run ./setup.sh -c mail debug inspect assert_success - [ "${lines[0]}" = "Image: tvial/docker-mailserver:testing" ] - [ "${lines[1]}" = "Container: mail" ] + assert_line --index 0 "Image: tvial/docker-mailserver:testing" + assert_line --index 1 "Container: mail" } @test "checking setup.sh: setup.sh debug login ls" { run ./setup.sh -c mail debug login ls @@ -1486,15 +1467,12 @@ EOF @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" assert_success - - run docker exec mail_override_hostname /bin/sh -c "grep 'postmaster_address = postmaster@my-domain.com' /etc/dovecot/conf.d/15-lda.conf" - assert_success } @test "checking spoofing: rejects sender forging" { # checking rejection of spoofed sender - run docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/added-smtp-auth-spoofed.txt | grep 'Sender address rejected: not owned by user'" - assert_success + run docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/added-smtp-auth-spoofed.txt" + assert_output --partial 'Sender address rejected: not owned by user' } @test "checking spoofing: accepts sending as alias" { @@ -1604,11 +1582,6 @@ EOF assert_success } -@test "checking restart of process: clamav (clamav disabled by ENABLED_CLAMAV=0)" { - run docker exec mail_disabled_clamav_spamassassin /bin/bash -c "pkill -f clamd && sleep 10 && ps aux --forest | grep -v grep | grep '/usr/sbin/clamd'" - assert_failure -} - # # root mail delivery # @@ -1618,11 +1591,6 @@ EOF assert_success } -# -# clean exit -# - -@test "checking that the container stops cleanly" { - run docker stop -t 60 mail_override_hostname - assert_success +@test "last" { + # this test is only there to reliably mark the end for the teardown_file }