Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tests(refactor): Adjust mail_changedetector + change detection helpers #2997

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
abafd43
tests(refactor): `mail_changedetector.bats` - Leverage DRY methods
polarathene Jan 11, 2023
5b28815
tests(chore): Remove sleep and redundant change event
polarathene Jan 11, 2023
4530f25
tests(refactor): Add more DRY methods
polarathene Jan 11, 2023
4729d10
tests(chore): Revise the change detection helper method
polarathene Jan 11, 2023
4378031
tests(chore): Migrate to current test conventions
polarathene Jan 11, 2023
3596a29
tests(chore): Remove legacy change detection
polarathene Jan 11, 2023
c3dbe4a
chore: Lock removal should not incur `sleep 5` afterwards
polarathene Jan 11, 2023
5a4bd9b
tests(chore): `tls_letsencrypt.bats` leverage improved change detection
polarathene Jan 11, 2023
c9a8d5c
tests(chore): Convert `setup-cli.bats` to new test conventions
polarathene Jan 11, 2023
ae3dc4c
tests(chore): Extract out helpers related to change-detection
polarathene Jan 11, 2023
fa4ec9c
tests(refactor): `tls_letsencrypt.bats` remove `_get_service_logs()`
polarathene Jan 11, 2023
a04a147
tests(chore): Remove common setup methods from `test_helper/common.bash`
polarathene Jan 11, 2023
a28d19d
chore: Minor style changes
polarathene Jan 12, 2023
91610dc
chore: Relocate inline docs
polarathene Jan 12, 2023
32aea55
chore: Simplify setting `EXPECTED_COUNT`
polarathene Jan 14, 2023
655bb50
Revert "chore: Simplify setting `EXPECTED_COUNT`"
polarathene Jan 14, 2023
0893c98
chore: Use `|| true` to simplify setting `EXPECTED_COUNT` correctly
polarathene Jan 15, 2023
cbf58ef
Merge branch 'master' into tests/migrate-changedetector
georglauterbach Jan 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions target/scripts/helpers/lock.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,16 @@ function _create_lock
LOCK_FILE="/tmp/docker-mailserver/${SCRIPT_NAME}.lock"
while [[ -e "${LOCK_FILE}" ]]
do
_log 'warn' "Lock file '${LOCK_FILE}' exists - another execution of '${SCRIPT_NAME}' is happening - trying again shortly"
# Handle stale lock files left behind on crashes
# or premature/non-graceful exits of containers while they're making changes
if [[ -n "$(find "${LOCK_FILE}" -mmin +1 2>/dev/null)" ]]
then
_log 'warn' 'Lock file older than 1 minute - removing stale lock file'
rm -f "${LOCK_FILE}"
else
_log 'warn' "Lock file '${LOCK_FILE}' exists - another execution of '${SCRIPT_NAME}' is happening - trying again shortly"
sleep 5
fi
sleep 5
done
trap _remove_lock EXIT

Expand Down
29 changes: 29 additions & 0 deletions test/helper/change-detection.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/bash

load "${REPOSITORY_ROOT}/test/helper/common"

function wait_until_change_detection_event_begins() {
local MATCH_CONTENT='Change detected'
local MATCH_IN_LOG='/var/log/supervisor/changedetector.log'

_wait_until_expected_count_is_matched "${@}"
}

# NOTE: Change events can start and finish all within < 1 sec,
# Reliably track the completion of a change event by counting events:
function wait_until_change_detection_event_completes() {
local MATCH_CONTENT='Completed handling of detected change'
local MATCH_IN_LOG='/var/log/supervisor/changedetector.log'

_wait_until_expected_count_is_matched "${@}"
}

