summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-06-14 23:07:12 +0300
committerPaul Buetow <paul@buetow.org>2026-06-14 23:07:12 +0300
commitb7d5c1102c3679b67532f30f2613e4d2d61c59ba (patch)
tree573548dc8762aeb7c642fed804894d280425f84c /tests
parent0f0662850ad2df3502241cd1bdfcb366dd68d717 (diff)
rm0 wire stats feature into generate with STATS_PAGE toggle
Final integration of the stats-site feature: - Add STATS_PAGE config (default yes) mirroring SPLASH_PAGE through every layer: shuriken.default.conf, apply_config_defaults, CLI_OPTION_SPEC (--stats/--no-stats), the CLI_CONFIG_OVERRIDE_TARGETS allowlist (without which the flag was silently ignored), usage text, yes/no validation, print_config, the action config list + effective-setting log, the generation metadata, and the dry-run plan. - generate() now calls a gated generate_stats_pages helper after the album pages (and before archiving, so the stats/camera pages are tarballed): collect_photo_exif_stats + render_stats_page . . + render_camera_pages . . - Emit "stats_page" in shuriken.json next to "splash_page". - Gate the header Stats nav link behind STATS_PAGE via a new render_stats_page_html config_html field, so it is hidden (no 404) when stats are disabled. - Document STATS_PAGE and --stats/--no-stats in the README. - Tests: full --generate produces stats.html + camera-<slug>.html + the nav link and stats_page=true; --no-stats suppresses all three and sets stats_page=false; print-config and metadata expectations updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'tests')
-rwxr-xr-xtests/cli.sh118
1 files changed, 118 insertions, 0 deletions
diff --git a/tests/cli.sh b/tests/cli.sh
index 50a61f3..6f56dad 100755
--- a/tests/cli.sh
+++ b/tests/cli.sh
@@ -142,6 +142,7 @@ assert metadata["settings"]["maxpreviews"] == maxpreviews
assert metadata["settings"]["image_jobs"] == "3"
assert metadata["settings"]["shuffle"] is False
assert isinstance(metadata["settings"]["splash_page"], bool)
+assert isinstance(metadata["settings"]["stats_page"], bool)
assert "original_basepath" in metadata["settings"]
PY
}
@@ -1308,6 +1309,7 @@ IMAGEMAGICK_TIMEOUT=60
RANDOM_SEED=''
SHUFFLE=no
SPLASH_PAGE=yes
+STATS_PAGE=yes
TARBALL_INCLUDE=yes
TARBALL_SUFFIX=.tar
TAR_TIMEOUT=120
@@ -1354,6 +1356,7 @@ IMAGEMAGICK_TIMEOUT=60
RANDOM_SEED=''
SHUFFLE=no
SPLASH_PAGE=yes
+STATS_PAGE=yes
TARBALL_INCLUDE=no
TARBALL_SUFFIX=.tar
TAR_TIMEOUT=120
@@ -1493,6 +1496,7 @@ IMAGEMAGICK_TIMEOUT=60
RANDOM_SEED=''
SHUFFLE=yes
SPLASH_PAGE=yes
+STATS_PAGE=yes
TARBALL_INCLUDE=no
TARBALL_SUFFIX=.tar
TAR_TIMEOUT=120
@@ -1534,6 +1538,7 @@ IMAGEMAGICK_TIMEOUT=60
RANDOM_SEED=''
SHUFFLE=no
SPLASH_PAGE=yes
+STATS_PAGE=yes
TARBALL_INCLUDE=no
TARBALL_SUFFIX=.tar
TAR_TIMEOUT=120
@@ -1600,6 +1605,7 @@ IMAGEMAGICK_TIMEOUT=60
RANDOM_SEED=cli-seed
SHUFFLE=yes
SPLASH_PAGE=no
+STATS_PAGE=yes
TARBALL_INCLUDE=yes
TARBALL_SUFFIX=.tar
TAR_TIMEOUT=120
@@ -3005,6 +3011,112 @@ test_generate_cli_no_splash_overrides_config() {
test::teardown
}
+# Synthetic `identify -verbose` output with EXIF the stats aggregation can parse,
+# so a full --generate produces a real camera leaderboard + per-camera page.
+test::stats_identify_output() {
+ printf '%s\n' \
+ ' Format: JPEG (Joint Photographic Experts Group JFIF format)' \
+ ' Geometry: 160x90+0+0' \
+ ' exif:Make: Canon' \
+ ' exif:Model: EOS R5' \
+ ' exif:FNumber: 28/10' \
+ ' exif:ExposureTime: 1/250' \
+ ' exif:PhotographicSensitivity: 400' \
+ ' exif:FocalLength: 50/1' \
+ ' exif:DateTimeOriginal: 2023:07:15 14:30:00'
+}
+
+test_generate_stats_pages_created_and_nav_linked() {
+ local config_file
+ local fake_bin
+ local -i nav_links
+
+ test::setup
+ fake_bin="$TEST_TMPDIR/bin"
+ config_file="$TEST_TMPDIR/shuriken.conf"
+
+ test::install_fake_imagemagick "$fake_bin"
+ PATH="$fake_bin:$PATH" \
+ test::generate_fixture_images "$TEST_TMPDIR/incoming"
+ test::write_album_config \
+ "$config_file" "$TEST_TMPDIR/incoming" "$TEST_TMPDIR/dist" \
+ 'Stats album' 40
+
+ (
+ cd "$TEST_TMPDIR"
+ PATH="$fake_bin:$PATH" \
+ TEST_IMAGEMAGICK_IDENTIFY_OUTPUT="$(test::stats_identify_output)" \
+ "$TEST_SHURIKEN" --generate --random-seed stats-seed
+ )
+
+ # Stats page and the per-camera page (Canon EOS R5 -> canon-eos-r5) exist.
+ test::assert_file_exists "$TEST_TMPDIR/dist/stats.html"
+ test::assert_file_exists "$TEST_TMPDIR/dist/camera-canon-eos-r5.html"
+ test::assert_contains 'Canon EOS R5' "$(<"$TEST_TMPDIR/dist/stats.html")"
+ test::assert_not_contains '<script' "$(<"$TEST_TMPDIR/dist/stats.html")"
+
+ # The header bar links to the stats page on at least one generated page.
+ nav_links=$(grep -lF 'stats.html">Stats' "$TEST_TMPDIR"/dist/*.html | wc -l)
+ test "$nav_links" -gt 0
+
+ python3 - "$TEST_TMPDIR/dist/shuriken.json" <<'PY'
+import json
+import pathlib
+import sys
+
+metadata = json.loads(pathlib.Path(sys.argv[1]).read_text())
+assert metadata["settings"]["stats_page"] is True
+PY
+
+ test::teardown
+}
+
+test_generate_no_stats_suppresses_pages_and_nav() {
+ local config_file
+ local fake_bin
+
+ test::setup
+ fake_bin="$TEST_TMPDIR/bin"
+ config_file="$TEST_TMPDIR/shuriken.conf"
+
+ test::install_fake_imagemagick "$fake_bin"
+ PATH="$fake_bin:$PATH" \
+ test::generate_fixture_images "$TEST_TMPDIR/incoming"
+ test::write_album_config \
+ "$config_file" "$TEST_TMPDIR/incoming" "$TEST_TMPDIR/dist" \
+ 'No stats album' 40
+
+ (
+ cd "$TEST_TMPDIR"
+ PATH="$fake_bin:$PATH" \
+ TEST_IMAGEMAGICK_IDENTIFY_OUTPUT="$(test::stats_identify_output)" \
+ "$TEST_SHURIKEN" --generate --no-stats --random-seed stats-seed
+ )
+
+ # No stats page, no per-camera pages.
+ test::assert_path_absent "$TEST_TMPDIR/dist/stats.html"
+ if compgen -G "$TEST_TMPDIR/dist/camera-*.html" >/dev/null; then
+ printf 'FAIL: --no-stats still produced camera pages\n' >&2
+ exit 1
+ fi
+ # No stats nav link anywhere.
+ if grep -RF 'stats.html">Stats' "$TEST_TMPDIR"/dist/*.html; then
+ printf 'FAIL: --no-stats still rendered the Stats nav link\n' >&2
+ exit 1
+ fi
+
+ python3 - "$TEST_TMPDIR/dist/shuriken.json" <<'PY'
+import json
+import pathlib
+import sys
+
+metadata = json.loads(pathlib.Path(sys.argv[1]).read_text())
+assert metadata["settings"]["stats_page"] is False
+PY
+
+ test::teardown
+}
+
test_refresh_splash_rewrites_only_index_from_existing_assets() {
local after_index
local after_metadata
@@ -5464,6 +5576,12 @@ main() {
'--generate --no-splash keeps root index redirect' \
test_generate_cli_no_splash_overrides_config
test::run_case \
+ '--generate creates stats and per-camera pages with nav link' \
+ test_generate_stats_pages_created_and_nav_linked
+ test::run_case \
+ '--generate --no-stats suppresses stats pages and nav link' \
+ test_generate_no_stats_suppresses_pages_and_nav
+ test::run_case \
'--refresh-splash rewrites only root index from existing assets' \
test_refresh_splash_rewrites_only_index_from_existing_assets
test::run_case \