#!/usr/bin/env python3 """Negative fixtures for public documentation publication checks.""" import importlib.util import json from pathlib import Path import tempfile import unittest spec = importlib.util.spec_from_file_location( "public_docs", Path(__file__).with_name("verify-public-docs.py") ) verifier = importlib.util.module_from_spec(spec) spec.loader.exec_module(verifier) class PublicationBoundaryTest(unittest.TestCase): def setUp(self): self.temp = tempfile.TemporaryDirectory(prefix="thoth-public-docs-") self.addCleanup(self.temp.cleanup) self.root = Path(self.temp.name) self.site = self.root / "site" self.config = "nav:\n- Home: index.md\nexclude_docs: |\n *\n" for source in ["index.md", *sorted(verifier.PUBLIC_ASSETS)]: self.config += f" !/{source}\n" self.write(self.root / "docs" / source, "# Public") self.write(self.site / verifier.output_path(source), "public") self.write(self.root / "mkdocs.yml", self.config) for asset in sorted(verifier.THEME_ASSETS): self.config += f" !/{asset}\n" self.write(self.site / asset, "theme fixture") self.write(self.root / "mkdocs.yml", self.config) self.search = {"docs": [{"location": "", "text": "public"}]} self.write_search() self.write(self.root / "docs/plans/private.md", "# Internal") self.write(self.root / "docs/reports/screenshot.png", "internal asset") self.write(self.site / "index.html", '') self.write(self.site / "css/base.css", '@font-face { src: url("../fonts/demo.woff2?v=1"); }') self.write(self.site / "fonts/demo.woff2", "font fixture") self.write(self.site / "js/base.js", "// script fixture") @staticmethod def write(path, content): path.parent.mkdir(parents=True, exist_ok=True) path.write_text(content) def write_search(self): self.write(self.site / "search/search_index.json", json.dumps(self.search)) def test_public_only(self): self.assertEqual(verifier.check(self.root, self.site), 1) def test_internal_nav(self): self.write(self.root / "mkdocs.yml", self.config.replace( "- Home: index.md", "- Home: index.md\n- Internal: plans/private.md" )) with self.assertRaisesRegex(ValueError, "internal page"): verifier.check(self.root, self.site) def test_unlisted_exception(self): self.write(self.root / "mkdocs.yml", self.config + " !/plans/private.md\n") with self.assertRaisesRegex(ValueError, "exceptions must match"): verifier.check(self.root, self.site) def test_missing_deny_default(self): self.write(self.root / "mkdocs.yml", self.config.replace(" *\n", "")) with self.assertRaisesRegex(ValueError, "deny-by-default"): verifier.check(self.root, self.site) def test_leaked_internal_page(self): self.write(self.site / "plans/private/index.html", "internal") with self.assertRaisesRegex(ValueError, "internal file leaked"): verifier.check(self.root, self.site) def test_leaked_internal_asset(self): self.write(self.site / "reports/screenshot.png", "internal") with self.assertRaisesRegex(ValueError, "internal file leaked"): verifier.check(self.root, self.site) def test_internal_search_entry(self): self.search["docs"].append({"location": "plans/private/#internal", "text": "internal"}) self.write_search() with self.assertRaisesRegex(ValueError, "non-public page in search"): verifier.check(self.root, self.site) def test_missing_public_page(self): (self.site / "index.html").unlink() with self.assertRaisesRegex(ValueError, "missing generated public"): verifier.check(self.root, self.site) def test_missing_theme_stylesheet(self): (self.site / "css/base.css").unlink() with self.assertRaisesRegex(ValueError, "missing generated page asset: css/base.css"): verifier.check(self.root, self.site) def test_docs_cannot_shadow_theme_assets(self): self.write(self.root / "docs/css/base.css", "internal content") with self.assertRaisesRegex(ValueError, "docs source shadows theme asset"): verifier.check(self.root, self.site) def test_missing_theme_script(self): (self.site / "js/base.js").unlink() with self.assertRaisesRegex(ValueError, "missing generated page asset: js/base.js"): verifier.check(self.root, self.site) def test_missing_css_font(self): (self.site / "fonts/demo.woff2").unlink() with self.assertRaisesRegex(ValueError, "missing generated page asset: fonts/demo.woff2"): verifier.check(self.root, self.site) def test_asset_outside_site_prefix(self): self.write(self.root / "mkdocs.yml", self.config + "site_url: https://docs.example.invalid/manual/\n") self.write(self.site / "index.html", '') with self.assertRaisesRegex(ValueError, "asset escapes public site prefix"): verifier.check(self.root, self.site) def test_nested_installation_asset_resolution(self): self.write(self.site / "install/manual/index.html", '') verifier.check_assets(self.site, ["install/manual.md"], "https://docs.example.invalid/manual/") (self.site / "css/base.css").unlink() with self.assertRaisesRegex(ValueError, "missing generated page asset: css/base.css"): verifier.check_assets(self.site, ["install/manual.md"], "https://docs.example.invalid/manual/") if __name__ == "__main__": unittest.main()