Files
xinmi/scripts/frontend_runtime_config.test.cjs
T

63 lines
3.8 KiB
JavaScript
Raw Normal View History

const assert = require('node:assert/strict')
const { execFileSync } = require('node:child_process')
const fs = require('node:fs')
const os = require('node:os')
const path = require('node:path')
const vm = require('node:vm')
const test = require('node:test')
const frontend = path.resolve(__dirname, '../frontend')
const docker = (...args) => execFileSync('docker', [
...(process.env.NGINX_TEST_DOCKER_CONTEXT ? ['--context', process.env.NGINX_TEST_DOCKER_CONTEXT] : []), ...args,
], { encoding: 'utf8', timeout: 30000, stdio: ['ignore', 'pipe', 'pipe'] }).trim()
test('startup embeds current validated config in both entry points on every run', { timeout: 90000 }, async () => {
const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'weknora-runtime-config-'))
const name = `weknora-runtime-config-${process.pid}-${Date.now()}`
const image = process.env.NGINX_TEST_IMAGE || fs.readFileSync(path.join(frontend, 'Dockerfile'), 'utf8').match(/^FROM (\S+)/m)[1]
let created = false
try {
// The stand-in nginx exits, so the real startup script can be run repeatedly.
fs.writeFileSync(path.join(temp, 'nginx'), '#!/bin/sh\nexit 0\n', { mode: 0o755 })
docker('create', '--rm', '--pull', 'never', '--network', 'none', '--name', name,
'--entrypoint', 'sh', image, '-c', 'while :; do sleep 30; done')
created = true
docker('start', name)
docker('exec', name, 'mkdir', '-p', '/usr/share/nginx/runtime-templates', '/etc/nginx/templates', '/fixture-bin')
for (const page of ['index.html', 'embed.html']) docker('cp', path.join(frontend, page), `${name}:/usr/share/nginx/runtime-templates/${page}`)
docker('cp', path.join(frontend, 'nginx.conf'), `${name}:/etc/nginx/templates/default.conf.template`)
docker('cp', path.join(frontend, 'docker-entrypoint.sh'), `${name}:/fixture-entrypoint.sh`)
docker('cp', path.join(temp, 'nginx'), `${name}:/fixture-bin/nginx`)
const run = env => docker('exec', '-e', 'PATH=/fixture-bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin',
...Object.entries(env).flatMap(([key, value]) => ['-e', `${key}=${value}`]), name, 'sh', '/fixture-entrypoint.sh')
const readConfig = (source) => {
const matches = [...source.matchAll(/<script data-runtime-config>([\s\S]*?)<\/script>/g)]
assert.equal(matches.length, 1)
assert.ok(!source.includes('src="/config.js"'))
const context = { window: {} }
vm.runInNewContext(matches[0][1], context, { timeout: 500 })
return JSON.parse(JSON.stringify(context.window.__RUNTIME_CONFIG__))
}
for (const [env, expected] of [
[{ DEFAULT_LOCALE: 'zh-CN', MAX_FILE_SIZE_MB: '50', MAX_SKILL_BUNDLE_SIZE_MB: '300' },
{ DEFAULT_LOCALE: 'zh-CN', MAX_FILE_SIZE_MB: 50, MAX_SKILL_BUNDLE_SIZE_MB: 300 }],
[{ DEFAULT_LOCALE: 'en-US', MAX_FILE_SIZE_MB: '99999', MAX_SKILL_BUNDLE_SIZE_MB: '600' },
{ DEFAULT_LOCALE: 'en-US', MAX_FILE_SIZE_MB: 20480, MAX_SKILL_BUNDLE_SIZE_MB: 512 }],
[{ DEFAULT_LOCALE: '</script><script>alert(1)</script>', MAX_FILE_SIZE_MB: 'invalid', MAX_SKILL_BUNDLE_SIZE_MB: '1;alert(1)' },
{ DEFAULT_LOCALE: '', MAX_FILE_SIZE_MB: 50, MAX_SKILL_BUNDLE_SIZE_MB: 256 }],
]) {
run(env)
for (const page of ['index.html', 'embed.html']) {
const body = docker('exec', name, 'cat', `/usr/share/nginx/html/${page}`)
assert.deepEqual(readConfig(body), expected)
assert.ok(body.includes('type="module"'))
}
const compatibility = { window: {} }
vm.runInNewContext(docker('exec', name, 'cat', '/usr/share/nginx/html/config.js'), compatibility, { timeout: 500 })
assert.deepEqual(JSON.parse(JSON.stringify(compatibility.window.__RUNTIME_CONFIG__)), expected)
}
} finally {
try { if (created) docker('rm', '--force', name) } finally { fs.rmSync(temp, { recursive: true, force: true }) }
}
})