Record queue lifecycle release test

This commit is contained in:
Mario Fetka
2026-07-23 11:28:28 +02:00
parent 1441ab1aa6
commit cdb3bb54fc
3 changed files with 272 additions and 1 deletions
+215
View File
@@ -0,0 +1,215 @@
#!/bin/sh
#
# Exercise Queue administration, retry preservation, expiry, and DSN creation
# against a running local Bongo installation.
set -eu
user=${1:-stqqueue}
domain=${2:-bongo.test}
queue_tool=${BONGO_QUEUE_TOOL:-/usr/bin/bongo-queuetool}
store_tool=${BONGO_STORE_TOOL:-/usr/bin/bongo-storetool}
service=${BONGO_SERVICE:-bongo.service}
run_as=${BONGO_TEST_RUN_AS:-bongo}
as_root=${BONGO_TEST_AS_ROOT:-sudo -n}
if [ "$(id -un)" = "$run_as" ]; then
as_bongo=
else
as_bongo="sudo -n -u $run_as"
fi
work=$(mktemp -d "${TMPDIR:-/tmp}/bongo-stq05.XXXXXX")
chmod 0755 "$work"
managed_id=
retry_id=
expiry_id=
cleanup()
{
status=$?
for queue_id in "$managed_id" "$retry_id" "$expiry_id"; do
if [ -n "$queue_id" ]; then
$as_bongo "$queue_tool" delete "$queue_id" \
>/dev/null 2>&1 || true
fi
done
rm -rf "$work"
exit "$status"
}
trap cleanup EXIT HUP INT TERM
wait_queue()
{
attempts=30
while [ "$attempts" -gt 0 ]; do
if $as_bongo "$queue_tool" list >/dev/null 2>&1; then
return 0
fi
attempts=$((attempts - 1))
sleep 1
done
echo "STQ-05: Queue did not become ready" >&2
return 1
}
queue_contains()
{
$as_bongo "$queue_tool" list |
grep -F "$1 " >/dev/null
}
wait_message()
{
queue_id=$1
output=$2
attempts=30
while [ "$attempts" -gt 0 ]; do
if $as_bongo "$queue_tool" message "$queue_id" \
>"$output" 2>/dev/null; then
return 0
fi
attempts=$((attempts - 1))
sleep 1
done
echo "STQ-05: queue entry $queue_id did not become readable" >&2
return 1
}
printf '%s\r\n' \
"From: STQ-05 <stq05@$domain>" \
"To: $user@$domain" \
'Subject: STQ-05 queue lifecycle' \
'Date: Thu, 23 Jul 2026 12:34:56 +0200' \
'Message-ID: <stq05-lifecycle@bongo.test>' \
'' \
'Queue listing, hold, retry, expiry, bounce, and deletion.' \
>"$work/message.eml"
chmod 0644 "$work/message.eml"
# A normal committed entry must be listable, movable to hold, preserved across
# restart, releasable to an explicit target, and deletable.
managed_id=$($as_bongo "$queue_tool" hold-local "$work/message.eml" 9)
case "$managed_id" in
009-*) ;;
*)
echo "STQ-05: unexpected management fixture ID: $managed_id" >&2
exit 1
;;
esac
queue_contains "$managed_id"
held_id=$($as_bongo "$queue_tool" hold "$managed_id")
managed_id=$held_id
case "$managed_id" in
999-*) ;;
*)
echo "STQ-05: hold did not return a queue-999 ID: $managed_id" >&2
exit 1
;;
esac
queue_contains "$managed_id"
$as_root /usr/bin/systemctl restart "$service"
wait_queue
queue_contains "$managed_id"
released_id=$($as_bongo "$queue_tool" release "$managed_id" 9)
managed_id=$released_id
case "$managed_id" in
009-*) ;;
*)
echo "STQ-05: release did not return a queue-9 ID: $managed_id" >&2
exit 1
;;
esac
queue_contains "$managed_id"
$as_bongo "$queue_tool" delete "$managed_id"
if queue_contains "$managed_id"; then
echo "STQ-05: deleted entry remains in the listing" >&2
exit 1
fi
managed_id=
# A failed remote attempt must remain queued without changing the message.
retry_id=$($as_bongo "$queue_tool" enqueue-remote \
"$work/message.eml" "$user@$domain" nobody@retry.invalid 7)
case "$retry_id" in
007-*) ;;
*)
echo "STQ-05: unexpected retry fixture ID: $retry_id" >&2
exit 1
;;
esac
wait_message "$retry_id" "$work/retry-before.eml"
retry_before=$(sha256sum "$work/retry-before.eml" | awk '{ print $1 }')
$as_bongo "$queue_tool" flush
wait_message "$retry_id" "$work/retry-after.eml"
retry_after=$(sha256sum "$work/retry-after.eml" | awk '{ print $1 }')
if [ "$retry_before" != "$retry_after" ]; then
echo "STQ-05: retry changed the queued message" >&2
exit 1
fi
$as_bongo "$queue_tool" delete "$retry_id"
retry_id=
# Deterministic expiry starts in hold so the normal remote worker cannot race
# the diagnostic command. Exactly one DSN must appear in the isolated inbox.
$as_bongo "$store_tool" -u "$user" \
document-list /mail/INBOX >"$work/inbox-before"
expiry_id=$($as_bongo "$queue_tool" enqueue-remote \
"$work/message.eml" "$user@$domain" nobody@retry.invalid 999)
case "$expiry_id" in
999-*) ;;
*)
echo "STQ-05: unexpected expiry fixture ID: $expiry_id" >&2
exit 1
;;
esac
$as_bongo "$queue_tool" expire "$expiry_id" >/dev/null
expiry_id=
attempts=30
while [ "$attempts" -gt 0 ]; do
$as_bongo "$store_tool" -u "$user" \
document-list /mail/INBOX >"$work/inbox-after"
awk '{ print $1 }' "$work/inbox-before" | sort -u \
>"$work/inbox-before.ids"
awk '{ print $1 }' "$work/inbox-after" | sort -u \
>"$work/inbox-after.ids"
comm -13 "$work/inbox-before.ids" "$work/inbox-after.ids" \
>"$work/new.ids"
if [ "$(wc -l <"$work/new.ids")" -eq 1 ]; then
break
fi
attempts=$((attempts - 1))
sleep 1
done
if [ "$(wc -l <"$work/new.ids")" -ne 1 ]; then
echo "STQ-05: expiry did not create exactly one DSN" >&2
diff -u "$work/inbox-before" "$work/inbox-after" >&2 || true
exit 1
fi
document=$(sed -n '1p' "$work/new.ids")
document_path=$(awk -v id="$document" '$1 == id { print $3 }' \
"$work/inbox-after")
$as_bongo "$store_tool" -u "$user" \
document-get "$document_path" - >"$work/dsn.eml"
for expected in \
'Subject: Returned mail: Delivery time exceeded' \
'Original-recipient: rfc822;nobody@retry.invalid' \
'Final-recipient: rfc822;nobody@retry.invalid' \
'Action: failed' \
'Status: 5.4.7'
do
if ! grep -F "$expected" "$work/dsn.eml" >/dev/null; then
echo "STQ-05: generated DSN lacks '$expected'" >&2
exit 1
fi
done
printf 'STQ-05 PASS management=hold/restart/release/delete retry-sha256=%s dsn=%s status=5.4.7\n' \
"$retry_after" "$document"
+1 -1
View File
@@ -110,7 +110,7 @@ inputs and outputs is absent from the ZIP.
| STQ-02 | Local message survives Queue-to-Store delivery byte-for-byte | [PASS](test-evidence/0.7-r1.md#stq-02) | | |
| STQ-03 | Queue free-space calculation is correct on 32/64-bit and reserves space | [PASS](test-evidence/0.7-r1.md#stq-03) | | |
| STQ-04 | Full-disk and unreadable-spool failures defer safely without mail loss | [PASS](test-evidence/0.7-r1.md#stq-04) | | |
| STQ-05 | Retry, expiry, bounce, listing, hold/release, and deletion | | | |
| STQ-05 | Retry, expiry, bounce, listing, hold/release, and deletion | [PASS](test-evidence/0.7-r1.md#stq-05) | | |
| STQ-06 | Concurrent delivery/read/move/delete/restart preserves integrity | | | |
| STQ-07 | Backup/export and restore reproduce mail, metadata, contacts, calendars | | | |
| STQ-08 | Invalid commands, oversized lines, and unauthorised access fail safely | | | |
+56
View File
@@ -1523,3 +1523,59 @@ Bongo finished active/running as manager PID 1225547 with systemd result
`success`. The temporary fixture, fill allocation, and failed submission were
removed; no Store object, external mail, SMTP relay, collector source, or
LMTP endpoint was used.
## STQ-05
Result: **PASS**
Commit `efc5173d` added deterministic Queue listing, hold/release controls,
the reserved Queue 999 class, and recovery of held entries across manager
restarts. It also fixed `QMOVE` to use its parsed destination. Commit
`69ab7094` added complete remote-delivery fixtures and deterministic expiry
diagnostics. Commit `1441ab1a` fixed the expired-recipient envelope boundary
so the generated delivery-status recipient and enhanced status code are
parsed correctly instead of falling back to a generic internal error. Unit
tests cover the Queue protocol clients, movement, listing, recovery, expiry,
and exact bounce-recipient formatting. The complete rebuilt suite passed 86
of 86 tests in 11.26 seconds.
The commits were pushed to `master`, installed as
`mail-mta/bongo-9999::bongo` with GCC 15.3.0, and exercised on 2026-07-23:
```sh
./contrib/testing/queue-lifecycle-check.sh
```
The repeatable live test first listed a Queue 009 entry, moved it to hold,
restarted Bongo, and proved that Queue 999 recovery retained it. It released
the message to Queue 009, listed it there, deleted it, and confirmed that it
was gone. A remote-delivery fixture for `nobody@retry.invalid` remained in
Queue 007 after a flush with identical message bytes:
```text
retry SHA-256 39b785579f9bdc5914216aa84f83cd19ca6014eef95d49e388b1d52f209f26a6
```
A separate held remote fixture was then expired without racing the normal
remote worker. It left the Queue and created exactly one new Store object,
`000000000000001b`, in the isolated `stqqueue` inbox. The multipart delivery
status notification contained:
```text
Subject: Returned mail: Delivery time exceeded
Original-recipient: rfc822;nobody@retry.invalid
Final-recipient: rfc822;nobody@retry.invalid
Action: failed
Status: 5.4.7
```
The script reported:
```text
STQ-05 PASS management=hold/restart/release/delete retry-sha256=39b785579f9bdc5914216aa84f83cd19ca6014eef95d49e388b1d52f209f26a6 dsn=000000000000001b status=5.4.7
```
All temporary Queue entries were removed. The intentionally retained DSN is
local release evidence; no external mail, SMTP relay, collector source, or
LMTP endpoint was used. Bongo finished active/running as manager PID 1253396
with systemd result `success`.