WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s

This commit is contained in:
yawan
2026-09-22 16:19:06 +08:00
commit f7fc992f57
3929 changed files with 1006607 additions and 0 deletions
+121
View File
@@ -0,0 +1,121 @@
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 test = require('node:test')
const { setTimeout: delay } = require('node:timers/promises')
const frontend = path.resolve(__dirname, '../frontend')
const dockerContext = process.env.NGINX_TEST_DOCKER_CONTEXT
const docker = (...args) => execFileSync('docker', [
...(dockerContext ? ['--context', dockerContext] : []), ...args,
], { encoding: 'utf8', timeout: 30000, stdio: ['ignore', 'pipe', 'pipe'] }).trim()
// Runs only a disposable nginx with static fixtures, never the application stack.
test('nginx serves runtime config and versioned assets with distinct cache policies', { timeout: 90000 }, async t => {
const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'weknora-cache-test-'))
const container = `weknora-cache-test-${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 {
const site = path.join(temp, 'html')
const fixtures = {
'index.html': '<html>main cache fixture</html>',
'embed.html': '<html>embed cache fixture</html>',
'config.js': 'window.__RUNTIME_CONFIG__ = { MAX_FILE_SIZE_MB: 50 };',
'tdesign-icons/0.4.1/fonts/index.js': 'window.cacheTestIcon = true;',
'assets/cache-test-012345.js': 'window.cacheTestAsset = true;',
}
for (const [name, body] of Object.entries(fixtures)) {
const filename = path.join(site, name)
fs.mkdirSync(path.dirname(filename), { recursive: true })
fs.writeFileSync(filename, body)
}
const replacements = { APP_SCHEME: 'http', APP_HOST: '127.0.0.1', APP_PORT: '9', MAX_FILE_SIZE: '50M', MAX_SKILL_BUNDLE_SIZE: '256M' }
const config = fs.readFileSync(path.join(frontend, 'nginx.conf'), 'utf8')
.replace(/\$\{(APP_SCHEME|APP_HOST|APP_PORT|MAX_FILE_SIZE|MAX_SKILL_BUNDLE_SIZE)\}/g, (_, key) => replacements[key])
fs.writeFileSync(path.join(temp, 'default.conf'), config)
docker('create', '--rm', '--pull', 'never', '--name', container,
'--publish', '127.0.0.1::80',
'--entrypoint', 'nginx', image, '-g', 'daemon off;')
created = true
// Copying also supports remote Docker contexts without host-directory mounts.
docker('cp', `${site}/.`, `${container}:/usr/share/nginx/html`)
docker('cp', path.join(temp, 'default.conf'), `${container}:/etc/nginx/conf.d/default.conf`)
docker('cp', path.join(frontend, 'nginx-api-proxy.conf'), `${container}:/etc/nginx/api-proxy.conf`)
docker('start', container)
docker('exec', container, 'nginx', '-t')
const address = docker('port', container, '80/tcp').split('\n')[0]
assert.match(address, /^127\.0\.0\.1:\d+$/)
const base = `http://${address}`
const request = async (url, options) => {
const response = await fetch(`${base}${url}`, { ...options, signal: AbortSignal.timeout(3000) })
return { status: response.status, headers: response.headers, body: await response.text() }
}
for (let attempt = 0; ; attempt += 1) {
try { await request('/login'); break } catch (error) {
if (attempt >= 19) throw error
await delay(250)
}
}
await t.test('runtime config is never stored, including conditional responses', async () => {
const response = await request('/config.js?deployment=next')
assert.equal(response.status, 200)
assert.equal(response.body, fixtures['config.js'])
assert.equal(response.headers.get('cache-control'), 'no-store')
assert.equal(response.headers.get('x-content-type-options'), 'nosniff')
assert.ok(response.headers.get('etag'))
const conditional = await request('/config.js', { headers: { 'If-None-Match': response.headers.get('etag') } })
assert.equal(conditional.status, 304)
assert.equal(conditional.headers.get('cache-control'), 'no-store')
})
await t.test('versioned icons and hashed build assets retain immutable caching', async () => {
for (const name of ['tdesign-icons/0.4.1/fonts/index.js', 'assets/cache-test-012345.js']) {
const response = await request(`/${name}`)
assert.equal(response.status, 200)
assert.equal(response.body, fixtures[name])
assert.equal(response.headers.get('cache-control'), 'public, max-age=31536000, immutable')
assert.equal(response.headers.get('x-content-type-options'), 'nosniff')
assert.ok(response.headers.get('etag'))
const conditional = await request(`/${name}`, { headers: { 'If-None-Match': response.headers.get('etag') } })
assert.equal(conditional.status, 304)
assert.equal(conditional.headers.get('cache-control'), 'public, max-age=31536000, immutable')
}
})
await t.test('missing versioned assets return an uncacheable 404, never the SPA', async () => {
for (const url of ['/tdesign-icons/0.4.1/fonts/missing.js', '/tdesign-icons/99.0.0/fonts/index.js', '/assets/missing.js']) {
const response = await request(url)
assert.equal(response.status, 404)
assert.equal(response.headers.get('cache-control'), 'no-store')
assert.ok(!response.body.includes('main cache fixture'))
}
})
await t.test('SPA and embed entry points still require revalidation', async () => {
for (const [url, fixture] of [['/login', 'index.html'], ['/embed/cache-test', 'embed.html']]) {
const response = await request(url)
assert.equal(response.status, 200)
assert.equal(response.body, fixtures[fixture])
assert.equal(response.headers.get('cache-control'), 'no-cache, must-revalidate')
}
})
await t.test('missing runtime configuration is an uncacheable 404', async () => {
docker('exec', container, 'rm', '/usr/share/nginx/html/config.js')
const response = await request('/config.js')
assert.equal(response.status, 404)
assert.equal(response.headers.get('cache-control'), 'no-store')
assert.ok(!response.body.includes('main cache fixture'))
})
} finally {
try {
if (created) docker('rm', '--force', container)
} finally {
fs.rmSync(temp, { recursive: true, force: true })
}
}
})