Files
vue-seo-layer/tests/rootFiles.test.ts
Jakub Zych d1629439c7 feat: root files from the CMS and the 404 report
- server/middleware/seo-root.ts proxies /robots.txt, /sitemap.xml,
  /sitemap-<name>.xml, /llms.txt and the verification and IndexNow key
  files the site answer lists in root_files, with the backend's status,
  content type and X-Robots-Tag; a backend failure is 503 Retry-After 60
- server/lib/site.ts caches GET /_seo/api/v1/site (300 s, cleared by the
  purge); a backend without it (404) keeps the v0.1.1 behaviour: no root
  proxies, the verification lookup by name
- the 404 report and the verification lookup honour seo.cms and rootFiles
- smoke runs for the layer off (seo.cms and the v0.1.1 enabled switch), the
  CMS down (fail open) and no 404 token; npm run purge:check
2026-11-04 09:26:00 +01:00

73 lines
4.0 KiB
TypeScript

import assert from 'node:assert/strict'
import test from 'node:test'
import { rootFileTargets, siteState, type SeoSiteState } from '../server/lib/site.ts'
import { seoBackendBase, seoServerConfig } from '../server/lib/config.ts'
const B = 'http://127.0.0.1:8096'
const A = 'http://127.0.0.1:8096/_seo/api/v1'
const KEY = 'a1b2c3d4e5f60718293a4b5c6d7e8f90.txt'
const ok: SeoSiteState = { state: 'ok', site: { root_files: ['google1234.html', KEY, 'BingSiteAuth.xml'] } }
test('rootFileTargets: robots.txt, sitemap.xml, llms.txt and sitemap files go to the backend', () => {
assert.deepEqual(rootFileTargets('/robots.txt', ok, B, A), [{ kind: 'root', url: `${B}/robots.txt` }])
assert.deepEqual(rootFileTargets('/sitemap.xml?x=1', ok, B, A), [{ kind: 'root', url: `${B}/sitemap.xml` }])
assert.deepEqual(rootFileTargets('/llms.txt', { state: 'unknown' }, B, A), [{ kind: 'root', url: `${B}/llms.txt` }])
assert.deepEqual(rootFileTargets('/sitemap-posts-1.xml', ok, B, A), [{ kind: 'sitemap', url: `${B}/_sitemap/files/sitemap-posts-1.xml` }])
})
test('rootFileTargets: an absent site answer (older backend) serves none of them', () => {
for (const p of ['/robots.txt', '/sitemap.xml', '/llms.txt', '/sitemap-posts-1.xml', '/google1234.html']) {
assert.equal(rootFileTargets(p, { state: 'absent' }, B, A), null, p)
}
})
test('rootFileTargets: listed root files only, verification first then the IndexNow key', () => {
assert.deepEqual(rootFileTargets('/google1234.html', ok, B, A), [{ kind: 'verification', url: `${A}/verification/google1234.html` }])
assert.deepEqual(rootFileTargets(`/${KEY}`, ok, B, A), [
{ kind: 'verification', url: `${A}/verification/${KEY}` },
{ kind: 'indexnow', url: `${B}/_sitemap/indexnow/${KEY}` },
])
for (const p of ['/random.txt', '/kontakt', '/google1234.html/x', '/a/google1234.html', '/GOOGLE1234.HTML']) {
assert.equal(rootFileTargets(p, ok, B, A), null, p)
}
assert.equal(rootFileTargets('/google1234.html', { state: 'unknown' }, B, A), null)
})
test('rootFileTargets: no request text beyond an allow-listed name reaches the URL', () => {
for (const p of ['/sitemap-../../x.xml', '/sitemap-%2e%2e.xml', '/robots.txt/../admin', '//evil.test/robots.txt', '/sitemap-A.xml']) {
assert.equal(rootFileTargets(p, ok, B, A), null, p)
}
})
test('siteState: 200 object ok, 404 absent, anything else unknown', () => {
assert.equal(siteState(200, { root_files: [] }).state, 'ok')
assert.equal(siteState(404, { error: 'not_found' }).state, 'absent')
for (const [s, b] of [[500, {}], [200, 'x'], [200, null], [null, null], [200, []]] as const) assert.equal(siteState(s, b).state, 'unknown')
})
test('seoBackendBase: explicit, derived from apiBase, or its origin', () => {
assert.equal(seoBackendBase('http://go:9000/', A), 'http://go:9000')
assert.equal(seoBackendBase('', A), B)
assert.equal(seoBackendBase('', 'http://go:9000/_seo/api/v1/'), 'http://go:9000')
assert.equal(seoBackendBase('', 'http://go:9000/seo'), 'http://go:9000')
assert.equal(seoBackendBase('', 'not a url'), '')
})
test('seoServerConfig: defaults, aliases and tokens', () => {
const d = seoServerConfig({})
assert.equal(d.enabled, true)
assert.equal(d.apiBase, A)
assert.equal(d.backendBase, B)
assert.equal(d.timeoutMs, 1500)
assert.equal(d.resolveCacheTtl, 60)
assert.equal(d.rootFiles, true)
assert.deepEqual(d.resolveFiles, ['pdf'])
assert.equal(seoServerConfig({ public: { seo: { enabled: false } } }).enabled, false)
assert.equal(seoServerConfig({ public: { seo: { cms: 'false' } } }).enabled, false)
assert.equal(seoServerConfig({ public: { seo: { rootFiles: false } } }).rootFiles, false)
assert.equal(seoServerConfig({ seo: { resolveCacheTtl: '0' } }).resolveCacheTtl, 0)
assert.equal(seoServerConfig({ seo: { timeoutMs: '2500' } }).timeoutMs, 2500)
assert.equal(seoServerConfig({ seo: { notfoundToken: 't' } }).routeReportToken, 't')
assert.equal(seoServerConfig({ seo: { notfoundToken: 't', routeReportToken: 'r' } }).routeReportToken, 'r')
})