import assert from 'node:assert/strict' import { createServer } from 'node:http' import { spawn } from 'node:child_process' import { after, before, test } from 'node:test' import { createServer as createViteServer } from 'vite' import { render } from './dist/server/entry-server.js' const origin = 'https://ssr.example.invalid' const date = '2026-09-15T00:00:00+09:00' let api, apiURL, routes, vite, node, httpOrigin let failAPI = false const requests = [] const tag = locale => ({ id: 1, name: locale === 'ja' ? 'SSR タグ' : 'SSR シロシ', category: 'general', deprecated_at: null, aliases: [], parents: [], children: [], post_count: 1, created_at: date, updated_at: date, has_wiki: true, material_id: null, has_deerjikists: false, sections: [] }) const post = locale => ({ id: 1, version_no: 1, title: `SSR post ${locale}`, url: 'https://example.invalid/post', thumbnail: null, thumbnail_base: null, video_ms: null, tags: [tag (locale)], parent_posts: [], child_posts: [], sibling_posts: {}, viewed: false, related: [], original_created_from: date, original_created_before: date, created_at: date, updated_at: date, uploaded_user: null }) const wiki = locale => ({ id: 1, tag_id: 1, resource_type: 'tag_description', language_code: locale === 'ja' ? 'ja' : 'ain', title: tag (locale).name, body: `SSR wiki body ${locale}`, created_at: date, updated_at: date, revision_id: 1, pred: null, succ: null, deprecated_at: null }) const material = locale => ({ id: 1, version_no: 1, tag: tag (locale), file: null, url: 'https://example.invalid', thumbnail: null, thumbnail_fallback_text: 'SSR material', thumbnail_fallback_kind: 'tag_name', media_kind: 'url_only', content_type: null, file_byte_size: null, export_paths: {}, export_items: [], created_at: date, updated_at: date, created_by_user: { id: 1, name: 'SSR' }, updated_by_user: { id: 1, name: 'SSR' } }) const responseFor = url => { const locale = url.searchParams.get ('locale') ?? 'ja' const path = url.pathname if (path === '/locales') return { locales: [{ code: 'ja', name: '日本語' }, { code: 'ain-Kana', name: 'アイヌ' }] } if (path === '/users/session') return { id: 1, name: 'SSR account', role: 'admin' } if (path === '/users/settings') return { theme: 'system', wiki_editor_mode: 'split' } if (path === '/users/theme_slots') return [] if (path === '/posts') return { posts: [post (locale)], count: 1 } if (path === '/gekanator/posts') return { posts: [post (locale)] } if (path === '/gekanator/questions') return { questions: [] } if (path === '/posts/1') return post (locale) if (path === '/tags') return { tags: [tag (locale)], count: 1 } if (path === '/tags/1' || path.startsWith ('/tags/name/')) return tag (locale) if (path === '/tags/1/deerjikists') return { tag: tag (locale), deerjikists: [] } if (path === '/tags/with-depth') return [] if (path === '/tags/nico' || path === '/nico/tags') return { tags: [], count: 0 } if (path.endsWith ('/versions')) return { versions: [], count: 0 } if (path === '/wiki') return [wiki (locale)] if (path === '/wiki/changes') return [] if (path.includes ('autolink')) return [] if (path === '/wiki/1/diff') return { title: 'SSR wiki diff', diff: [] } if (path.startsWith ('/wiki/')) return wiki (locale) if (path === '/materials') return { materials: [material (locale)], count: 1 } if (path === '/materials/1') return material (locale) if (path.includes ('suppressions')) return { suppressions: [] } if (path === '/theatres/1') return { id: 1, name: 'SSR theatre', created_at: date } if (path === '/theatres/1/programmes') return [] return null } before (async () => { api = createServer ((request, response) => { requests.push ({ method: request.method, url: request.url }) const url = new URL (request.url, 'http://localhost') const data = responseFor (url) response.writeHead (failAPI ? 503 : (data == null ? 404 : 200), { 'Content-Type': 'application/json' }) response.end (JSON.stringify (data)) }) await new Promise (resolve => api.listen (0, '127.0.0.1', resolve)) apiURL = `http://127.0.0.1:${api.address ().port}` node = spawn (process.execPath, ['server.mjs'], { env: { ...process.env, NODE_ENV: 'production', PORT: '0', HOST: '127.0.0.1', PUBLIC_ORIGIN: origin, SSR_API_BASE_URL: apiURL, SSR_COOKIE_SECRET: 'automated-check-only-not-a-production-secret' }, stdio: ['ignore', 'pipe', 'pipe'] }) httpOrigin = await new Promise ((resolve, reject) => { node.stdout.on ('data', data => { const match = data.toString ().match (/listening on (\d+)/) if (match) resolve (`http://127.0.0.1:${match[1]}`) }) node.on ('error', reject) node.on ('exit', code => reject (new Error (`SSR server exited ${code}`))) }) vite = await createViteServer ({ server: { middlewareMode: true }, appType: 'custom' }) const { pageRoutes } = await vite.ssrLoadModule ('/src/App.tsx') const flatten = (entries, parent = '') => entries.flatMap (route => { const path = [parent, route.path].filter (Boolean).join ('/') return route.children ? flatten (route.children, path) : [{ ...route, path }] }) routes = flatten (pageRoutes (null, () => {})) }) after (async () => { await vite?.close () node?.kill () api?.closeAllConnections () await new Promise (resolve => api?.close (resolve)) }) test ('every canonical screen route returns page HTML without JavaScript', async t => { for (const route of routes) { for (const locale of ['ja', 'ain-Kana']) { const path = route.path.replace (':identifier', 'home').replace (/:id|:title/g, '1') .replace ('*', 'unknown-screen') const url = `/${locale}/${path}?from=2026-09-15&to=2026-09-15` await t.test (`${locale} ${route.path}`, async () => { const result = await render (url, origin, apiURL, { transferCode: 'test-secret' }) assert.ok (result != null) if (route.handle?.redirectTo) { assert.equal (result.status, 308) return } assert.ok (result.html?.length > 300, 'actual page HTML is required') assert.equal (result.status, route.path === '*' ? 404 : 200) assert.match (result.head, /]*>/g, ''), /SSR[  ]theatre/) if (route.path === 'wiki' || route.path === 'wiki/:title') assert.match (result.html, new RegExp (`SSR wiki body ${locale}|SSR`)) assert.doesNotMatch (result.html, /Switched to client rendering|template id="B:/) }) } } }) test ('parallel localized requests keep list data isolated', async () => { const pages = await Promise.all (['ja', 'ain-Kana', 'ja', 'ain-Kana'].map (locale => render (`/${locale}/posts?limit=50&page=2`, origin, apiURL))) for (const [index, page] of pages.entries ()) { const locale = index % 2 === 0 ? 'ja' : 'ain-Kana' assert.match (page.html, new RegExp (`SSR post ${locale}`)) assert.equal (page.status, 200) const state = JSON.parse (page.bootstrap) assert.equal (state.locale, locale) } assert.ok (requests.some (request => request.url.includes ('page=2'))) assert.ok (requests.every (request => request.method === 'GET')) }) test ('unknown resources and legacy URLs have correct status', async () => { assert.equal ((await render ('/ja/unknown', origin, apiURL)).status, 404) assert.equal ((await render ('/ja/help/unknown', origin, apiURL)).status, 404) assert.equal ((await render ('/ja/posts/999', origin, apiURL)).status, 404) const redirect = await render ('/help/home?edition=2026-09-15', origin, apiURL) assert.equal (redirect.status, 308) assert.equal (redirect.location, '/ja/help/home?edition=2026-09-15') }) test ('HTTP cookie migration, CSRF protection, and API failure', async () => { const rejected = await fetch (`${httpOrigin}/_ssr/session`, { method: 'POST', headers: { Origin: 'https://other.invalid', 'X-Transfer-Code': 'test-secret' } }) assert.equal (rejected.status, 403) const created = await fetch (`${httpOrigin}/_ssr/session`, { method: 'POST', headers: { Origin: origin, 'X-Transfer-Code': 'test-secret' } }) assert.equal (created.status, 204) const cookie = created.headers.get ('set-cookie') assert.match (cookie, /HttpOnly; Secure; SameSite=Lax/) assert.doesNotMatch (cookie, /test-secret/) const settings = await fetch (`${httpOrigin}/ja/users/settings`, { headers: { Cookie: cookie } }) assert.equal (settings.status, 200) const html = await settings.text () assert.match (html, /SSR account/) assert.doesNotMatch (html, /test-secret/) assert.match (settings.headers.get ('cache-control'), /private, no-store/) assert.equal ((await fetch (`${httpOrigin}/ja/users/settings`)).status, 401) const invalid = cookie.replace ('=', '=invalid') assert.equal ((await fetch (`${httpOrigin}/ja/users/settings`, { headers: { Cookie: invalid } })).status, 401) failAPI = true try { assert.equal ((await fetch (`${httpOrigin}/ja/posts`)).status, 503) } finally { failAPI = false } }) test ('hydrate every route with stored browser preferences', { skip: !process.env.SSR_CHROME_PATH, timeout: 180_000, }, async t => { const { default: WebSocket } = await import ('ws') const chrome = spawn (process.env.SSR_CHROME_PATH, [ '--headless', '--no-sandbox', '--disable-gpu', '--disable-dev-shm-usage', '--remote-debugging-port=0', `--user-data-dir=/tmp/btrc-ssr-browser-${process.pid}`], { stdio: ['ignore', 'ignore', 'pipe'] }) let ws try { const endpoint = await new Promise ((resolve, reject) => { let output = '' chrome.stderr.on ('data', data => { output += data const match = output.match (/DevTools listening on (ws:\/\/\S+)/) if (match) resolve (match[1]) }) chrome.on ('error', reject) }) ws = new WebSocket (endpoint) await new Promise (resolve => ws.on ('open', resolve)) const pending = new Map () let sequence = 0, errors = [] const send = (method, params = {}, sessionId) => new Promise ((resolve, reject) => { const id = ++sequence pending.set (id, { resolve, reject }) ws.send (JSON.stringify ({ id, method, params, sessionId })) }) ws.on ('message', raw => { const message = JSON.parse (raw) if (message.id) { const entry = pending.get (message.id) pending.delete (message.id) if (message.error) entry?.reject (message.error) else entry?.resolve (message.result) } else if (message.method === 'Fetch.requestPaused') { const { requestId, request } = message.params const url = new URL (request.url) if (url.origin === httpOrigin && url.pathname !== '/_ssr/session') { void send ('Fetch.continueRequest', { requestId }, message.sessionId) return } const data = url.pathname === '/users/verify' ? { valid: true, user: { id: 1, name: 'SSR account', role: 'admin', inheritance_code: 'test-secret' } } : responseFor (url) void send ('Fetch.fulfillRequest', { requestId, responseCode: request.method === 'OPTIONS' ? 204 : 200, responseHeaders: [{ name: 'Content-Type', value: 'application/json' }, { name: 'Access-Control-Allow-Origin', value: httpOrigin }, { name: 'Access-Control-Allow-Headers', value: '*' }, { name: 'Access-Control-Allow-Methods', value: '*' }], body: Buffer.from (JSON.stringify (data ?? {})).toString ('base64') }, message.sessionId) } else if (message.method === 'Runtime.exceptionThrown') { errors.push (message.params.exceptionDetails.exception?.description ?? message.params.exceptionDetails.text) } else if (message.method === 'Runtime.consoleAPICalled' && message.params.type === 'error') { errors.push (message.params.args.map (arg => arg.value ?? arg.description).join (' ')) } }) const { targetId } = await send ('Target.createTarget', { url: 'about:blank' }) const { sessionId } = await send ('Target.attachToTarget', { targetId, flatten: true }) await send ('Runtime.enable', {}, sessionId) await send ('Page.enable', {}, sessionId) await send ('Network.enable', {}, sessionId) await send ('Fetch.enable', { patterns: [{ urlPattern: '*' }] }, sessionId) const session = await fetch (`${httpOrigin}/_ssr/session`, { method: 'POST', headers: { Origin: origin, 'X-Transfer-Code': 'test-secret' } }) await send ('Network.setExtraHTTPHeaders', { headers: { Cookie: session.headers.get ('set-cookie').split (';')[0] } }, sessionId) await send ('Page.addScriptToEvaluateOnNewDocument', { source: ` localStorage.setItem('user_code','test-secret'); localStorage.setItem('btrc_hub.client_settings',JSON.stringify({ lists:{postList:{limit:50},tagList:{limit:50},postSearch:{limit:50}}, behavior:{animation:'off'},theatre:{layoutMode:'tagsBottom',tagFlow:'horizontal'} })); new MutationObserver(()=>{ const child=document.getElementById('root')?.firstElementChild; if(child&&!window.__initialSSRNode)window.__initialSSRNode=child; }).observe(document,{childList:true,subtree:true});` }, sessionId) const evaluate = async expression => (await send ('Runtime.evaluate', { expression, returnByValue: true }, sessionId)).result.value for (const route of routes.filter (entry => !entry.handle?.redirectTo)) { for (const locale of ['ja', 'ain-Kana']) { await t.test (`hydrate ${locale} ${route.path}`, async () => { const path = route.path.replace (':identifier', 'home').replace (/:id|:title/g, '1') .replace ('*', 'unknown-screen') errors = [] await send ('Page.navigate', { url: `${httpOrigin}/${locale}/${path}?from=2026-09-15&to=2026-09-15` }, sessionId) let hydrated = false for (let attempt = 0; attempt < 100; ++attempt) { await new Promise (resolve => setTimeout (resolve, 75)) try { hydrated = await evaluate (`Object.keys(document.getElementById('root')??{}) .some(key=>key.startsWith('__reactContainer'))`) } catch {} if (hydrated) break } assert.ok (hydrated, 'hydrateRoot must run') await new Promise (resolve => setTimeout (resolve, 150)) assert.deepEqual (errors, []) assert.ok (await evaluate (`document.getElementById('root').firstElementChild ===window.__initialSSRNode`), 'server DOM must be preserved') assert.equal (await evaluate ('document.documentElement.lang'), locale) }) } } } finally { ws?.close () chrome.kill () } })