按审查结论重构:先补测试网,再搬代码
顺序是刻意的。前端一行测试都没有,直接挪代码挪坏了 bun test 不会红,所以先把纯逻辑 抽到能被测试的位置并补上用例,最后才动组件。测试从 37 条增加到 77 条。 抽出来的四块: - shared/outbound-chain.ts 「这个端口现在走哪条线」的链路解析,带防环,9 条测试 - web/delay-test.ts 延迟测试失败时说什么,6 个分支原本一条测试都没有 - web/runtime.ts 运行时状态的模块级单例,照 prefs.ts 已有的模式 - web/components/InboundList.vue 入站列表 出站的文本原本散成四份(读一份、显示一份、写回一份、表单又抄一份),加一种出站模式 要同时改四处,漏一个就是显示与写回不一致。合并成 OUTBOUND_PRESETS 一张表。 给认证和透传白名单补了 20 条测试。两者都是安全边界、都是纯函数、都不需要内核在线, 之前却只靠需要活内核的冒烟脚本间接覆盖。其中「形似但不是健康检查的路径」几条确认了 正则没写松。 另外:配置快照不再把整份文件原文下发(前端从不读,白白让响应体积翻倍); yaml-cosmetics 的测试改走生产路径,之前它自己拼了一遍三步流程,落盘链路漏掉其中一步 测试照样会绿。
This commit is contained in:
+34
-9
@@ -33,8 +33,6 @@ const KEEP_BACKUPS = 10
|
||||
export interface ConfigSnapshot {
|
||||
/** 解析出来的配置数据 */
|
||||
data: ConfigObject
|
||||
/** 文件原文 */
|
||||
yaml: string
|
||||
/**
|
||||
* 内容指纹。客户端改完提交时要带上它,服务端据此判断这份改动
|
||||
* 是不是基于最新的文件——中途有人 SSH 上去手改过就会对不上。
|
||||
@@ -56,7 +54,12 @@ export type ApplyFailureStage =
|
||||
| 'write' // 写文件失败
|
||||
| 'reload' // 文件写进去了,但内核没能重新加载
|
||||
|
||||
export async function readConfig(env: Env): Promise<ConfigSnapshot> {
|
||||
/** 内部用的完整读取,带原文。原文只在生成新内容时需要,不下发给前端。 */
|
||||
interface ConfigDocument extends ConfigSnapshot {
|
||||
yaml: string
|
||||
}
|
||||
|
||||
async function readDocument(env: Env): Promise<ConfigDocument> {
|
||||
const yaml = await readFile(env.configPath, 'utf8')
|
||||
const doc = openDocument(yaml)
|
||||
return {
|
||||
@@ -67,6 +70,30 @@ export async function readConfig(env: Env): Promise<ConfigSnapshot> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读当前配置。
|
||||
*
|
||||
* 不含文件原文:前端从来不读它,带上只会让每次请求的响应体积翻倍。要原文的地方
|
||||
* 在这个文件内部,用 readDocument。
|
||||
*/
|
||||
export async function readConfig(env: Env): Promise<ConfigSnapshot> {
|
||||
const { yaml: _yaml, ...snapshot } = await readDocument(env)
|
||||
return snapshot
|
||||
}
|
||||
|
||||
/**
|
||||
* 把新的配置数据写成 YAML 文本。
|
||||
*
|
||||
* 单独导出是为了让测试能走**生产用的这一条**路径。之前测试自己拼了
|
||||
* 「解析 → 替换 → 还原对齐」三步,于是如果有人在落盘链路里漏掉最后那步,
|
||||
* 那些专为守护它而写的测试照样全绿。
|
||||
*/
|
||||
export function rewriteYaml(currentYaml: string, nextData: ConfigObject): string {
|
||||
const rewritten = replaceDocumentData(openDocument(currentYaml), nextData)
|
||||
// 把只是丢了对齐空格的行还原成原样,否则没被改动的段落也会出现在 diff 里,见 ORIGIN.md
|
||||
return restoreInlineCommentAlignment(currentYaml, rewritten.yaml)
|
||||
}
|
||||
|
||||
/**
|
||||
* 把一份新的配置数据落盘。
|
||||
*
|
||||
@@ -78,7 +105,7 @@ export async function applyConfig(
|
||||
nextData: ConfigObject,
|
||||
baseRevision?: string,
|
||||
): Promise<ApplyOutcome> {
|
||||
const current = await readConfig(env)
|
||||
const current = await readDocument(env)
|
||||
|
||||
if (baseRevision !== undefined && baseRevision !== current.revision) {
|
||||
return {
|
||||
@@ -90,14 +117,12 @@ export async function applyConfig(
|
||||
}
|
||||
}
|
||||
|
||||
// 生成新内容。restoreInlineCommentAlignment 把只是丢了对齐空格的行还原成原样,
|
||||
// 否则没被改动的段落也会出现在 diff 里,见 ORIGIN.md。
|
||||
const rewritten = replaceDocumentData(openDocument(current.yaml), nextData)
|
||||
const nextYaml = restoreInlineCommentAlignment(current.yaml, rewritten.yaml)
|
||||
const nextYaml = rewriteYaml(current.yaml, nextData)
|
||||
|
||||
// 内容没变就不要走后面那一串——不必要的备份和重载只会制造噪音。
|
||||
if (nextYaml === current.yaml) {
|
||||
return { ok: true, snapshot: current, reloaded: false, reloadMessage: '内容没有变化' }
|
||||
const { yaml: _yaml, ...snapshot } = current
|
||||
return { ok: true, snapshot, reloaded: false, reloadMessage: '内容没有变化' }
|
||||
}
|
||||
|
||||
const problems = validateDocument(nextData).filter((d) => d.severity === 'error')
|
||||
|
||||
Reference in New Issue
Block a user