Smail/test/frontend/setup-ssl-proxy.test.mjs
smail 4d3d46c96e
Some checks are pending
Security Checks / security (push) Waiting to run
修复部署与反代初始化回归测试
2026-10-03 05:02:42 +08:00

125 lines
5.0 KiB
JavaScript

import assert from 'node:assert/strict'
import test from 'node:test'
import { readFileSync } from 'node:fs'
import { compileFunction } from 'node:vm'
import { createRequire } from 'node:module'
import { computed, reactive, ref } from './vue.mjs'
const root = new URL('../../frontend/src/views/Setup/', import.meta.url)
const source = readFileSync(new URL('Setup.js', root), 'utf8')
const createPage = compileFunction(
source.replace(/^import .*$/gm, '').replaceAll('import.meta.env.DEV', 'false')
.replace('export function usePage', 'function usePage') + '\nreturn usePage()',
['computed', 'onUnmounted', 'reactive', 'ref', 'ElMessage', 'lang', 'axios', 'Plus', 'http', 'buildSetupFinalURL', 'probeService', 'window']
)
function fixture(tlsMode, type = '2') {
const requests = []
const messages = []
let scheduledTimer = null
const window = {
location: { href: 'https://mail.example.test/setup' },
setTimeout: callback => {
scheduledTimer = callback
return 1
},
clearTimeout: () => {
scheduledTimer = null
}
}
const page = createPage(computed, () => {}, reactive, ref,
{ error: message => messages.push(message) }, {}, {}, {}, {
post: async (path, payload) => {
requests.push({ path, payload })
return { errorNo: 0, data: { tlsMode, type, port: 8080 } }
}
}, () => 'https://mail.example.test/', () => true, window)
return {
page,
requests,
messages,
window,
runScheduledTimer: async () => {
scheduledTimer = null
await page.checkStatus()
}
}
}
test('反代初始化强制导入外部证书,即使旧配置仍是自动 DNS 类型', async () => {
const { page, requests, messages, window, runScheduledTimer } = fixture('proxy')
await page.getSSLConfig()
assert.equal(page.proxyHTTPS.value, true)
assert.equal(page.sslSettings.type, '1')
page.sslSettings.type = '0'
page.handleSSLTypeChange()
assert.equal(page.sslSettings.type, '1')
await page.handleSSLChallengeChange('dns')
assert.equal(requests.length, 1)
assert.equal(page.dnsChecking.value, false)
// 绕过控件强行修改内存也不能让提交回到自动签发分支。
page.sslSettings.type = '0'
page.sslSettings.challenge = 'dns'
page.sslSettings.key_path = '/external/key.pem'
page.sslSettings.crt_path = '/external/cert.pem'
await page.setSSLConfig()
assert.deepEqual(requests.at(-1), {
path: '/api/v1/setup', payload: {
action: 'set', step: 'ssl', ssl_type: '1',
key_path: '/external/key.pem', crt_path: '/external/cert.pem'
}
})
assert.equal(requests.length, 2)
await runScheduledTimer()
assert.equal(window.location.href, 'https://mail.example.test/')
assert.deepEqual(messages, [])
})
test('直接 HTTPS 初始化保留自动 HTTP 和 DNS 选项', async () => {
for (const type of ['0', '2']) {
const { page, messages } = fixture('direct', type)
await page.getSSLConfig()
assert.equal(page.proxyHTTPS.value, false)
assert.equal(page.sslSettings.type, '0')
assert.equal(page.sslSettings.challenge, type === '2' ? 'dns' : 'http')
assert.deepEqual(messages, [])
}
})
test('初始化模板能编译,实际渲染按反代策略隐藏自动选项并保留导入路径', async () => {
const require = createRequire(new URL('../../frontend/package.json', import.meta.url))
const { parse, compileScript } = require('@vue/compiler-sfc')
const { createSSRApp, compile, h } = require('vue')
const { renderToString } = require('vue/server-renderer')
const { descriptor, errors } = parse(readFileSync(new URL('Setup.vue', root), 'utf8'))
assert.deepEqual(errors, [])
assert.doesNotThrow(() => compileScript(descriptor, { id: 'setup-proxy', inlineTemplate: true }))
const render = compile(descriptor.template.content)
for (const tlsMode of ['proxy', 'direct']) {
const { page } = fixture(tlsMode, '0')
await page.getSSLConfig()
page.active.value = 5
page.lang.ssl_auto = '自动申请证书'
page.lang.ssl_manuallyf = '导入外部证书'
const app = createSSRApp({ setup: () => page, render })
for (const name of new Set(descriptor.template.content.match(/\bel-[a-z-]+/g))) {
app.component(name, {
props: ['label', 'value'],
setup: (props, { slots }) => () => h('div', { 'data-label': props.label, 'data-value': props.value }, slots.default?.())
})
}
app.directive('loading', { getSSRProps: () => ({}) })
const html = await renderToString(app)
assert.match(html, /data-label="导入外部证书"/)
if (tlsMode === 'proxy') {
assert.doesNotMatch(html, /data-label="自动申请证书"|ssl-challenge-select-row|ssl-storage-card|ssl-port-warning/)
assert.match(html, /外部续期后也需重新导入并应用/)
assert.match(html, /modelvalue="\.\/data\/ssl\/private\.key"/)
assert.match(html, /modelvalue="\.\/data\/ssl\/public\.crt"/)
} else {
assert.match(html, /data-label="自动申请证书"/)
assert.match(html, /ssl-challenge-select-row|ssl-storage-card/)
}
}
})