按审查结论重构:先补测试网,再搬代码
顺序是刻意的。前端一行测试都没有,直接挪代码挪坏了 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:
+45
-11
@@ -55,23 +55,59 @@ export interface Inbound {
|
||||
legacyKey: string | null
|
||||
/** 在 listeners 数组里的下标,用于写回。旧写法为 null */
|
||||
listenerIndex: number | null
|
||||
/** 给人看的一句话说明,主要是给默认入站解释它为什么不能改出站 */
|
||||
note: string | null
|
||||
}
|
||||
|
||||
/**
|
||||
* 不用指名道姓的那几种出站,以及它们各自对应配置里的什么值。
|
||||
*
|
||||
* 这张表是「读配置 → 界面 → 写回配置」三者的唯一约定来源。之前它被拆成四份散在
|
||||
* 各处(读一份、显示一份、写回一份、表单里又抄了一份),加一种出站模式就要同时改
|
||||
* 四个地方,漏一个就是「界面显示的」和「实际写进文件的」对不上。
|
||||
*
|
||||
* `field` 为 undefined 表示配置里**不写** proxy 这个字段——「没有这个键」和
|
||||
* 「这个键是空字符串」在 YAML 里是两回事。
|
||||
*/
|
||||
export const OUTBOUND_PRESETS: Array<{
|
||||
mode: OutboundMode
|
||||
field: string | undefined
|
||||
label: string
|
||||
}> = [
|
||||
{ mode: { kind: 'rules' }, field: undefined, label: 'RULE' },
|
||||
{ mode: { kind: 'direct' }, field: 'DIRECT', label: 'DIRECT' },
|
||||
{ mode: { kind: 'global' }, field: 'GLOBAL', label: 'GLOBAL' },
|
||||
]
|
||||
|
||||
function outboundOf(proxy: unknown): OutboundMode {
|
||||
if (typeof proxy !== 'string' || proxy === '') return { kind: 'rules' }
|
||||
if (proxy === 'DIRECT') return { kind: 'direct' }
|
||||
if (proxy === 'GLOBAL') return { kind: 'global' }
|
||||
return { kind: 'named', name: proxy }
|
||||
const preset = OUTBOUND_PRESETS.find((p) => p.field === proxy)
|
||||
return preset ? preset.mode : { kind: 'named', name: proxy }
|
||||
}
|
||||
|
||||
/** 把出站去向写回成配置里 proxy 字段该有的值。返回 undefined 表示不写这个字段。 */
|
||||
export function outboundToProxyField(mode: OutboundMode): string | undefined {
|
||||
if (mode.kind === 'rules') return undefined
|
||||
if (mode.kind === 'direct') return 'DIRECT'
|
||||
if (mode.kind === 'global') return 'GLOBAL'
|
||||
return mode.name
|
||||
if (mode.kind === 'named') return mode.name
|
||||
return OUTBOUND_PRESETS.find((p) => p.mode.kind === mode.kind)?.field
|
||||
}
|
||||
|
||||
/** 出站在界面上的名字。指名道姓的就是它自己的名字,其余用配置里的原词。 */
|
||||
export function outboundLabel(mode: OutboundMode): string {
|
||||
if (mode.kind === 'named') return mode.name
|
||||
return OUTBOUND_PRESETS.find((p) => p.mode.kind === mode.kind)?.label ?? mode.kind
|
||||
}
|
||||
|
||||
/**
|
||||
* 是不是「没有明确指定」那种状态。
|
||||
*
|
||||
* 界面上用来决定要不要弱化显示:RULE 是不写 proxy 字段时的默认结果,不是用户做出的
|
||||
* 选择,跟他明确选了 DIRECT 是两回事,不该长得一样。
|
||||
*/
|
||||
export function isUnsetOutbound(mode: OutboundMode): boolean {
|
||||
return mode.kind === 'rules'
|
||||
}
|
||||
|
||||
/** 入站类型在界面上怎么念。以配置里的原词为准,不另造描述。 */
|
||||
export function inboundTypeLabel(type: string): string {
|
||||
return type === '' ? '—' : type
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,7 +148,6 @@ export function collectInbounds(data: ConfigObject): Inbound[] {
|
||||
isDefault: true,
|
||||
legacyKey: key,
|
||||
listenerIndex: null,
|
||||
note: `由配置文件顶层的 ${key} 声明。这种写法没法单独指定出站,流量一律走分流规则。想让这个端口固定走某个出站,要把它改成命名入站。`,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -131,7 +166,6 @@ export function collectInbounds(data: ConfigObject): Inbound[] {
|
||||
isDefault: false,
|
||||
legacyKey: null,
|
||||
listenerIndex: index,
|
||||
note: null,
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user