Files
crawler-plugin/frontend-vue/tests/secret-setup-gate-consistency.test.ts
T
huangzd1997 8cab9d4bad fix(密钥配置): 保存沿用刚检测过的输入值结果,消除「三项检测通过却提示未检测」死循环
- 服务端:对「输入值(未保存)」的检测结果按 uid+模块+值指纹暂存 Redis(TTL 30min,
  Redis 异常降级为需重新检测,不阻断保存);保存同一个值时落库该结果
  (passed/failed/error 一并沿用),改过值或从未检测则维持未检测
- 前端:保存后清理本地「输入值(未保存)」绿字,展示统一走服务端快照,避免展示与门禁矛盾;
  门禁提示改列「模块名(掩码):未检测 / 检测失败:原因 / 未配置」,同名掩码也能分辨模块
- 测试:UserApiSecretServiceTest 补沿用 / 值不一致 / Redis 降级用例;
  新增前后端一致性守卫测试(保存后必须清检测结果、提示必须带模块名)
2026-09-14 13:49:25 +08:00

60 lines
2.7 KiB
TypeScript

import { test } from 'node:test'
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import { fileURLToPath } from 'node:url'
import { resolve, dirname } from 'node:path'
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..')
const read = (rel: string) => readFileSync(resolve(repoRoot, rel), 'utf-8')
/** 截取函数体:先按括号配平跳过参数列表,再按花括号配平取函数体(模板字面量里的 ${} 也是配对的,不影响计数)。 */
function functionBody(source: string, signature: string): string {
const start = source.indexOf(signature)
assert.ok(start >= 0, `未找到 ${signature}`)
const openParen = source.indexOf('(', start)
let parenDepth = 0
let cursor = openParen
for (; cursor < source.length; cursor += 1) {
if (source[cursor] === '(') parenDepth += 1
else if (source[cursor] === ')') {
parenDepth -= 1
if (parenDepth === 0) break
}
}
const openBrace = source.indexOf('{', cursor)
let depth = 0
for (let i = openBrace; i < source.length; i += 1) {
if (source[i] === '{') depth += 1
else if (source[i] === '}') {
depth -= 1
if (depth === 0) return source.slice(openBrace, i + 1)
}
}
throw new Error('函数体未闭合')
}
/**
* 密钥卡片保存后必须清掉本地「检测输入值(未保存)」的结果:
* 保存后输入框已清空,那份绿字不再代表当前配置,继续展示就会出现
* 「三项都显示检测通过、页面却提示尚未通过检测」的自相矛盾状态(2026-09-14 修复)。
*/
test('密钥面板保存后清理过期检测结果', () => {
const body = functionBody(read('src/shared/components/ApiSecretSettingsPanel.vue'), 'async function saveAll(')
const saveIndex = body.indexOf('await saveApiSecret(')
assert.ok(saveIndex >= 0, 'saveAll 应逐个保存模块输入值')
const afterSave = body.slice(saveIndex)
assert.match(afterSave, /state\.input = ''/, '保存后应清空输入框')
assert.match(afterSave, /state\.result = null/, '保存后应清掉本地检测结果')
assert.match(afterSave, /state\.lastCheckSource = ''/, '保存后应重置检测来源标记')
})
/**
* 门禁拦截提示必须带模块名:同一个密钥填进多个模块时,
* 只列脱敏值会出现「sk-P****9BgQ、sk-P****9BgQ」这种分不清是哪个模块的提示。
*/
test('密钥门禁提示按模块名列出未通过项', () => {
const body = functionBody(read('src/pages/setup/DesktopSecretSetupPage.vue'), 'async function submit(')
assert.match(body, /module\.moduleLabel/, '未通过提示应包含模块名')
assert.match(body, /snapshot\.masked/, '未通过提示应包含脱敏值')
})