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 = [] // Rails owns encryption and verification; this upstream fixture tests Node's forwarding contract. const recoveryCode = 'test-recovery-secret' const users = new Map ([ [recoveryCode, { id: 1, name: 'SSR account', role: 'admin' }], ['second-recovery-secret', { id: 2, name: 'Second account', role: 'member' }]]) const sessions = new Map () let sessionSequence = 0 const cookieName = '__Host-btrc-session' const cookieFor = value => `${cookieName}=${value}` const sessionValue = request => request.headers.cookie?.split (';') .map (part => part.trim ()).find (part => part.startsWith (`${cookieName}=`)) ?.slice (cookieName.length + 1) const issueSession = (response, code = null) => { const value = `opaque-rails-session-${++sessionSequence}` sessions.set (value, { code, token: `csrf-${sessionSequence}` }) refreshCookie (response, value) return value } const refreshCookie = (response, value) => response.setHeader ('Set-Cookie', `${cookieFor (value)}; Path=/; Max-Age=630720000; HttpOnly; Secure; SameSite=Lax`) 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/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 [] if (path === '/theatres/1/comments') return [] if (path === '/theatres/1/post_selection_weights') return { tag_penalties: [], lightest_posts: [], heaviest_posts: [] } if (path === '/theatres/1/watching') return { host_flg: false, post_id: null, post_started_at: null, post_elapsed_ms: null, watching_users: [], skip_vote: { votes_count: 0, required_count: 1, watching_users_count: 0, voted: false } } return null } before (async () => { api = createServer (async (request, response) => { requests.push ({ method: request.method, url: request.url, cookie: request.headers.cookie, csrf: request.headers['x-csrf-token'], transferCode: request.headers['x-transfer-code'] }) const url = new URL (request.url, 'http://localhost') response.setHeader ('Content-Type', 'application/json') response.setHeader ('Cache-Control', 'private, no-store') if (failAPI) { response.writeHead (503).end () return } if (url.pathname.startsWith ('/users')) { const value = sessionValue (request) const session = sessions.get (value) const user = users.get (session?.code) if (request.method === 'GET') { if (url.pathname === '/users/csrf') { const id = session != null ? value : issueSession (response) response.end (JSON.stringify ({ token: sessions.get (id).token })) return } if (user == null) { response.writeHead (401).end () return } refreshCookie (response, value) const data = url.pathname === '/users/session' ? user : (url.pathname === '/users/me' ? { ...user, inheritance_code: session.code } : responseFor (url)) response.writeHead (data == null ? 404 : 200) response.end (JSON.stringify (data)) return } if (![origin, httpOrigin].includes (request.headers.origin) || session == null || request.headers['x-csrf-token'] !== session.token) { response.writeHead (403, { 'X-CSRF-Invalid': '1' }).end () return } let body = '' for await (const chunk of request) body += chunk const params = JSON.parse (body || '{}') if (request.method === 'POST' && url.pathname === '/users/verify') { const code = params.restore && user != null ? session.code : params.code const verified = users.get (code) if (verified == null) { response.end (JSON.stringify ({ valid: false })) return } sessions.delete (value) issueSession (response, code) response.end (JSON.stringify ({ valid: true, user: { ...verified, inheritance_code: code } })) return } if (request.method === 'DELETE' && url.pathname === '/users/session') { sessions.delete (value) response.writeHead (204, { 'Set-Cookie': `${cookieName}=; Path=/; Max-Age=0; HttpOnly; Secure; SameSite=Lax` }).end () return } if (request.method === 'POST' && url.pathname === '/users/code/renew' && user != null) { const code = `rotated-recovery-${sessionSequence}` users.delete (session.code) users.set (code, user) sessions.delete (value) issueSession (response, code) response.end (JSON.stringify ({ code })) return } response.writeHead (404).end () return } const data = responseFor (url) response.writeHead (data == null ? 404 : 200) 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 }, 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, () => {})) }) const authenticatedCookie = async (code = recoveryCode) => { const csrf = await fetch (`${httpOrigin}/api/users/csrf`) assert.equal (csrf.status, 200) const { token } = await csrf.json () const verified = await fetch (`${httpOrigin}/api/users/verify`, { method: 'POST', headers: { Origin: origin, 'Content-Type': 'application/json', Cookie: csrf.headers.get ('set-cookie').split (';')[0], 'X-CSRF-Token': token }, body: JSON.stringify ({ code, restore: true }) }) assert.equal (verified.status, 200) assert.equal ((await verified.json ()).valid, true) const cookie = verified.headers.get ('set-cookie') assert.match (cookie, /HttpOnly; Secure; SameSite=Lax/) assert.doesNotMatch (cookie, new RegExp (code)) return cookie.split (';')[0] } 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 => { const cookie = await authenticatedCookie () 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, { cookie }) 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 start = requests.length 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.slice (start).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 forwards Rails cookies, CSRF, expiration and upstream errors', async () => { const cookie = await authenticatedCookie () 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-recovery-secret|opaque-rails-session/) assert.match (settings.headers.get ('cache-control'), /private, no-store/) assert.match (settings.headers.get ('set-cookie'), /Max-Age=630720000/) assert.ok (requests.some (request => request.url === '/users/session' && request.cookie === cookie)) assert.ok (requests.every (request => request.transferCode == null)) 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) const publicPage = await fetch (`${httpOrigin}/ain-Kana/posts`) assert.equal (publicPage.status, 200) assert.match (await publicPage.text (), /SSR post ain-Kana/) assert.equal ((await fetch (`${httpOrigin}/_ssr/session`, { method: 'POST' })).status, 405) const csrf = await fetch (`${httpOrigin}/api/users/csrf`, { headers: { Cookie: cookie } }) const { token } = await csrf.json () const write = (csrfToken, requestOrigin = origin) => fetch ( `${httpOrigin}/api/users/verify`, { method: 'POST', headers: { Cookie: cookie, Origin: requestOrigin, 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken }, body: JSON.stringify ({ code: recoveryCode }) }) assert.equal ((await write ('invalid')).status, 403) assert.equal ((await write (token, 'https://other.invalid')).status, 403) assert.equal ((await fetch (`${httpOrigin}/ja/users/settings`, { headers: { Cookie: cookie } })).status, 200) const renewed = await write (token) assert.equal (renewed.status, 200) const renewedCookie = renewed.headers.get ('set-cookie').split (';')[0] assert.notEqual (renewedCookie, cookie) assert.equal ((await fetch (`${httpOrigin}/ja/users/settings`, { headers: { Cookie: cookie } })).status, 401) const renewedCSRF = await fetch (`${httpOrigin}/api/users/csrf`, { headers: { Cookie: renewedCookie } }) const newToken = (await renewedCSRF.json ()).token const logout = await fetch (`${httpOrigin}/api/users/session`, { method: 'DELETE', headers: { Cookie: renewedCookie, Origin: origin, 'X-CSRF-Token': newToken } }) assert.equal (logout.status, 204) assert.match (logout.headers.get ('set-cookie'), /Max-Age=0/) assert.equal ((await fetch (`${httpOrigin}/ja/users/settings`, { headers: { Cookie: renewedCookie } })).status, 401) failAPI = true try { assert.equal ((await fetch (`${httpOrigin}/ja/posts`)).status, 503) } finally { failAPI = false } }) test ('parallel user and Locale requests never share authentication or query state', async () => { const cookies = await Promise.all ([authenticatedCookie (), authenticatedCookie ('second-recovery-secret')]) const inputs = cookies.flatMap ((cookie, user) => ['ja', 'ain-Kana'].map (locale => ({ cookie, locale, name: user === 0 ? 'SSR account' : 'Second account', other: user === 0 ? 'Second account' : 'SSR account' }))) const pages = await Promise.all (inputs.map (async input => { const response = await fetch (`${httpOrigin}/${input.locale}/users/settings`, { headers: { Cookie: input.cookie } }) assert.equal (response.status, 200) return response.text () })) for (const [index, html] of pages.entries ()) { const input = inputs[index] assert.match (html, new RegExp (input.name)) assert.doesNotMatch (html, new RegExp (input.other)) assert.match (html, new RegExp (`<html lang="${input.locale}"`)) assert.doesNotMatch (html, /recovery-secret|opaque-rails-session|csrf-/) } }) test ('renewed recovery codes invalidate old sessions and forward the new Rails cookie', async () => { const cookie = await authenticatedCookie ('second-recovery-secret') const csrf = await fetch (`${httpOrigin}/api/users/csrf`, { headers: { Cookie: cookie } }) const { token } = await csrf.json () const renewed = await fetch (`${httpOrigin}/api/users/code/renew`, { method: 'POST', headers: { Cookie: cookie, Origin: origin, 'X-CSRF-Token': token } }) assert.equal (renewed.status, 200) const newCookie = renewed.headers.get ('set-cookie').split (';')[0] assert.notEqual (newCookie, cookie) assert.equal ((await fetch (`${httpOrigin}/ja/users/settings`, { headers: { Cookie: cookie } })).status, 401) assert.equal ((await fetch (`${httpOrigin}/ain-Kana/users/settings`, { headers: { Cookie: newCookie } })).status, 200) }) 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) { void send ('Fetch.continueRequest', { requestId }, message.sessionId) return } const data = 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 cookie = await authenticatedCookie () await send ('Network.setExtraHTTPHeaders', { headers: { Cookie: cookie } }, sessionId) await send ('Page.addScriptToEvaluateOnNewDocument', { source: ` localStorage.setItem('user_code','test-recovery-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 () } })