Add bats test suite for image processing scripts

Tests cover skip-already-processed logic, cleanup, idempotency, and
the config.sh single-source-of-truth invariant. bats-core, bats-assert,
and bats-support added as submodules under tests/libs/.
This commit is contained in:
2026-07-03 19:49:04 -04:00
parent 6901c66bcf
commit f5ed6617c5
11 changed files with 233 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
[submodule "tests/libs/bats-core"]
path = tests/libs/bats-core
url = https://github.com/bats-core/bats-core.git
[submodule "tests/libs/bats-assert"]
path = tests/libs/bats-assert
url = https://github.com/bats-core/bats-assert.git
[submodule "tests/libs/bats-support"]
path = tests/libs/bats-support
url = https://github.com/bats-core/bats-support.git
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env bats
load 'libs/bats-support/load'
load 'libs/bats-assert/load'
load 'helpers/common'
@test "config.sh defines MAGICK_COMMENT" {
run bash -c "source '$REPO_ROOT/config.sh' && echo \"\$MAGICK_COMMENT\""
assert_success
assert_output "magick:resize=8mp,quality=85,sampling=4:2:0"
}
@test "config.sh defines MAX_PIXELS" {
run bash -c "source '$REPO_ROOT/config.sh' && echo \"\$MAX_PIXELS\""
assert_success
assert_output "8000000"
}
@test "MAGICK_COMMENT matches in images_resize.sh and zip_images_resize.sh" {
local comment_from_config comment_in_resize comment_in_zip
comment_from_config=$(bash -c "source '$REPO_ROOT/config.sh' && echo \"\$MAGICK_COMMENT\"")
comment_in_resize=$(grep 'MAGICK_COMMENT=' "$REPO_ROOT/images_resize.sh" | grep -v source || true)
comment_in_zip=$(grep 'MAGICK_COMMENT=' "$REPO_ROOT/zip_images_resize.sh" | grep -v source || true)
# Neither script should define MAGICK_COMMENT itself — only config.sh should
assert_equal "" "$comment_in_resize"
assert_equal "" "$comment_in_zip"
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 804 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

+30
View File
@@ -0,0 +1,30 @@
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
FIXTURES="$REPO_ROOT/tests/fixtures"
MOCKS="$REPO_ROOT/tests/mocks"
setup_mock_env() {
# Redirect ~/Applications/magick to our mock by overriding HOME
TEST_HOME="$(mktemp -d)"
mkdir -p "$TEST_HOME/Applications"
ln -s "$MOCKS/magick" "$TEST_HOME/Applications/magick"
export HOME="$TEST_HOME"
# Working dir for each test
TEST_WORKDIR="$(mktemp -d)"
}
teardown_mock_env() {
rm -rf "$TEST_HOME" "$TEST_WORKDIR"
}
make_zip() {
local zip_path="$1"; shift # destination zip path
local -a jpegs=("$@") # JPEG files to include
local tmpdir
tmpdir="$(mktemp -d)"
for jpg in "${jpegs[@]}"; do
cp "$jpg" "$tmpdir/"
done
(cd "$tmpdir" && zip -q "$zip_path" ./*.jpg)
rm -rf "$tmpdir"
}
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/env bats
load 'libs/bats-support/load'
load 'libs/bats-assert/load'
load 'helpers/common'
SCRIPT="$REPO_ROOT/images_resize.sh"
setup() {
setup_mock_env
}
teardown() {
teardown_mock_env
}
@test "skips JPEG that already has the processing comment" {
cp "$FIXTURES/processed.jpg" "$TEST_WORKDIR/img.jpg"
run bash -c "cd '$TEST_WORKDIR' && '$SCRIPT'"
assert_success
assert_output --partial "Skipping"
assert_output --partial "already processed"
}
@test "processes JPEG without the comment and embeds it" {
cp "$FIXTURES/unprocessed.jpg" "$TEST_WORKDIR/img.jpg"
run bash -c "cd '$TEST_WORKDIR' && '$SCRIPT'"
assert_success
assert_output --partial "Resized"
local comment
comment=$(identify -format "%c" "$TEST_WORKDIR/img.jpg")
assert_equal "$comment" "magick:resize=8mp,quality=85,sampling=4:2:0"
}
@test "re-running skips a JPEG that was just processed" {
cp "$FIXTURES/unprocessed.jpg" "$TEST_WORKDIR/img.jpg"
bash -c "cd '$TEST_WORKDIR' && '$SCRIPT'" >/dev/null 2>&1
run bash -c "cd '$TEST_WORKDIR' && '$SCRIPT'"
assert_success
assert_output --partial "Skipping"
}
@test "processes only JPEGs, ignores other file types" {
cp "$FIXTURES/unprocessed.jpg" "$TEST_WORKDIR/img.jpg"
echo "not an image" > "$TEST_WORKDIR/readme.txt"
run bash -c "cd '$TEST_WORKDIR' && '$SCRIPT'"
assert_success
assert_output --partial "Resized"
refute_output --partial "readme.txt"
}
+24
View File
@@ -0,0 +1,24 @@
#!/bin/bash
# Mock for ~/Applications/magick.
# identify: delegate to real system identify.
# convert (resize call): copy in-place and embed the comment so the file
# looks processed on the next run.
if [ "$1" = "identify" ]; then
shift
identify "$@"
exit $?
fi
# Parse -set comment <value> and the output file (last arg) from the argument list
COMMENT=""
OUTFILE="${@: -1}"
args=("$@")
for i in "${!args[@]}"; do
if [ "${args[$i]}" = "-set" ] && [ "${args[$i+1]}" = "comment" ]; then
COMMENT="${args[$i+2]}"
fi
done
if [ -n "$COMMENT" ]; then
convert "$OUTFILE" -set comment "$COMMENT" "$OUTFILE"
fi
+90
View File
@@ -0,0 +1,90 @@
#!/usr/bin/env bats
load 'libs/bats-support/load'
load 'libs/bats-assert/load'
load 'helpers/common'
SCRIPT="$REPO_ROOT/zip_images_resize.sh"
setup() {
setup_mock_env
}
teardown() {
teardown_mock_env
}
@test "prints usage and exits 1 when no arguments given" {
run "$SCRIPT"
assert_failure
assert_output --partial "Usage:"
}
@test "skips ZIP whose first JPEG already has the processing comment" {
make_zip "$TEST_WORKDIR/archive.zip" "$FIXTURES/processed.jpg"
run "$SCRIPT" "$TEST_WORKDIR/archive.zip"
assert_success
assert_output --partial "Skipping"
assert_output --partial "already processed"
}
@test "does not skip ZIP whose JPEG has no processing comment" {
make_zip "$TEST_WORKDIR/archive.zip" "$FIXTURES/unprocessed.jpg"
run "$SCRIPT" "$TEST_WORKDIR/archive.zip"
assert_success
refute_output --partial "Skipping"
assert_output --partial "Resized"
}
@test "processed ZIP is unchanged on disk after skip" {
make_zip "$TEST_WORKDIR/archive.zip" "$FIXTURES/processed.jpg"
local before_size after_size
before_size=$(stat -c%s "$TEST_WORKDIR/archive.zip")
run "$SCRIPT" "$TEST_WORKDIR/archive.zip"
after_size=$(stat -c%s "$TEST_WORKDIR/archive.zip")
assert_equal "$before_size" "$after_size"
}
@test "after processing, re-running skips the same ZIP" {
make_zip "$TEST_WORKDIR/archive.zip" "$FIXTURES/unprocessed.jpg"
"$SCRIPT" "$TEST_WORKDIR/archive.zip" >/dev/null 2>&1
run "$SCRIPT" "$TEST_WORKDIR/archive.zip"
assert_success
assert_output --partial "Skipping"
}
@test "ZIP with no JPEGs inside is processed without error" {
echo "not an image" > "$TEST_WORKDIR/readme.txt"
(cd "$TEST_WORKDIR" && zip -q archive.zip readme.txt)
run "$SCRIPT" "$TEST_WORKDIR/archive.zip"
assert_success
refute_output --partial "Skipping"
refute_output --partial "Error:"
}
@test "no temp files left in /dev/shm after processing" {
make_zip "$TEST_WORKDIR/archive.zip" "$FIXTURES/unprocessed.jpg"
local before after
before=$(find /dev/shm -maxdepth 1 -name 'tmp.*' | sort)
run "$SCRIPT" "$TEST_WORKDIR/archive.zip"
after=$(find /dev/shm -maxdepth 1 -name 'tmp.*' | sort)
assert_equal "$before" "$after"
}
@test "no temp files left in /dev/shm after skipping" {
make_zip "$TEST_WORKDIR/archive.zip" "$FIXTURES/processed.jpg"
local before after
before=$(find /dev/shm -maxdepth 1 -name 'tmp.*' | sort)
run "$SCRIPT" "$TEST_WORKDIR/archive.zip"
after=$(find /dev/shm -maxdepth 1 -name 'tmp.*' | sort)
assert_equal "$before" "$after"
}
@test "processes multiple ZIPs, skipping already-processed ones" {
make_zip "$TEST_WORKDIR/done.zip" "$FIXTURES/processed.jpg"
make_zip "$TEST_WORKDIR/todo.zip" "$FIXTURES/unprocessed.jpg"
run "$SCRIPT" "$TEST_WORKDIR/done.zip" "$TEST_WORKDIR/todo.zip"
assert_success
assert_output --partial "Skipping"
assert_output --partial "Resized"
}