feat: expand portfolios, drop wedding crowd photos, offer studio on site

- 28 more of the operator's own public images (42 -> 67), alts from his captions
- remove group-feier, group-outdoor, group-posiert (wedding guests visible)
- studio section now offers Studio Neumuenster and Mobiles Studio
- deploy: --force-recreate on release and rollback, plus in-container content probe
- verify: DESFOTO_BASE dry-run, redirect, asset and privacy checks
- tests: 57 OK (3 new content guards, 2 deploy/verify regression guards)
This commit is contained in:
desfoto automation
2026-09-19 17:37:49 +02:00
parent 447b909d05
commit 8ffeea6731
141 changed files with 476 additions and 72 deletions

View File

@@ -408,6 +408,17 @@ class Redesign(unittest.TestCase):
for image in (SITE / "assets" / "img").glob(f"{slug}-*.webp"):
self.fail(f"stale image built: {image.name}")
def test_wedding_crowd_photos_are_not_shipped(self):
"""The operator asked to drop the wedding photos showing the guests."""
manifest = json.loads(read(SRC / "images.json"))
slugs = {entry["slug"] for entry in manifest["images"]}
for slug in ("group-feier", "group-outdoor", "group-posiert"):
with self.subTest(slug=slug):
self.assertNotIn(slug, slugs)
self.assertEqual([], list((SITE / "assets" / "img").glob(f"{slug}-*.webp")))
for file in HTML_FILES:
self.assertNotIn(slug, read(file))
def test_sitemap_lists_the_new_route_only(self):
sitemap = read(SITE / "sitemap.xml")
self.assertIn("<loc>https://desfoto.de/shootings/</loc>", sitemap)
@@ -454,6 +465,52 @@ class Redesign(unittest.TestCase):
with self.subTest(file=rel, needle=needle):
self.assertIn(needle, text)
def test_studio_in_neumuenster_is_offered_alongside_the_mobile_studio(self):
"""Projects can also be photographed in the operator's own studio on site."""
for rel in (
"index.html",
"shootings/index.html",
"businessfotografie/index.html",
"ueber/index.html",
):
text = read(SITE / rel)
for needle in ("Studio Neumünster", "Studio in Neumünster"):
with self.subTest(file=rel, needle=needle):
self.assertIn(needle, text)
def test_portfolio_expansion_covers_more_of_the_public_work(self):
pages = {
"projekte": (
"music-dreadlocks",
"music-horns",
"band-extinct-group",
"free-castle-lake",
"free-swans",
"free-sparrow",
"free-dogs-forest",
),
"familien-und-paare": (
"couple-park",
"couple-backlight-kiss",
"couple-forest-walk",
"couple-sun-silhouette",
"couple-laugh-walk",
"portrait-marina-snow",
),
"portrait-und-model": ("editorial-studio-side", "editorial-balance", "editorial-leap"),
"businessfotografie": (
"business-hotel-window",
"business-hotel-orange",
"business-hotel-tea",
"business-hotel-laugh",
),
}
for rel, slugs in pages.items():
text = read(SITE / f"{rel}/index.html")
for slug in slugs:
with self.subTest(page=rel, slug=slug):
self.assertIn(f"/assets/img/{slug}-1200.webp", text)
def test_music_and_free_work_portfolio_is_present(self):
projekte = read(SITE / "projekte" / "index.html")
video = read(SITE / "video" / "index.html")
@@ -514,6 +571,24 @@ class Release(unittest.TestCase):
self.assertIn('mktemp "$STACK_DIR/', deploy)
self.assertIn('sudo mv "$stack_tmp" "$stack_file"', deploy)
def test_deploy_rebinds_the_site_tree_after_replacing_it(self):
"""Regression: `rm -rf site` + tar replaces the bind-mount inode.
A container that is merely running keeps the deleted directory mounted and
then serves an empty document root (every request 404), so both the release
path and the rollback path must recreate the container.
"""
deploy = read(ROOT / ".ocauto" / "deploy")
self.assertEqual(2, deploy.count("up -d --force-recreate --remove-orphans"))
self.assertIn("docker exec desfoto-web-1 wget -q --spider http://127.0.0.1/", deploy)
def test_verify_hook_covers_the_renamed_route(self):
verify = read(ROOT / ".ocauto" / "verify")
self.assertIn('check_redirect "$base/fotografie/" "$base/shootings/"', verify)
self.assertIn("""check_not_contains "$base/" 'href="/fotografie/'""", verify)
self.assertNotIn('/fotografie/ /businessfotografie/', verify)
self.assertNotIn("hero-studio-1200.webp", verify)
def test_deploy_hooks_are_executable(self):
import os