Smail/test/frontend/output-paths.test.mjs
smail 606861822b
Some checks are pending
Security Checks / security (push) Waiting to run
保存统一Web域名恢复入口接手基线
2026-09-16 19:17:55 +08:00

108 lines
5.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import test from 'node:test'
import { buildPlan, hostTarget, plugins, targets } from '../../deploy/build-plugins.mjs'
import { packagePlan } from '../../deploy/package.mjs'
const root = fileURLToPath(new URL('../../', import.meta.url))
const read = relative => readFile(path.join(root, relative), 'utf8')
test('跨平台构建输出不得越过项目 out 目录', async () => {
const content = await read('Makefile')
const commands = [...content.matchAll(/cd ([^\s]+) && [^\n]*go build [^\n]* -o ([^\s]+) /g)]
assert.equal(commands.length, 12)
for (const [, directory, output] of commands) {
const relative = path.relative(path.join(root, 'out'), path.resolve(root, directory, output))
assert.ok(relative && !relative.startsWith('..') && !path.isAbsolute(relative), output)
}
})
test('前端构建必须自动同步 Go 嵌入资源', async () => {
const frontend = JSON.parse(await read('frontend/package.json'))
const telegram = JSON.parse(await read('plugins/pmail_telegram_push/package.json'))
assert.equal(frontend.scripts.postbuild, 'node ../deploy/prepare-frontend.mjs')
assert.equal(telegram.scripts['postbuild-only'], 'node ../../deploy/prepare-frontend.mjs telegram')
assert.match(await read('frontend/vite.config.js'), /\.\.\/out\/frontend/)
assert.match(await read('plugins/pmail_telegram_push/vite.config.ts'), /\.\.\/\.\.\/out\/plugins\/telegram-ui/)
})
test('前端入口按需加载 Element Plus,并延迟设置页和写信编辑器', async () => {
const main = await read('frontend/src/main.js')
const mainApp = await read('frontend/src/main-app.js')
const vite = await read('frontend/vite.config.js')
const header = await read('frontend/src/views/components/HomeHeader/HomeHeader.js')
assert.doesNotMatch(main, /app\.use\(ElementPlus\)/)
assert.doesNotMatch(main, /element-plus\/dist\/index\.css/)
assert.match(main, /public-recovery\.js/)
assert.match(mainApp, /element-services\.css/)
assert.match(vite, /manualChunks\(id\)/)
assert.match(header, /defineAsyncComponent/)
assert.match(header, /import\('\.\.\/ConfigSettings\/ConfigSettings\.vue'\)/)
})
test('开发服务器将匿名恢复入口和接口安全转发到后端', async () => {
const vite = await read('frontend/vite.config.js')
assert.match(vite, /"\/public-password-recovery": developmentBackendProxy\(backendUrl, backendOrigin\)/)
assert.match(vite, /"\/rest\/api\/recovery": developmentBackendProxy\(backendUrl, backendOrigin\)/)
assert.doesNotMatch(vite, /publicRecoveryOrigin|mailOrigin/)
})
test('开发启动必须固定运行配置根目录并集中保存日志', async () => {
const content = await read('deploy/start-dev.ps1')
assert.match(content, /\$env:SMAIL_ROOT = \$runtime\r?\n/)
assert.match(content, /\$legacyConfig = Join-Path \$root 'backend\\config'/)
assert.match(content, /Test-Path -LiteralPath \(Join-Path \$legacyConfig \$name\)/)
assert.match(content, /\$workingDirectory = \$runtime/)
for (const name of ['spam_block.exe', 'pmail_telegram_push.exe', 'wechat_push.exe']) assert.ok(content.includes(name))
assert.match(content, /Join-Path \$runtime 'smail\.exe'/)
assert.match(content, /Join-Path \$root 'test-log\\service-launch'/)
assert.match(content, /-WindowStyle Hidden/)
assert.doesNotMatch(content, /SkipBuild/)
assert.match(content, /--dev-disable-admin-reset/)
assert.match(content, /SMAIL_HTTP_PORT_OVERRIDE = '5201'/)
assert.match(content, /SMAIL_HTTP_TLS_MODE_OVERRIDE = 'proxy'/)
assert.match(content, /LocalPort 5201/)
assert.match(content, /@\(80, 443\)/)
assert.match(await read('frontend/.env.development'), /VITE_DEV_BACKEND_URL=http:\/\/127\.0\.0\.1:5201/)
assert.match(await read('frontend/vite.config.js'), /port: 443/)
assert.match(await read('frontend/vite.config.js'), /smail-development-http-redirect/)
assert.match(content, /deploy-runtime\.ps1/)
assert.ok(content.charCodeAt(0) === 0xfeff, 'Windows PowerShell 5 脚本应有 UTF-8 BOM')
})
test('六个平台默认包含三个插件,构建和发布名称与后端目录完全匹配', async () => {
const catalog = await read('backend/plugins/catalog/catalog.go')
for (const target of targets) {
const plan = buildPlan({ target })
assert.equal(plan.length, 3)
assert.deepEqual(plan.map(item => item.id), plugins.map(item => item.id))
for (const item of plan) {
assert.equal(path.dirname(item.output), path.join(root, 'out', 'plugins', target))
assert.ok(catalog.includes(`"${path.basename(item.output)}"`))
}
const files = packagePlan(target)
assert.equal(files.length, 2)
assert.equal(files.filter(file => path.basename(path.dirname(file.target)) === 'plugins').length, 0)
assert.ok(files.every(file => file.target.startsWith(path.join(root, 'out', 'package', target))))
}
assert.ok(buildPlan({ development: true }).every(item => path.dirname(item.output) === path.join(root, 'out', 'plugins')))
assert.throws(() => buildPlan({ target: '../invalid' }))
assert.throws(() => buildPlan({ plugin: 'unknown' }))
assert.throws(() => buildPlan({ development: true, target: targets.find(target => target !== hostTarget()) }))
assert.match(await read('deploy/build.ps1'), /build-plugins\.mjs'\) --dev/)
assert.match(await read('Makefile'), /package: build_server plugin/)
assert.match(await read('.github/workflows/release.yml'), /node deploy\/build-plugins\.mjs --target=/)
})
test('安全工作流必须先构建嵌入资源再执行 Go 测试', async () => {
for (const file of ['.github/workflows/release.yml', '.github/workflows/security.yml']) {
const content = (await read(file)).split(' security:')[1]
assert.ok(content)
const prepare = content.indexOf('yarn build')
const goTest = content.indexOf('node test/run.mjs backend')
assert.ok(prepare >= 0 && goTest > prepare, file)
}
})