msos/tools/tests/test_build_content.py

94 lines
3.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
Characterization tests for tools/build_content.py (U2).
The core guarantee (plan R5): rendering every content/ entry reproduces the
CURRENT live detail page, compared semantically (whitespace- and
attribute-order-insensitive). This is what protects against SEO/layout
regressions when the build takes over page generation.
Run directly: python tools/tests/test_build_content.py
Or with pytest: pytest tools/tests/test_build_content.py
"""
import os
import sys
import unittest
TOOLS = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
ROOT = os.path.dirname(TOOLS)
sys.path.insert(0, TOOLS)
import build_content as bc # noqa: E402
def _render(out_rel):
"""Render the content entry whose output path ends with out_rel."""
for meta, body, _path in bc.iter_entries():
if bc.output_path(meta).replace(os.sep, "/").endswith(out_rel):
base = bc._template_html(meta)
return meta, bc.build_entry(meta, body, base)
raise AssertionError(f"no content entry maps to {out_rel}")
def _current(out_rel):
with open(os.path.join(ROOT, out_rel), encoding="utf-8") as f:
return f.read()
class ReproducesEverySite(unittest.TestCase):
def test_all_pages_semantically_equivalent(self):
"""Every rendered page must equal the current page (semantic compare)."""
_built, mismatches = bc.run(check=True, write=False)
self.assertEqual(mismatches, [], f"pages diverged from the live site: {mismatches}")
class TitleSuffixFaithful(unittest.TestCase):
def test_cyrillic_suffix_preserved(self):
# MK pages use ' - МСОС' (Cyrillic); it must survive, not be dropped.
meta, rendered = _render("mk/projects/macedonian-student-night-in-ljubljana/index.html")
self.assertIn("- МСОС</title>", rendered) # - МСОС
def test_no_suffix_stays_absent(self):
# Some event pages have no ' - MSOS' suffix; we must not invent one.
_meta, rendered = _render("en/projects/morning-coffee-in-front-of-ctk/index.html")
self.assertNotIn("- MSOS</title>", rendered)
class RichContentSurvives(unittest.TestCase):
def test_event_gallery_and_lightbox_render(self):
_meta, rendered = _render("en/projects/paint-and-wine-at-sunset/index.html")
self.assertIn("event-gallery", rendered)
self.assertIn("eg-thumb", rendered)
def test_blog_guidebox_and_scrollspy_render(self):
_meta, rendered = _render("en/blog/how-to-open-slovenian-bank-account/index.html")
self.assertIn("guide-box", rendered)
self.assertIn('class="scrollspy-nav"', rendered)
class ScrollspyPreservesCuratedLabels(unittest.TestCase):
def test_hand_shortened_labels_not_overwritten(self):
# The body H2 is long; the sidebar label was shortened by hand. The
# rendered page must keep the curated short label, not the H2 text.
_meta, rendered = _render("en/projects/macedonian-student-night-in-ljubljana/index.html")
self.assertIn(">Idea to Movement</a>", rendered)
class HeroPathDepth(unittest.TestCase):
def test_hero_src_uses_relative_depth(self):
_meta, rendered = _render("en/blog/how-to-open-slovenian-bank-account/index.html")
# Hero must resolve from the page's depth, not a root-absolute path.
self.assertIn('src="../../../images/', rendered)
self.assertNotIn('src="/images/', rendered)
class NewsStructure(unittest.TestCase):
def test_news_uses_news_article_body(self):
_meta, rendered = _render("en/news/proof-of-means-updated-2026/index.html")
self.assertIn('class="news-article-body"', rendered)
self.assertIn('class="news-article-date"', rendered)
if __name__ == "__main__":
unittest.main(verbosity=2)