function _get_logs_since_last_change_detection() {
local CONTAINER_NAME=${1}
local MATCH_IN_FILE='/var/log/supervisor/changedetector.log'
local MATCH_STRING='Change detected'

# Read file in reverse, collect lines until match with sed is found,
# then stop and return these lines back in original order (flipped again through tac):
docker exec "${CONTAINER_NAME}" bash -c "tac ${MATCH_IN_FILE} | sed '/${MATCH_STRING}/q' | tac"
}
48 changes: 24 additions & 24 deletions test/helper/common.bash
Original file line number Diff line number Diff line change
Expand Up @@ -180,40 +180,40 @@ function wait_for_service() {
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}
# NOTE: Relies on ENV `LOG_LEVEL=debug` or higher
function _wait_until_expected_count_is_matched() {
function __get_count() {
# NOTE: `|| true` required due to `set -e` usage:
# https://github.com/docker-mailserver/docker-mailserver/pull/2997#discussion_r1070583876
docker exec "${CONTAINER_NAME}" grep --count "${MATCH_CONTENT}" "${MATCH_IN_LOG}" || true
}

# shellcheck disable=SC2016
repeat_in_container_until_success_or_timeout "${TIMEOUT}" "${CONTAINER_NAME}" bash -c 'source /usr/local/bin/helpers/index.sh; _obtain_hostname_and_domainname; cmp --silent -- <(_monitored_files_checksums) "${CHKSUM_FILE}" >/dev/null'
}
# WARNING: Keep in mind it is a '>=' comparison.
# If you provide an explict count to match, ensure it is not too low to cause a false-positive.
function __has_expected_count() {
[[ $(__get_count) -ge "${EXPECTED_COUNT}" ]]
}

local CONTAINER_NAME=${1}
local EXPECTED_COUNT=${2}

