Files
bench/tests/test_site_deploy.py
T
istosandClaude Opus 5 274ad6b2c6 site: the landing page, on things bench really does
Turn design 1b (Dockside) into `/`: a terminal hero, the claim README.md
already makes, six doors, and a strip at the foot. The layout is task
31's; what this commit is really about is that nothing factual on the
page is typed by hand.

- **Two facts are read, not written.** `site/build.py` reads the install
  one-liner out of README.md's "Install into a repo" block and the
  version out of `manager/core/VERSION`, and offers them to the template
  as `$install_block` and `$version`. A renamed section, a missing
  VERSION or an install section that lost its command block stops the
  build, exactly as a renamed heading already did. `pages.json` loses
  its `version` key, and a build refuses one if it comes back.

- **A dead internal link stops the build.** Every href a rendered page
  emits — a door as much as a link inside a slice — must resolve to a
  route in the manifest or a file in `static/`/`root/`. The check runs
  after rendering and before writing, so a bad link leaves the last good
  build standing rather than shipping a 404 with a nice typeface.

- **Six real doors.** `pages.json` grows the routes they open: install
  and first run, the five stages, agents on the board, PRs and review,
  team mode, the three-layer law. They are heading slices, i.e. the
  stub routes task 33 expected and task 34 will re-cut.

- **The terminal is a transcript.** README.md's own command, then lines
  install.py and board.py really print, with the abridgement declared in
  the terminal's title bar. `tests/test_site_landing.py` holds every one
  of those lines against the source that prints it, so a reworded prompt
  fails the suite instead of quietly making the page fiction.

- **No fake telemetry.** Turn 1's "most opened this week" strip becomes
  the version, read from VERSION, and a link to the releases.

The landing page no longer carries a generated body, so the tests that
read one from it now read `/concepts/stages/`, and the two scratch repos
in the suite copy VERSION alongside the markdown.

python3 -m unittest: 407 tests, OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 12:10:25 +02:00

418 lines
17 KiB
Python

