6c2ecd053818535194c7845a899bca0ecd3c10d8
braney
  Thu Sep 17 17:08:10 2026 -0700
docent: preflight checks a session settings file named inside a goto: URL, refs #38252

`loadSession:` is the verb for a saved state, and preflight already checks the
file it names.  But loadSession: builds its own URL, so a test about db= TOGETHER
with a session load has to spell the whole request out in a goto: -- and db= in
front of the load is the entire bug in #38184.  The settings file it names rots
the same way any other fixture does, silently, because hgTracks answers a missing
one with a perfectly good page.

Read hgS_loadUrlName out of a goto: the way hubUrl is already read out of one.

diff --git src/hg/utils/docent/tests/preflight.js src/hg/utils/docent/tests/preflight.js
index a495a1ef7b9..e2640a5cc0c 100644
--- src/hg/utils/docent/tests/preflight.js
+++ src/hg/utils/docent/tests/preflight.js
@@ -171,30 +171,41 @@
     } else if (verb === 'login') {
       // Keyed by server, so a directory pointed at two of them reports two accounts.
       const acct = loginAccount(server);
       add({ script: f, kind: 'login', label: `${acct.label} on ${server}`,
             check: async () => acct.why });
     } else if (verb === 'goto' && typeof arg === 'string') {
       // A hub can also arrive inside a goto: URL, which is the only way to write a test
       // about the genome= form (the hub: verb builds db=). Pull hubUrl out of the query
       // so those hubs are checked too, rather than being invisible to preflight because
       // of how the step happens to be spelled.
       const m = /[?&]hubUrl=([^&]+)/.exec(arg);
       if (m) {
         const url = decodeURIComponent(m[1]);
         add({ script: f, kind: 'hub', label: url, check: urlCheck(url, 'hub') });
       }
+      // And so can a session settings file. `loadSession:` is the verb for one, but it
+      // builds the URL itself, so a test about db= TOGETHER WITH a session load has to
+      // spell the whole thing out in a goto: -- and the settings file it names rots the
+      // same way any other fixture does, silently, because hgTracks answers a missing one
+      // with a perfectly good page.
+      const sm = /[?&]hgS_loadUrlName=([^&]+)/.exec(arg);
+      if (sm) {
+        const url = decodeURIComponent(sm[1]);
+        if (/^https?:/.test(url))
+          add({ script: f, kind: 'session-url', label: url, check: urlCheck(url) });
+      }
     } else if (verb === 'hub' || verb === 'addHub') {
       const url = (typeof arg === 'string') ? arg : (o && o.url);
       // A hub.txt replaced by a directory listing or an error page still answers 200, so
       // require the one word every hub.txt has to contain.
       if (url) add({ script: f, kind: 'hub', label: url, check: urlCheck(url, 'hub') });
     } else if (verb === 'addCustomTrack') {
       if (o && o.url) add({ script: f, kind: 'ct-url', label: o.url, check: urlCheck(o.url) });
       const rel = o && (o.file || o.pasteFile);
       if (rel) add({ script: f, kind: 'file', label: rel, check: fileCheck(path.join(DIR, rel)) });
     }
   }
 }
 
 for (const [server, f] of seenServer) {
   fixtures.unshift({ script: f, kind: 'server', label: server,