# NOTE: Relies on ENV `LOG_LEVEL=debug` or higher
function wait_until_change_detection_event_completes() {
local CONTAINER_NAME="${1}"
# Ensure early failure if arg is missing:
assert_not_equal "${CONTAINER_NAME}" ""
assert_not_equal "${CONTAINER_NAME}" ''

# Ensure the container is configured with the required `LOG_LEVEL` ENV:
assert_regex \
$(docker exec "${CONTAINER_NAME}" env | grep '^LOG_LEVEL=') \
'=(debug|trace)$'

# NOTE: Change events can start and finish all within < 1 sec,
# Reliably track the completion of a change event by comparing the before/after count:
function __change_event_count() {
docker exec "${CONTAINER_NAME}" grep --count "${CHANGE_EVENT_END}" /var/log/supervisor/changedetector.log
}

function __is_changedetector_finished() {
[[ $(__change_event_count) -gt "${NUM_CHANGE_EVENTS_BEFORE}" ]]
}

# Count by completions of this debug log line from `check-for-changes.sh`:
local CHANGE_EVENT_END='Completed handling of detected change'
local NUM_CHANGE_EVENTS_BEFORE=$(__change_event_count)
# Default behaviour is to wait until one new match is found (eg: incremented),
# unless explicitly set (useful for waiting on a min count to be reached):
if [[ -z $EXPECTED_COUNT ]]
then
# +1 of starting count:
EXPECTED_COUNT=$(( $(__get_count) + 1 ))
fi

repeat_until_success_or_timeout 60 __is_changedetector_finished
repeat_until_success_or_timeout 20 __has_expected_count
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't compared, but casper had suggested a different approach than polling like I'm doing here.

Suggested change
repeat_until_success_or_timeout 20 __has_expected_count
timeout 60 docker exec "${CONTAINER_NAME}" bash -c "tail -F '${MATCH_IN_LOG}' | grep --max-count ${EXPECTED_COUNT} '${MATCH_CONTENT}'"

That will take a stream of the log and exit once the result count is matched. It needed to use bash -c, even if using docker logs --follow for the input as I found it'd hang otherwise (presumably something to do with stdin not closing?)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot reproduce the hanging. I tried this:

# exit as soon as the keyword "spawned" appears
docker logs -n0 -f mail | grep -q spawned

Then in another session:

docker exec mail supervisorctl restart update-check

PS: The container must be running with SUPERVISOR_LOGLEVEL=info I think, that this example works.

This comment was marked as outdated.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to reproduce on current master the same issue. I tested with replacing this:

# message will be added to a queue with varying delay until amavis receives it
run repeat_until_success_or_timeout 60 bash -c "docker logs ${CONTAINER_NAME} | grep 'Passed SPAM {RelayedTaggedInbound,Quarantined}'"
assert_success

Reproduction
# Passes before timing out:
timeout 20 docker exec "${CONTAINER_NAME}" bash -c "tail -F /var/log/mail/mail.log | grep --max-count 1 'Passed SPAM'"
# Same result here:
docker exec "${CONTAINER_NAME}" bash -c "timeout 20 tail -F /var/log/mail/mail.log | grep --max-count 1 'Passed SPAM'"
# Times out with failure, and due to -q has no output:
timeout 20 bash -c "docker logs --follow ${CONTAINER_NAME} | grep -q 'Passed SPAM'"
# Times out with failure, but correctly shows output of requested max lines (even if there were more present)
timeout 20 bash -c "docker logs --follow ${CONTAINER_NAME} | grep --max-count 1 'Passed SPAM'"
# Times out but **passes** (docker logs was stopped by timeout, grep was successful)
timeout 20 docker logs --follow "${CONTAINER_NAME}" | grep --max-count 1 'Passed SPAM'
function _passed_spam() {
  # Any method from above here
}

# Replace as 2nd assert in either test case of `tests/parallel/set1/spam_virus/spam_junk_folder.bats:
run _passed_spam
assert_success

Perhaps it's something different with our environments or how docker logs --follow works on our machines? For me, given the above observations, it doesn't seem like the stream is closed?

I had also verified by running the same command in another terminal session directly while the test was running. It would find the match and output it, but not exit until the timeout was reached.

I've had a similar experience with -q (not as nice as --max-count), where either option was working at least for decoder as the value to match when the container was starting up (I could not reproduce with Passed SPAM for some reason..). I could repeat the command in the terminal several times and it'll exit quickly with either option successful. However a few seconds after that and it would fail, even though the logs command would clearly show the lines 🤷‍♂️

Clearly something is iffy there, at least on my end :\

UPDATE: My grep command is the problem.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I swapped grep for rg (ripgrep), and now it works as you'd expect...

So something wrong with my grep command I guess? 🤷‍♂️


I'd rather avoid the -q or --max-count approach, at least with a local grep call, as it seems that's where the problem is. The containers grep was working fine with tail, which would be ok as it avoids false positives when I run tests locally.

We could add ripgrep as a dependency for testing like we do with jq I suppose? (but then CI may need to grab/install it each time, unless it already provides that like it does jq)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So something wrong with my grep command I guess? 🤷‍♂️

yes 😄

docker logs ${CONTAINER_NAME} | grep 'Passed SPAM {RelayedTaggedInbound,Quarantined}'"

When you don't pass -q or --max-count 1, grep runs infinitly. Why should it stop?

If you only need to now, if a keyword was found, go with -q. If you need the output to parse further or make assertions, go with --max-count 1.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you don't pass -q or --max-count 1, grep runs infinitly. Why should it stop?

You're referring to the current version in master I referenced? That is not using --follow, so it does not run infinitely. The attempts I tried can be found in the collapsed "> Reproduction" section.

I collapsed it after responding with an update about my local grep being the problem. I understand the reason for -q or --max-count 1, but only if we're using grep within the container (doesn't make sense for docker logs --follow due to that), as for whatever reason my local grep is misbehaving which would be problematic for me running tests locally 😅 (something I've been doing quite frequently lately)

If we're to use either option with docker logs --follow, then I'd need to know why my grep is broken and how to fix it, or add ripgrep as a dependency for tests.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The attempts I tried can be found in the collapsed "> Reproduction" section.

Thanks, I overlooked that.

BTW: Are you running the tests on debian/ubuntu?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you running the tests on debian/ubuntu?

EndeavourOS (ArchLinux based)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending on how much more time you want to invest, you may try the "grep" binary from debian:

docker create --name grep debian
docker cp grep:/bin/grep .
docker rm grep

I am wondering, that such a basic tool behaves differently between linux distros.

PS: If we are ever in doubt, we should stick with debian based distros supported for testing (because CI is also running on ubuntu).

}