"""bench.12vectors.com is a Cloudflare Worker serving site/dist/ as
static assets. What can be tested here is everything the deploy depends
on *before* it reaches Cloudflare: that the config says what the site
needs it to say, that the build produces the files that config names,
and that the three of them — wrangler.jsonc, pages.json, README.md —
cannot drift apart about which domain this is.
What cannot be tested here is a live response. `not_found_handling` is
asserted as configuration plus the 404.html it points at; that a request
to a missing path comes back 404 is Cloudflare's half, and the deploy
checklist in site/README.md is where a person confirms it.
python3 -m unittest discover -s tests
"""
import json
import re
import shutil
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
from urllib.parse import urlparse
REPO = Path(__file__).resolve().parents[1]
SITE = REPO / "site"
WRANGLER = SITE / "wrangler.jsonc"
HEADERS = SITE / "root" / "_headers"
README = SITE / "README.md"
# Set on every response, whatever it is. The task's three, plus the two
# that come free with them.
BASELINE = ("X-Content-Type-Options", "Referrer-Policy",
"Strict-Transport-Security")
try:
import markdown_it # noqa: F401
HAS_MARKDOWN_IT = True
except ImportError: # pragma: no cover - environment
HAS_MARKDOWN_IT = False
# ── reading the two config formats by hand ────────────────────────────
COMMENT = re.compile(r'"(?:\\.|[^"\\])*"|//[^\n]*|/\*.*?\*/', re.DOTALL)
def read_jsonc(path: Path) -> dict:
"""wrangler.jsonc is JSON with comments, and comments are how that
file explains itself. Strings are matched first so a `//` inside one
survives."""
def keep(match):
text = match.group(0)
return text if text.startswith('"') else " "
return json.loads(COMMENT.sub(keep, path.read_text(encoding="utf-8")))
def read_headers(path: Path) -> list:
"""`_headers` as [(pattern, {header: value})], in file order — an
unindented line opens a rule, an indented one adds a header to it."""
rules, current = [], None
for line in path.read_text(encoding="utf-8").splitlines():
if not line.strip() or line.lstrip().startswith("#"):
continue
if not line.startswith((" ", "\t")):
current = (line.strip(), {})
rules.append(current)
elif current is not None:
name, _, value = line.strip().partition(":")
current[1][name.strip()] = value.strip()
return rules
def readme_text() -> str:
return README.read_text(encoding="utf-8")
def rule(pattern: str) -> dict:
for found, headers in read_headers(HEADERS):
if found == pattern:
return headers
raise AssertionError(f"_headers has no rule for {pattern}")
def run_build(repo: Path, out: Path) -> subprocess.CompletedProcess:
return subprocess.run(
[sys.executable, str(repo / "site" / "build.py"), "--out", str(out)],
capture_output=True, text=True, cwd=repo)
class Built(unittest.TestCase):
"""The real site, built once into a scratch directory."""
@classmethod
def setUpClass(cls):
if not HAS_MARKDOWN_IT:
raise unittest.SkipTest("markdown-it-py is not installed")
cls.out = Path(tempfile.mkdtemp(prefix="bench-deploy-")).resolve()
result = run_build(REPO, cls.out)
if result.returncode != 0: # not assert: must survive python -O
raise RuntimeError(
f"site/build.py failed:\n{result.stdout}{result.stderr}")
@classmethod
def tearDownClass(cls):
if hasattr(cls, "out"):
shutil.rmtree(cls.out, ignore_errors=True)
# ── the worker config ─────────────────────────────────────────────────
class TheWorkerServesTheBuild(unittest.TestCase):
"""site/wrangler.jsonc, read the way wrangler reads it."""
def setUp(self):
self.config = read_jsonc(WRANGLER)
self.assets = self.config.get("assets", {})
def test_the_assets_directory_is_what_the_builder_writes(self):
"""Paths in wrangler config are relative to the config file, so
this one has to resolve to site/dist and not to a sibling of the
repo root."""
directory = (WRANGLER.parent / self.assets["directory"]).resolve()
self.assertEqual(directory, (SITE / "dist").resolve())
def test_there_is_no_worker_script(self):
"""The site is files. A `main` would mean a fetch handler to
maintain, and nothing here needs one."""
self.assertNotIn("main", self.config)
def test_a_url_has_exactly_one_form(self):
"""force-trailing-slash: /x redirects to /x/, which is the url
the pages link and the one rel=canonical names. Anything else
leaves two urls serving one page."""
self.assertEqual("force-trailing-slash",
self.assets.get("html_handling"))
def test_an_unknown_path_gets_the_sites_own_404(self):
"""Not single-page-application, which answers 200 with the
landing page and tells a crawler every typo is a real url."""
self.assertEqual("404-page", self.assets.get("not_found_handling"))
def test_the_route_is_a_custom_domain(self):
routes = self.config.get("routes", [])
self.assertEqual(1, len(routes), "expected exactly one route")
self.assertTrue(routes[0].get("custom_domain"),
"the route is not a custom domain")
def test_the_worker_is_named(self):
self.assertTrue(self.config.get("name"))
def test_a_compatibility_date_is_pinned(self):
self.assertRegex(self.config.get("compatibility_date", ""),
r"^\d{4}-\d{2}-\d{2}$")
class TheDomainIsWrittenDownOnce(unittest.TestCase):
"""Three files name this site's address. They are allowed to say it
three times; they are not allowed to disagree."""
def setUp(self):
self.config = read_jsonc(WRANGLER)
self.manifest = json.loads(
(SITE / "pages.json").read_text(encoding="utf-8"))
def test_the_route_is_the_host_the_pages_canonicalise_to(self):
"""A rel=canonical pointing somewhere the Worker does not answer
is worse than none at all."""
canonical = urlparse(self.manifest["site"]["base_url"])
self.assertEqual("https", canonical.scheme)
self.assertEqual(canonical.netloc,
self.config["routes"][0]["pattern"])
def test_the_readme_names_the_worker_and_the_route(self):
"""Acceptance: the next person should not have to guess where
the site lives."""
readme = README.read_text(encoding="utf-8")
self.assertIn(self.config["name"], readme)
self.assertIn(self.config["routes"][0]["pattern"], readme)
def test_the_readme_names_the_deploy_command(self):
self.assertRegex(readme_text(), r"wrangler[^\n]*\bdeploy\b")
def test_the_readme_names_the_account(self):
self.assertRegex(readme_text(), r"(?i)\baccount\b")
# ── the 404 page ──────────────────────────────────────────────────────
class TheNotFoundPageIsAPage(Built):
"""`not_found_handling: "404-page"` is half of it. The other half is
that a 404.html exists, looks like the rest of the site, and offers a
way out."""
def setUp(self):
self.page = (self.out / "404.html").read_text("utf-8")
def test_it_is_written_where_the_host_looks(self):
"""A 404/index.html would never be found: the host wants a file
called 404.html at the root of the assets directory."""
self.assertTrue((self.out / "404.html").is_file())
self.assertFalse((self.out / "404" / "index.html").exists())
def test_it_wears_the_sites_design(self):
self.assertIn('rel="stylesheet" href="/static/site.css', self.page)
self.assertIn("404", self.page)
def test_it_offers_the_way_back(self):
self.assertIn('href="/"', self.page)
def test_it_claims_no_canonical_url(self):
"""A 404 is not a page to canonicalise to, and telling a crawler
to index it is worse still."""
self.assertNotIn('rel="canonical"', self.page)
self.assertIn('name="robots" content="noindex"', self.page)
def test_it_stays_out_of_the_nav_and_the_sidebar(self):
home = (self.out / "index.html").read_text("utf-8")
article = (self.out / "concepts" / "claiming-a-card"
/ "index.html").read_text("utf-8")
for html in (home, article):
self.assertNotIn("404.html", html)
# ── headers ───────────────────────────────────────────────────────────
class TheHeaderPolicyTravelsWithTheBuild(Built):
"""site/root/_headers is the policy; the build has to carry it to
the root of dist/ or the host never reads it."""
def test_it_reaches_the_root_of_the_build(self):
shipped = self.out / "_headers"
self.assertTrue(shipped.is_file())
self.assertEqual(HEADERS.read_text("utf-8"), shipped.read_text("utf-8"))
def test_it_is_not_also_served_as_a_page(self):
"""The host consumes _headers rather than serving it, but the
build must not have made a route out of it either."""
self.assertFalse((self.out / "_headers" / "index.html").exists())
def test_no_page_needs_what_the_policy_forbids(self):
"""The CSP carries no 'unsafe-inline', so an inline <script> or a
style="" attribute is a page that renders right in every test
here and wrong in production. The only place that can be caught
before a deploy is the build output."""
for path in sorted(self.out.rglob("*.html")):
html = path.read_text("utf-8")
where = path.relative_to(self.out)
self.assertNotIn("<script", html.lower(),
f"{where} has a script the CSP would block")
self.assertNotRegex(
html, r"""\sstyle\s*=\s*["']""",
f"{where} has an inline style the CSP would block")
class EveryResponseCarriesTheBaseline(unittest.TestCase):
def setUp(self):
self.everything = rule("/*")
def test_the_three_headers_a_public_page_owes(self):
for name in BASELINE:
self.assertIn(name, self.everything)
def test_hsts_is_a_year_and_does_not_preload(self):
value = self.everything["Strict-Transport-Security"]
self.assertIn("max-age=31536000", value)
self.assertNotIn("preload", value,
"preload is a submission to browser vendors, "
"not a header to set in passing")
def test_nothing_third_party_can_load(self):
"""The site's promise — no analytics, no font CDN — as something
the browser enforces rather than something a test asserted once
at build time."""
policy = self.everything["Content-Security-Policy"]
self.assertIn("default-src 'none'", policy)
self.assertNotIn("unsafe-inline", policy)
self.assertNotIn("unsafe-eval", policy)
self.assertNotIn("*", policy)
self.assertNotIn("//", policy, "the policy names another origin")
class TheTwoKindsOfFileAreCachedDifferently(unittest.TestCase):
"""The edge case in the task: a stale HTML page must not survive a
deploy, while the assets it links may live for a year."""
def test_html_revalidates_on_every_view(self):
value = rule("/*")["Cache-Control"]
self.assertIn("max-age=0", value)
self.assertIn("must-revalidate", value)
self.assertNotIn("immutable", value)
def test_fingerprinted_assets_are_kept_for_a_year(self):
value = rule("/static/*")["Cache-Control"]
self.assertIn("max-age=31536000", value)
self.assertIn("immutable", value)
def test_the_general_rule_comes_first(self):
"""Order is the whole argument: /static/* overrides /* on
Cache-Control, and a host that merged them instead of overriding
would land on max-age=0 — the safe side."""
patterns = [pattern for pattern, _ in read_headers(HEADERS)]
self.assertLess(patterns.index("/*"), patterns.index("/static/*"))
# ── fingerprinting ────────────────────────────────────────────────────
class Scratch(unittest.TestCase):
"""A copy of site/ plus the markdown it reads, so a test can edit the
stylesheet without touching the real one."""
def setUp(self):
if not HAS_MARKDOWN_IT:
raise unittest.SkipTest("markdown-it-py is not installed")
self.root = Path(tempfile.mkdtemp(prefix="bench-deploy-")).resolve()
self.addCleanup(shutil.rmtree, self.root, True)
shutil.copytree(SITE, self.root / "site",
ignore=shutil.ignore_patterns("dist", "__pycache__"))
# The markdown the pages are cut from, plus the file the version
# shown on them is read from.
for name in ("AGENTS.md", "README.md", "manager/core/VERSION"):
(self.root / name).parent.mkdir(parents=True, exist_ok=True)
shutil.copy(REPO / name, self.root / name)
self.out = self.root / "site" / "dist"
def build(self):
return run_build(self.root, self.out)
def home(self) -> str:
return (self.out / "index.html").read_text("utf-8")
class TheStylesheetUrlFollowsTheStylesheet(Scratch):
"""`immutable` for a year is only safe because the url changes when
the file does. That is the claim under test."""
STAMP = re.compile(r'href="(/static/site\.css\?v=([0-9a-f]{10}))"')
def test_the_pages_link_a_stamped_url(self):
self.assertEqual(0, self.build().returncode)
self.assertRegex(self.home(), self.STAMP.pattern)
def test_the_file_still_sits_at_its_plain_path(self):
"""Only the link carries the stamp — nothing in static/ is
renamed, so the /static/* glob in _headers still matches and the
tree stays readable."""
self.assertEqual(0, self.build().returncode)
self.assertTrue((self.out / "static" / "site.css").is_file())
def test_editing_the_stylesheet_changes_the_stamp(self):
self.assertEqual(0, self.build().returncode)
before = self.STAMP.search(self.home()).group(2)
css = self.root / "site" / "static" / "site.css"
css.write_text(css.read_text("utf-8") + "\n.lost{color:red}\n",
encoding="utf-8")
self.assertEqual(0, self.build().returncode)
after = self.STAMP.search(self.home()).group(2)
self.assertNotEqual(before, after,
"a changed stylesheet kept its url, so a "
"year-long cache would keep the old one")
def test_a_rebuild_that_changes_nothing_keeps_the_stamp(self):
"""The other half: a deploy that did not touch the stylesheet
must not invalidate everyone's cache."""
self.assertEqual(0, self.build().returncode)
before = self.STAMP.search(self.home()).group(2)
self.assertEqual(0, self.build().returncode)
self.assertEqual(before, self.STAMP.search(self.home()).group(2))
class TheRootTreeIsCopiedVerbatim(Scratch):
def test_a_root_file_lands_at_the_top_of_the_build(self):
(self.root / "site" / "root" / "robots.txt").write_text(
"User-agent: *\n", encoding="utf-8")
self.assertEqual(0, self.build().returncode)
self.assertEqual("User-agent: *\n",
(self.out / "robots.txt").read_text("utf-8"))
def test_a_root_file_a_route_also_claims_stops_the_build(self):
"""Both would be written, one would win, and which one is not
something a person should have to work out from a diff."""
(self.root / "site" / "root" / "404.html").write_text(
"mine", encoding="utf-8")
result = self.build()
self.assertNotEqual(0, result.returncode)
self.assertIn("404.html", result.stderr)
class TheDeployIsInTheRepository(unittest.TestCase):
"""The two facts about deployment that live outside a config file."""
def test_the_worker_config_and_the_headers_are_tracked(self):
tracked = subprocess.run(
["git", "check-ignore", "site/wrangler.jsonc", "site/root/_headers"],
capture_output=True, text=True, cwd=REPO)
self.assertNotEqual(tracked.returncode, 0,
"the deploy config is gitignored")
def test_the_release_artifact_still_does_not_ship_the_site(self):
"""A Worker config in site/ must not start travelling into host
projects with a release."""
manifest = (REPO / "manager" / "core" / "release-manifest"
).read_text("utf-8")
for line in manifest.splitlines():
if line.strip().startswith("#") or not line.strip():
continue
self.assertNotIn("site", line.split(None, 1)[1],
f"the release manifest names site/: {line}")
if __name__ == "__main__":
unittest.main()