按审查结论重构:先补测试网,再搬代码
顺序是刻意的。前端一行测试都没有,直接挪代码挪坏了 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:
@@ -0,0 +1,58 @@
|
||||
import { describe, expect, it } from 'bun:test'
|
||||
import { tokenFromRequest, tokenMatches } from './auth.ts'
|
||||
|
||||
const TOKEN = '208ac4512b9748b9b7408ebc16ec793d'
|
||||
|
||||
describe('令牌比较', () => {
|
||||
it('一模一样才算通过', () => {
|
||||
expect(tokenMatches(TOKEN, TOKEN)).toBe(true)
|
||||
})
|
||||
|
||||
it('差一个字符就不通过', () => {
|
||||
expect(tokenMatches(TOKEN, `${TOKEN.slice(0, -1)}x`)).toBe(false)
|
||||
})
|
||||
|
||||
it('前缀正确也不算通过', () => {
|
||||
// 逐字节比较写错时最容易漏掉的一种:只比对了开头
|
||||
expect(tokenMatches(TOKEN, TOKEN.slice(0, 8))).toBe(false)
|
||||
})
|
||||
|
||||
it('更长的字符串不算通过', () => {
|
||||
expect(tokenMatches(TOKEN, `${TOKEN}extra`)).toBe(false)
|
||||
})
|
||||
|
||||
it('没带令牌不算通过', () => {
|
||||
expect(tokenMatches(TOKEN, null)).toBe(false)
|
||||
expect(tokenMatches(TOKEN, '')).toBe(false)
|
||||
})
|
||||
|
||||
it('比较耗时不随匹配长度变化', () => {
|
||||
// 这是常数时间比较存在的理由:如果先匹配的字符越多越慢,就能靠计时逐字节猜出令牌。
|
||||
// 这里只验证行为上的对称——两个都不匹配的输入走的是同样多的比较。
|
||||
const almost = `${TOKEN.slice(0, -1)}x`
|
||||
const nothing = 'x'.repeat(TOKEN.length)
|
||||
expect(tokenMatches(TOKEN, almost)).toBe(false)
|
||||
expect(tokenMatches(TOKEN, nothing)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('从请求里取令牌', () => {
|
||||
const req = (init?: RequestInit, url = 'http://x/api/config') => new Request(url, init)
|
||||
|
||||
it('认 Authorization 头', () => {
|
||||
expect(tokenFromRequest(req({ headers: { Authorization: `Bearer ${TOKEN}` } }))).toBe(TOKEN)
|
||||
})
|
||||
|
||||
it('认查询参数,WebSocket 用得上——它没法自定义请求头', () => {
|
||||
expect(tokenFromRequest(req(undefined, `http://x/api/logs?token=${TOKEN}`))).toBe(TOKEN)
|
||||
})
|
||||
|
||||
it('头的格式不对时当作没带', () => {
|
||||
expect(tokenFromRequest(req({ headers: { Authorization: TOKEN } }))).toBeNull()
|
||||
expect(tokenFromRequest(req({ headers: { Authorization: `Basic ${TOKEN}` } }))).toBeNull()
|
||||
})
|
||||
|
||||
it('什么都没带时返回空', () => {
|
||||
expect(tokenFromRequest(req())).toBeNull()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user