e128682fd48d974c0eaa74366087d2be47080bf5
braney
  Fri Sep 4 11:43:52 2026 -0700
docent: add make preflight, which checks the fixtures a test suite does not own

A Docent test that loads a saved session or attaches a hub depends on something
outside the tree, and the failure when that thing goes away is silent rather
than loud. A session that has been renamed or deleted is not an error: hgTracks
answers HTTP 200 with a page titled "Very Early Error" whose body reads "Could
not find session NAME for user USER", the page carries no track image, and every
noText: assertion on it passes. The run goes green having tested nothing.
Verified both ways: a bogus session name passes a test whose only check is
noText:, and fails once the script also asserts noText: "Could not find
session".

preflight.js reads the fixtures out of the scripts themselves -- loadSession:,
hub:, addHub:, addCustomTrack: url:, and hubUrl= inside a goto: -- so the list
cannot drift from what the scripts actually use. It needs no browser, runs in a
couple of seconds, and exits non-zero if anything is unreachable, which is what
lets a nightly run tell "the fixtures are gone" from "a bug came back".

Checking it against the sessions cited by the tickets in #38252 found four that
do not exist on genome-test because they were saved on the RR or on beta, and
corrected one I had wrongly called missing: session names store a dash as %2D,
so a MySQL LIKE with a literal dash misses them. The HTTP check has no such
problem, which is a reason to prefer it over a query against namedSessionDb.

refs #37892 #38252

diff --git src/hg/utils/docent/tests/docentTest.mk src/hg/utils/docent/tests/docentTest.mk
index cd19e8bf5f3..6a7b08adf14 100644
--- src/hg/utils/docent/tests/docentTest.mk
+++ src/hg/utils/docent/tests/docentTest.mk
@@ -1,39 +1,50 @@
 # Shared rules for a directory of Docent tests. Included by tests/makefile and by
 # tests/regress/makefile, so both directories run the same code rather than a copy of it.
 #
 # An including makefile sets, before the include:
 #   DOCENT     path to docent.js from THIS directory     (required)
+#   PREFLIGHT  path to preflight.js from THIS directory  (required)
 #   PARITY     which script `make parity` runs           (default: the first one found)
 #
 # Everything else -- which scripts are tests, which have derive baselines -- comes from
 # what is on disk here, so a new *.docent.yaml is picked up with no edit.
 
 ifndef DOCENT
 $(error include docentTest.mk only after setting DOCENT, e.g. DOCENT = ../docent.js)
 endif
+ifndef PREFLIGHT
+$(error include docentTest.mk only after setting PREFLIGHT, e.g. PREFLIGHT = ./preflight.js)
+endif
 
 PW_DIR ?= /hive/groups/browser/uiTest/pw
 PW_ENV ?= PLAYWRIGHT_BROWSERS_PATH=$(PW_DIR)/browsers NODE_PATH=$(PW_DIR)/node_modules
 T      ?=
 # `make parity` needs one script that is expected to PASS, so an .xfail one is no use as
 # the default. An including makefile can name a better one.
 PASSING := $(filter-out %.xfail,$(patsubst %.docent.yaml,%,$(wildcard *.docent.yaml)))
 PARITY ?= $(firstword $(PASSING))
 TESTS  := $(if $(T),$(addsuffix .docent.yaml,$(T)),$(wildcard *.docent.yaml))
 
-.PHONY: test parity clean
+.PHONY: test parity clean preflight
+
+# The fixtures the scripts here name but do not contain: saved sessions, hub URLs, the
+# server itself. No browser, so this is seconds, and it is what separates "the fixtures
+# went away" from "a bug came back" -- which are the same red without it. Run it before
+# the suite, and on its own as often as you like.
+preflight:
+	@$(PW_ENV) node $(PREFLIGHT) .
 
 test:
 	@if [ -z "$(strip $(TESTS))" ]; then \
 	  echo "no *.docent.yaml here -- nothing was tested"; exit 1; fi
 	@fail=0; \
 	for f in $(TESTS); do \
 	  b=$${f%.docent.yaml}; want=0; \
 	  case $$b in *.xfail) want=1;; esac; \
 	  if [ $$want = 1 ]; then printf '=== %s (expected to fail)\n' "$$b"; \
 	  else printf '=== %s\n' "$$b"; fi; \
 	  $(PW_ENV) node $(DOCENT) $$f > $$b.log 2>&1; got=$$?; \
 	  if [ $$got -ne 0 ] && [ $$want -eq 0 ]; then \
 	    echo "  FAILED -- run said:"; sed 's/^/    /' $$b.log; fail=1; \
 	  elif [ $$got -eq 0 ] && [ $$want -eq 1 ]; then \
 	    echo "  FAILED -- this was supposed to fail, and it passed"; fail=1; \