Repository navigation
Add end-to-end tests for every option against PostgreSQL 18 and MinIO - #35
Merged
Merged
Conversation
Builds the image and runs it against real PostgreSQL 18, MinIO (HTTP and HTTPS with a throwaway CA) and a URL hook receiver on a private Docker network. Needs only bash and Docker. Includes a smoke test that backs up and restores a seeded database and compares fingerprints of both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
Adds a Test workflow for pull requests and pushes to master, a guide to the end-to-end suite, and a Testing section in the README. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
Script hooks: event order, arguments, uid, environment scrubbing, HOOK_INHERIT_ENV, HOOKS_DIR, fail-closed behaviour and error hooks. Scheduled backups: @every and cron expressions, invalid schedules, COMMAND_TIMEOUT parsing and enforcement, overlap skipping, failures, docker stop and BACKUP_FILE precedence. Known bugs are marked xfail: COMMAND_TIMEOUT leaves child processes running, a scheduled run hangs on an output line over 64 KiB, the end of a run's output can be lost, and HOOK_INHERIT_ENV=yes drops HOOK_EVENT and HOOK_EXIT_CODE. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
Plain and custom-format round trips checked against fingerprints of the source database: new, existing and renamed targets, CREATE_DATABASE, DROP_DATABASE (including with other sessions connected), quoted names, PARALLEL_JOBS, DECOMPRESSION_CMD, pg_dumpall into a fresh server, deep BACKUP_FILE prefixes, POSTGRES_EXTRA_OPTS and the error paths. Marks as xfail a known bug: with DROP_DATABASE=yes, a backup file with an unsupported extension still drops the target database. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
Plain, custom and pg_dumpall formats; COMPRESSION_CMD with pigz, arguments and failures; key naming and UTC timestamps; non-default POSTGRES_PORT; Docker link variables; POSTGRES_EXTRA_OPTS and POSTGRES_EXTRA_DUMP_OPTS; pipefail; upload failures; database names needing quoting; and a backup of an older (PostgreSQL 16) server. Known bugs are marked xfail: a database name starting with a hyphen is parsed as pg_dump options, a name containing '=' is treated as a connection string, and a POSTGRES_USER with a space breaks the command. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
The postgres image declares a volume, so docker rm -f without -v left one anonymous volume behind per server, filling the disk over repeated local runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
Encrypted plain and custom-format backups (only the .enc object is uploaded, it decrypts with the README's manual command, and it restores exactly), wrong and missing passwords, passwords with special characters, unencrypted backups restored with a password set, and legacy (pre-PBKDF2) backups. Known bugs are marked xfail, with fixed salts so they reproduce every time. AES-CBC has no authentication, so a wrong key passes the padding check about 1 time in 256: about 1 legacy backup in 256 cannot be restored, and a wrong password with DROP_DATABASE=yes drops the database about 2 times in 256. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
S3_PREFIX (default, nested, empty), S3_REGION checked against the signing region in MinIO's request trace, S3_S3V4, credentials from S3_*, AWS_* variables, shared credentials files, profiles and session tokens, precedence between them, wrong or missing credentials and buckets, unreachable endpoints, AWS_ENDPOINT_URL, checksum settings, a multipart round trip over 8 MB, and keys containing spaces. Known bugs are marked xfail: an empty S3_ENDPOINT breaks every aws call, an empty S3_PREFIX uploads to s3://bucket//file and makes retention fail with exit 2, and retention mangles keys with repeated spaces. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
Against MinIO over HTTPS with a throwaway CA: no CA, S3_SSL_VERIFY=yes, S3_CA_BUNDLE (including multi-certificate bundles and paths with spaces), CAs mounted in /usr/local/share/ca-certificates (including in scheduled mode and ignoring non-.crt files), AWS_CA_BUNDLE, the precedence between them, a wrong or missing CA file, a hostname mismatch, and S3_SSL_VERIFY=no for backup, retention and restore. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
Old objects are deleted and the new backup kept, the documented 'N days ago' form, an exclusive threshold, only objects directly in the prefix are touched (not sub-prefixes or sibling prefixes), any file in the prefix is deleted as the README warns, keys with spaces, nested prefixes, TZ, scheduled runs, and failures of the upload, listing or delete. Objects are backdated with MinIO's X-Minio-Source-Mtime header. Known bugs are marked xfail: a future threshold (for example '7 days' without 'ago') deletes the new backup and everything else while the run exits 0, an empty DELETE_OLDER_THAN deletes everything before midnight, keys differing only in whitespace are mixed up, and a key containing ' PRE ' is never deleted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
Request order for every HOOK_*_URL on backup and restore success and failure, no requests after validation errors, URLs kept out of logs, HTTP refused unless HOOK_ALLOW_HTTP=yes, refusal of other schemes, credentials, whitespace, loopback, link-local and metadata hosts before any request, 403 failing the job, 500 retried three times, redirects not followed, untrusted certificates failing closed, and a failing error URL keeping the original exit code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
Required variables for backup and restore, the S3 key pair, Docker link variables, run.sh dispatch (one-off backup, BACKUP_FILE winning over SCHEDULE), the README environment table matching the Dockerfile, and the image's tools, hook user and /hooks permissions. Also checks every variable set to an empty string, as the README's Kubernetes example does. Empty values that do not behave like unset are marked xfail: POSTGRES_DATABASE (backs up or restores into the wrong database and exits 0), DELETE_OLDER_THAN, ENCRYPTION_PASSWORD, AWS_CA_BUNDLE (turns off certificate checks), SCHEDULE, POSTGRES_PORT, S3_ENDPOINT, COMPRESSION_CMD, DECOMPRESSION_CMD, and BACKUP_FILE given to restore.sh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
The single job had grown to 15 minutes. Case files are discovered at run time, so a new file cannot be skipped by accident, and an "End-to-end tests" job still reports one overall result for branch protection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
46 deliberate breakages of hooks.sh, backup.sh, restore.sh, the Dockerfile and go-cron were run against the file; 43 were caught. The three survivors are now caught too: - post-backup must get BACKUP_DEST_FILE and BACKUP_S3_URI for a default plain backup, not only for encrypted ones - the same for an unencrypted custom-format (.dump) backup - a scheduled run's hook output must reach the container log on both stdout and stderr Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
19 deliberate breakages of go-cron, run.sh and backup.sh were run against the file, plus a build with the three known bugs fixed (which turns their xfails into XPASS). All are caught. Changes: - the repeated-run test changes the data between runs and restores the newest backup, proving scheduled runs dump fresh data - the timeout test blocks its hook on a marker file and uses an 8 s timeout, leaving headroom for slower CI runners - the 6-field cron case is no longer pinned as invalid: the README links to docs that describe 6-field expressions, so which is right is a documentation question Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
30 deliberate breakages of restore.sh and the image defaults were run against the file; 24 were caught. The five real survivors are now caught: - PARALLEL_JOBS logged but -j not passed to pg_restore, or hard-coded: a login event trigger now counts pg_restore sessions (PostgreSQL 17+; skipped with a note on older servers) - errors from a parallel pg_restore swallowed - the 'all' restore ignoring DECOMPRESSION_CMD, or swallowing pipeline errors: a bzip2 'all' script must fail with gunzip and succeed with bunzip2 Also lengthens the held session in the DROP ... WITH (FORCE) test so a slow runner cannot make it vacuous, and fails fast on setup errors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
25 deliberate breakages of backup.sh were run against the file; 19 were caught. The six survivors are now caught: - custom format and pg_dumpall ignoring POSTGRES_PORT: the non-default port test now also takes custom-format and 'all' backups - custom format ignoring POSTGRES_EXTRA_OPTS, or getting an unquoted database name - custom format written uncompressed: the archive header must show compression - pg_dumpall ignoring COMPRESSION_CMD The Docker link test also sets POSTGRES_PORT=tcp://... as Docker does, checking that the link variables override it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
27 deliberate breakages of backup.sh, restore.sh and the image's openssl were run against the file; 25 were caught here and one more by the script hook tests. The survivor, openssl defaulting to another message digest, is now caught: the README's manual decrypt and the legacy fixtures name -md sha256, which is what standard OpenSSL 1.1+ and every pre-PBKDF2 release used, so a digest change in the image breaks the tests instead of users' backups. Adds a check that the fixed-salt PBKDF2 fixtures used by the wrong-password xfails restore with the right password, so those xfails can only fail on the bug itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
33 deliberate breakages of backup.sh, restore.sh and run.sh were run against the file; 30 were caught here and one more by the retention tests. The others are now caught: - set -x in restore.sh printing the secret key: a failed restore's log must not contain it - an empty S3_ACCESS_KEY_ID or S3_SECRET_ACCESS_KEY treated as set: the empty-values test now also restores - a secret key without a key id accepted on restore The empty-prefix retention xfail now also checks the key is at the bucket root, so a wrong fix cannot pass it. The repeated-spaces retention xfail is removed as a duplicate of the one in retention.sh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
22 deliberate breakages of run.sh, backup.sh, restore.sh and go-cron were run against the file; 14 were caught. Five more are now caught: - S3_CA_BUNDLE applied only to one-off runs, not scheduled ones (new scheduled S3_CA_BUNDLE test; go-cron stripping AWS_* is also caught) - the CA directory pointing AWS_CA_BUNDLE at one file, or replacing the public roots instead of adding to them: three CAs are mounted and the merged bundle must hold them all alongside the public roots - DROP_DATABASE moved before the download: a restore rejected by TLS must leave the existing database untouched The three survivors are a value the README does not define (S3_SSL_VERIFY other than yes or no), a cleanup path only the restore tests can reach, and an export that has no effect on this image. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
23 deliberate breakages of backup.sh, restore.sh and the image default were run against the file; 19 were caught. Three more are now caught: - a default DELETE_OLDER_THAN in the image: new test that retention is off by default and old objects survive - read without -r mangling keys with backslashes - the sub-prefix skip widened to any key containing PRE The survivor (parsing the threshold in UTC) gives the same result for every documented threshold form. Also stops three tests asserting the '<new backup> not older than' log line, which pinned an implementation detail rather than the outcome (the new backup is still checked in S3), and gives the TZ test a valid POSIX zone: musl read 'E2E-14' as UTC-2 plus a DST zone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
22 deliberate breakages of the scripts and image were run against the file; 14 were caught. The other eight are now caught: blank S3 key pairs on restore, HOOK_DROP_PRIVS defaulting to no or baked into the image, HOOK_ALLOW_HTTP or DROP_DATABASE defaulting to yes, a setuid su-exec, and a world-writable /usr/local or /usr/local/bin. The README table is now checked against the built image's defaults rather than a parse of the Dockerfile, and the image test no longer pins the scripts' directory or the PostgreSQL major version beyond what the README states. Adds an xfail for a new bug: a pre-backup hook running as the unprivileged user can plant symlinks at /tmp/dump.sql.gz.enc or /tmp/s3-listing so that root overwrites another file. It skips where fs.protected_symlinks is on, as on most Ubuntu hosts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
- run.sh drops the databases a case created on the shared server when the case ends (names starting with its t_<slug> prefix, plus any made with new_db_named), so disk use no longer grows with every case - psql_exec no longer reads stdin, so it is safe inside while-read loops - the hook receiver also logs each request's headers, for checking that URL hooks send no secret headers (hook_request_headers) - README: call xfail just before the step that exposes the bug, so a setup failure shows as FAIL; assert outcomes rather than temporary paths or internal wording; use docker rm -v after E2E_KEEP=1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
- a failing HOOK_PRE_RESTORE_URL stops the restore before the download and before DROP_DATABASE, leaving the database untouched - a failing HOOK_POST_RESTORE_URL fails the job and fires the restore-error URL - a failing HOOK_RESTORE_ERROR_URL keeps the original exit code - hook requests are GETs without a body, credentials or secret values (README), checked against the receiver's header log - rename the validation test after the single error it exercises Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
- new xfail: a run stopped by COMMAND_TIMEOUT must still run cleanup (backup-error script hook and URL, no dump left in /tmp); today go-cron SIGKILLs the shell, so its EXIT trap never runs - '* * * * *' really fires (the only minute-long test) - @monthly, @Yearly and @annually are accepted - COMMAND_TIMEOUT does not affect one-off backups, as the README says - timeout tests match 'timed out after Ns' rather than the full log line, so a reworded message still passes - xfail calls moved to just before the step that exposes each bug Against PR #34 the file has no failures; its four xfails pass there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
New tests: - xfail: a plain restore whose SQL fails (target already populated) must not exit 0 and report success - xfail: a DROP_DATABASE that fails must fail the restore rather than restore on top of the existing database - restore with a non-default POSTGRES_USER (password with spaces and quotes) and POSTGRES_PORT, using parallel pg_restore - a backup of a PostgreSQL 16 server restores back into it: plain passes; custom is an xfail because pg_restore 18 sends 'SET transaction_timeout', which PostgreSQL 16 rejects, so the run exits 1 although the data is restored - POSTGRES_EXTRA_DUMP_OPTS is ignored by restores, as the README says The create-when-exists test now asserts the outcome instead of the warning text, the unsupported-format test is renamed for what it checks, and xfail calls sit just before the step that exposes each bug. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
New xfails: S3_BUCKET, POSTGRES_HOST, POSTGRES_USER and POSTGRES_PASSWORD set to an empty string (as the README's Kubernetes example shows them) must be rejected like unset ones, for backup and restore: exit 1, the usual message, no dump or download, no hooks. Today they pass validation, hooks fire, and the job fails later with a misleading error. The docs check now accepts a README row without an ENV only for a variable the scripts read (such as TZ), instead of pinning how a default is provided. The image test checks only that go-cron runs and mentions itself, not its exact usage text. The dispatch test is removed as a duplicate of test_schedule_backup_file_restores_once, and xfail calls sit just before the step that exposes each bug. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
New tests:
- S3_BUCKET with a sub-path (the README calls it a bucket path): backup
key, BACKUP_S3_URI, retention and restore all work
- xfails for S3_PREFIX with a trailing slash ('backups/'): every backup
fails against MinIO (XMinioInvalidObjectName) because the key has
'//', and AWS S3 would store such keys
- xfails for a leading slash ('/backups'): the request path and
BACKUP_S3_URI contain '//', and retention lists a prefix that matches
nothing, so the backup exits 2 after uploading
- the custom-region trace now also covers retention's list and delete
Removes four tests duplicated in validation.sh, retention.sh, smoke.sh
and backup.sh. Against PR #34 the file has no failures; its seven
prefix xfails pass there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
- an encrypted POSTGRES_DATABASE=all backup is stored only as all_<ts>.sql.gz.enc, decrypts with the README's command, and restores both databases, the role and its ownership into a fresh server - tests no longer pin the temporary file in the 'Encrypting ...' log line; the .enc object, its Salted__ header and the round trips prove encryption - the encryption-failure test uses a stand-in openssl that fails 'enc', instead of blocking a fixed temporary path - the legacy xfail is renamed for the behaviour it expects, and xfail calls sit just before the step that exposes each bug All three tests that failed against PR #34 only because of the old temporary path now pass on both images. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
- the error-event scrubbing test now also covers backup-error, with ENCRYPTION_PASSWORD, AWS_SESSION_TOKEN and AWS_ROLE_ARN set, and HOOK_EXIT_CODE=2 - new test: a directory at $HOOKS_DIR/<event> (which passes a plain -x check) fails the job closed with nothing uploaded - the scheduled-hooks test judges only events recorded before it stops the container. On PR #34 docker stop forwards SIGTERM to the running backup, which then correctly fires backup-error; the old check failed whenever the stop landed mid-run - the HOOK_INHERIT_ENV xfail moves to just before the step that exposes the bug Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
- removes three tests covered elsewhere: the Docker link fallback and the required-settings checks (validation.sh covers both, with restores and hook checks), and the single --exclude-table test (test_backup_extra_dump_opts_several_options asserts the same) - xfail calls move to just before the backup that exposes each bug - databases made by the quoting test are recorded for clean-up, since their names do not share the case prefix Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
- new test: with S3_CA_BUNDLE and verification on, DELETE_OLDER_THAN deletes an old object on minio-tls and keeps a newer one and the new backup (only S3_SSL_VERIFY=no exercised retention over TLS before) - renames the explicit S3_SSL_VERIFY=yes test for what it proves - drops per-test database clean-up helpers now that run.sh does it Passes on both the master and PR #34 images. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
The empty-threshold xfail is covered by validation.sh's test_validation_empty_delete_older_than_deletes_nothing, which keeps all the empty-value cases together. The remaining xfail calls move to just before the backup that exposes each bug. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
- fail() records the failure in the case directory, because inside a command substitution its exit ends only that subshell. run.sh reports such a case as FAIL, and one that failed before reaching its xfail step as FAIL rather than as an expected failure - the overall End-to-end tests job now says when it failed because the per-file jobs were cancelled (usually by a newer push); it still fails, so a cancelled run never satisfies a required check - README: note the $(...) behaviour and when to add '|| exit 1' Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
_val_put_aged computed '3 days ago' twice, once for the upload and once for the expected Last-Modified. When a second boundary fell between the two, they differed by a second and the test skipped as if MinIO could not backdate objects. It now computes the timestamp once, as the retention helper does; five runs on each image gave the same result. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
Without --console-address MinIO picks a random console port at start-up
and exits if that port is taken by the time it binds ('Specified port is
already in use'), which stopped one CI job before any test ran. Both
MinIO containers now use :9001 inside their own network namespace.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds an end-to-end test suite that builds the image and runs it the way users do, against real services on a private Docker network:
pg_dump18.6 (one test also backs up and restores PostgreSQL 16).There are 228 tests in 11 files, one file per feature area. They cover every option in the Dockerfile and README. Restores are checked against a fingerprint of every table, view, function, sequence, index, trigger and large object, so a restore must be exact to pass.
The suite needs only bash and Docker. CI runs each case file as a parallel job, so a full run takes about 4 minutes.
Known bugs
The tests found 41 product bugs. Each is a test for the correct behaviour, marked
xfail, so the suite is green onmastertoday. When a fix lands, its test passes and the run fails withXPASSuntil the marker is removed. That way each fix gets recorded.DROP_DATABASEonly warns, then restores on top of the existing databaseDROP_DATABASE=yesDELETE_OLDER_THAN="7 days", missing "ago") deletes the new backup and everything else, and exits 0awsprogress on a ~475 MB upload)COMMAND_TIMEOUTkills only the shell:pg_dump,awsand hooks keep running and pile up, cleanup and error hooks never runPOSTGRES_DATABASE=""backs up or restores the wrong database and reports successDELETE_OLDER_THAN=""deletes every backup older than midnight todayENCRYPTION_PASSWORD=""encrypts with an empty passwordSCHEDULE,POSTGRES_PORTorS3_ENDPOINTset to""(as in the README's Kubernetes example) breaks every runS3_BUCKET,POSTGRES_HOST,POSTGRES_USERorPOSTGRES_PASSWORDset to""pass validation and fail late, after hooks fireBACKUP_FILE=""given torestore.shdrops the database and restores nothingAWS_CA_BUNDLE=""silently turns off certificate checksCOMPRESSION_CMD=""orDECOMPRESSION_CMD=""makes every plain backup or restore failS3_PREFIXthat is empty, or has a leading or trailing slash, writes//keys; a trailing slash fails every backup on MinIO, and retention breakspre-backuphook (unprivileged) can plant symlinks in/tmpso that root overwrites another fileHOOK_INHERIT_ENV=yesdropsHOOK_EVENTandHOOK_EXIT_CODEDROP_DATABASE=yesdrops the database about 2 times in 256-is read aspg_dumpoptions, so another database is backed up under that name=, or aPOSTGRES_USERcontaining a space, breaks the dumppg_restore18 sendsSET transaction_timeout)PREis never deletedResults against #34
I ran the whole suite against an image built from #34 (
62aad10):xfailtests pass there (XPASS), so Fix backup bugs, harden runtime and add essential features #34 fixes those bugs.pg_dump18 writesSET transaction_timeout = 0into every plain dump, which PostgreSQL 16 rejects. Fix backup bugs, harden runtime and add essential features #34's new stop-on-error default then halts there: exit 3, nothing restored. Onmasterthe same restore works (test_restore_older_server_version_plain).test_validation_docs_env_table_matches_imagefails when this branch's README is compared with Fix backup bugs, harden runtime and add essential features #34's image, because Fix backup bugs, harden runtime and add essential features #34 adds six documented options. It passes on Fix backup bugs, harden runtime and add essential features #34's own branch.Merging this first and then updating #34 from
masterwould let #34's CI show exactly this.How the tests were checked
go-cron. Every one is caught by at least one test, apart from a few that change nothing observable; those are noted in the commit messages.master's image and once on Fix backup bugs, harden runtime and add essential features #34's, and the whole suite twice (187 passed, 41 expected failures, 0 skipped).Notes
cgr.dev/chainguard/minio.test/e2e/run.sh, optionally with a name filter. Guide: test/e2e/README.md.TZhas no effect (the image has no time zone data).HOOK_DROP_PRIVS=noruns hooks as root and is undocumented./usr/local/share/ca-certificatesstops the container on a read-only root filesystem.S3_S3V4has no effect with AWS CLI v2.AWS_REGIONoverridesS3_REGION.🤖 Generated with Claude Code
https://claude.ai/code/session_01UdSRPLaq3mTA6Djdzte7C5