# An account added to `postfix-accounts.cf` must wait for the `changedetector` service
Expand Down
9 changes: 4 additions & 5 deletions test/helper/setup.bash
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,14 @@ function wait_for_finished_setup_in_container() {
#
# For example, if you need an immutable config volume that can't be affected by other tests
# in the file, then use `local TEST_TMP_CONFIG=$(duplicate_config_for_container . "${UNIQUE_ID_HERE}")`
#
# REQUIRED: `CONTAINER_NAME` must be set before this method is called.
# It only affects the `TEST_TMP_CONFIG` directory created,
# but will be used in `common_container_create()` and implicitly in other helper methods.
function init_with_defaults() {
__initialize_variables

export TEST_TMP_CONFIG

# In `setup_file()` the default name to use for the currently tested docker container
# is `${CONTAINER_NAME}` global defined here. It derives the name from the test filename:
# `basename` to ignore absolute dir path and file extension, only extract filename.
# In `setup_file()` creates a single copy of the test config folder to use for an entire test file:
TEST_TMP_CONFIG=$(duplicate_config_for_container . "${CONTAINER_NAME}")

# Common complimentary test files, read-only safe to share across containers:
Expand Down
105 changes: 0 additions & 105 deletions test/test_helper/common.bash
Original file line number Diff line number Diff line change
Expand Up @@ -173,42 +173,6 @@ function wait_for_service() {
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/helpers/index.sh; _obtain_hostname_and_domainname; cmp --silent -- <(_monitored_files_checksums) "${CHKSUM_FILE}" >/dev/null'
}

# NOTE: Relies on ENV `LOG_LEVEL=debug` or higher
function wait_until_change_detection_event_completes() {
local CONTAINER_NAME="${1}"
# Ensure early failure if arg is missing:
assert_not_equal "${CONTAINER_NAME}" ""

# Ensure the container is configured with the required `LOG_LEVEL` ENV:
assert_regex \
$(docker exec "${CONTAINER_NAME}" env | grep '^LOG_LEVEL=') \
'=(debug|trace)$'

# NOTE: Change events can start and finish all within < 1 sec,
# Reliably track the completion of a change event by comparing the before/after count:
function __change_event_count() {
docker exec "${CONTAINER_NAME}" grep --count "${CHANGE_EVENT_END}" /var/log/supervisor/changedetector.log
}

function __is_changedetector_finished() {
[[ $(__change_event_count) -gt "${NUM_CHANGE_EVENTS_BEFORE}" ]]
}

# Count by completions of this debug log line from `check-for-changes.sh`:
local CHANGE_EVENT_END='Completed handling of detected change'
local NUM_CHANGE_EVENTS_BEFORE=$(__change_event_count)

repeat_until_success_or_timeout 60 __is_changedetector_finished
}

# An account added to `postfix-accounts.cf` must wait for the `changedetector` service
# to process the update before Dovecot creates the mail account and associated storage dir:
function wait_until_account_maildir_exists() {
Expand Down Expand Up @@ -241,72 +205,3 @@ 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"* ]]'
}

# Common defaults appropriate for most tests, override vars in each test when necessary.
# For all tests override in `setup_file()` via an `export` var.
# For individual test override the var via `local` var instead.
#
# For example, if you need an immutable config volume that can't be affected by other tests
# in the file, then use `local TEST_TMP_CONFIG=$(duplicate_config_for_container . "${UNIQUE_ID_HERE}")`
function init_with_defaults() {
export TEST_NAME TEST_TMP_CONFIG

# In `setup_file()` the default name to use for the currently tested docker container
# is `${TEST_NAME}` global defined here. It derives the name from the test filename:
# `basename` to ignore absolute dir path and file extension, only extract filename.
TEST_NAME=$(basename "${BATS_TEST_FILENAME}" '.bats')
# In `setup_file()` creates a single copy of the test config folder to use for an entire test file:
TEST_TMP_CONFIG=$(duplicate_config_for_container . "${TEST_NAME}")

# Common complimentary test files, read-only safe to share across containers:
export TEST_FILES_CONTAINER_PATH='/tmp/docker-mailserver-test'
export TEST_FILES_VOLUME="${PWD}/test/test-files:${TEST_FILES_CONTAINER_PATH}:ro"

# The config volume cannot be read-only as some data needs to be written at container startup
# - two sed failures (unknown lines)
# - dovecot-quotas.cf (setup-stack.sh:_setup_dovecot_quotas)
# - postfix-aliases.cf (setup-stack.sh:_setup_postfix_aliases)
# TODO: Check how many tests need write access. Consider using `docker create` + `docker cp` for easier cleanup.
export TEST_CONFIG_VOLUME="${TEST_TMP_CONFIG}:/tmp/docker-mailserver"

# The common default FQDN assigned to the container `--hostname` option:
export TEST_FQDN='mail.my-domain.com'

# Default Root CA cert used in TLS tests with `openssl` commands:
export TEST_CA_CERT="${TEST_FILES_CONTAINER_PATH}/ssl/example.test/with_ca/ecdsa/ca-cert.ecdsa.pem"
}

# Using `create` and `start` instead of only `run` allows to modify
# the container prior to starting it. Otherwise use this combined method.
# NOTE: Forwards all args to the create method at present.
function common_container_setup() {
common_container_create "$@"
common_container_start
}

# Common docker setup is centralized here.
#
# `X_EXTRA_ARGS` - Optional: Pass an array by it's variable name as a string, it will
# be used as a reference for appending extra config into the `docker create` below:
#
# NOTE: Using array reference for a single input parameter, as this method is still
# under development while adapting tests to it and requirements it must serve (eg: support base config matrix in CI)
function common_container_create() {
[[ -n ${1} ]] && local -n X_EXTRA_ARGS=${1}

run docker create --name "${TEST_NAME}" \
--hostname "${TEST_FQDN}" \
--tty \
--volume "${TEST_FILES_VOLUME}" \
--volume "${TEST_CONFIG_VOLUME}" \
"${X_EXTRA_ARGS[@]}" \
"${NAME}"
assert_success
}

function common_container_start() {
run docker start "${TEST_NAME}"
assert_success

wait_for_finished_setup_in_container "${TEST_NAME}"
}
72 changes: 17 additions & 55 deletions test/tests/parallel/set2/tls/tls_letsencrypt.bats
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
load "${REPOSITORY_ROOT}/test/helper/setup"
load "${REPOSITORY_ROOT}/test/helper/common"
load "${REPOSITORY_ROOT}/test/helper/change-detection"
load "${REPOSITORY_ROOT}/test/helper/setup"
load "${REPOSITORY_ROOT}/test/helper/tls"

BATS_TEST_NAME_PREFIX='[Security] (TLS) (SSL_TYPE=letsencrypt) '
Expand Down Expand Up @@ -103,18 +104,13 @@ function _initial_setup() {
)
common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
wait_for_service "${CONTAINER_NAME}" 'changedetector'

# Wait until the changedetector service startup delay is over:
repeat_until_success_or_timeout 20 sh -c "$(_get_service_logs 'changedetector') | grep 'Changedetector is ready'"
}

# Test `acme.json` extraction works at container startup:
# It should have already extracted `mail.example.test` from the original mounted `acme.json`.
function _acme_ecdsa() {
_should_have_succeeded_at_extraction 'mail.example.test'

# SSL_DOMAIN set as ENV, but startup should not have match in `acme.json`:
_should_have_failed_at_extraction '*.example.test' 'mailserver'
# SSL_DOMAIN value should not be present in current `acme.json`:
_should_fail_to_extract_for_wildcard_env
_should_have_valid_config 'mail.example.test' 'key.pem' 'fullchain.pem'

local ECDSA_KEY_PATH="${LOCAL_BASE_PATH}/key.ecdsa.pem"
Expand All @@ -127,7 +123,6 @@ function _initial_setup() {
# It should replace the cert files in the existing `letsencrypt/live/mail.example.test/` folder.
function _acme_rsa() {
_should_extract_on_changes 'mail.example.test' "${LOCAL_BASE_PATH}/rsa.acme.json"
_should_have_service_reload_count '1'

local RSA_KEY_PATH="${LOCAL_BASE_PATH}/key.rsa.pem"
local RSA_CERT_PATH="${LOCAL_BASE_PATH}/cert.rsa.pem"
Expand All @@ -139,7 +134,6 @@ function _initial_setup() {
# Wildcard `*.example.test` should extract to `example.test/` in `letsencrypt/live/`:
function _acme_wildcard() {
_should_extract_on_changes 'example.test' "${LOCAL_BASE_PATH}/wildcard/rsa.acme.json"
_should_have_service_reload_count '2'

# As the FQDN has changed since startup, the Postfix + Dovecot configs should be updated:
_should_have_valid_config 'example.test' 'key.pem' 'fullchain.pem'
Expand Down Expand Up @@ -194,21 +188,17 @@ function _has_matching_line() {
# Traefik `acme.json` specific
#

# It should log success of extraction for the expected domain and restart Postfix.
function _should_have_succeeded_at_extraction() {
local EXPECTED_DOMAIN=${1}
local SERVICE=${2}

run $(_get_service_logs "${SERVICE}")
assert_output --partial "_extract_certs_from_acme | Certificate successfully extracted for '${EXPECTED_DOMAIN}'"
}

function _should_have_failed_at_extraction() {
local EXPECTED_DOMAIN=${1}
local SERVICE=${2}

run $(_get_service_logs "${SERVICE}")
assert_output --partial "_extract_certs_from_acme | Unable to find key and/or cert for '${EXPECTED_DOMAIN}' in '/etc/letsencrypt/acme.json'"
function _should_fail_to_extract_for_wildcard_env() {
# Set as value for ENV `SSL_DOMAIN`, but during startup it should fail to find a match in the current `acme.json`:
local DOMAIN_WILDCARD='*.example.test'
# The expected domain to be found and extracted instead (value from container `--hostname`):
local DOMAIN_MAIL='mail.example.test'

# /var/log/mail/mail.log is not equivalent to stdout content,
# Relevant log content only available via docker logs:
run docker logs "${CONTAINER_NAME}"
assert_output --partial "_extract_certs_from_acme | Unable to find key and/or cert for '${DOMAIN_WILDCARD}' in '/etc/letsencrypt/acme.json'"
assert_output --partial "_extract_certs_from_acme | Certificate successfully extracted for '${DOMAIN_MAIL}'"
}

# Replace the mounted `acme.json` and wait to see if changes were detected.
Expand All @@ -217,25 +207,12 @@ function _should_extract_on_changes() {
local ACME_JSON=${2}

cp "${ACME_JSON}" "${TEST_TMP_CONFIG}/letsencrypt/acme.json"
# Change detection takes a little over 5 seconds to complete (restart services)
sleep 10
wait_until_change_detection_event_completes "${CONTAINER_NAME}"

# Expected log lines from the changedetector service:
run $(_get_service_logs 'changedetector')
assert_output --partial 'Change detected'
run _get_logs_since_last_change_detection "${CONTAINER_NAME}"
assert_output --partial "'/etc/letsencrypt/acme.json' has changed - extracting certificates"
assert_output --partial "_extract_certs_from_acme | Certificate successfully extracted for '${EXPECTED_DOMAIN}'"
assert_output --partial 'Reloading services due to detected changes'
assert_output --partial 'Completed handling of detected change'
}

# Ensure change detection is not mistakenly validating against previous change events:
function _should_have_service_reload_count() {
local NUM_RELOADS=${1}

# Count how many times processes (like Postfix and Dovecot) have been reloaded by the `changedetector` service:
_run_in_container grep --count 'Completed handling of detected change' '/var/log/supervisor/changedetector.log'
assert_output "${NUM_RELOADS}"
}

# Extracted cert files from `acme.json` have content matching the expected reference files:
Expand Down Expand Up @@ -278,18 +255,3 @@ function _should_be_equal_in_content() {
assert_output "$(cat "${LOCAL_PATH}")"
assert_success
}

function _get_service_logs() {
local SERVICE=${1:-'mailserver'}

local CMD_LOGS=(docker exec "${CONTAINER_NAME}" "supervisorctl tail -2200 ${SERVICE}")

# As the `mailserver` service logs are not stored in a file but output to stdout/stderr,
# The `supervisorctl tail` command won't work; we must instead query via `docker logs`:
if [[ ${SERVICE} == 'mailserver' ]]
then
CMD_LOGS=(docker logs "${CONTAINER_NAME}")
fi

echo "${CMD_LOGS[@]}"
}
1 change: 1 addition & 0 deletions test/tests/parallel/set3/smtp-delivery.bats
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
load "${REPOSITORY_ROOT}/test/helper/common"
load "${REPOSITORY_ROOT}/test/helper/change-detection"
load "${REPOSITORY_ROOT}/test/helper/setup"

BATS_TEST_NAME_PREFIX='[SMTP] (delivery) '
Expand Down