fix(web,agent): P1 正确性与鉴权加固,P2 抽 fsutil 并补测试(v1.2.8)

This commit is contained in:
2026-09-24 19:03:05 +08:00
parent 6abedbbe42
commit 8f75e99339
42 changed files with 1190 additions and 215 deletions
+69
View File
@@ -0,0 +1,69 @@
import { describe, expect, it } from 'vitest'
import { formatBytes, formatLogLine, formatLogTime, formatRate, formatRelativeTime, formatUptime } from './format'
describe('formatBytes', () => {
it('按 1024 进制换算并按量级保留有效位', () => {
expect(formatBytes(0)).toBe('0 B')
expect(formatBytes(512)).toBe('512 B')
expect(formatBytes(1024)).toBe('1.00 KB')
expect(formatBytes(1536)).toBe('1.50 KB')
expect(formatBytes(20 * 1024)).toBe('20.0 KB')
expect(formatBytes(200 * 1024)).toBe('200 KB')
expect(formatBytes(3 * 1024 ** 3)).toBe('3.00 GB')
})
it('非法输入回落 0 B,不产生 NaN', () => {
expect(formatBytes(Number.NaN)).toBe('0 B')
expect(formatBytes(Number.POSITIVE_INFINITY)).toBe('0 B')
expect(formatBytes(-1)).toBe('0 B')
})
})
describe('formatRate', () => {
it('复用字节格式化并加 /s', () => {
expect(formatRate(0)).toBe('0 B/s')
expect(formatRate(2048)).toBe('2.00 KB/s')
})
})
describe('formatUptime', () => {
it('小时为 0 时只显示分钟', () => {
expect(formatUptime(90)).toBe('1 分钟')
expect(formatUptime(3661)).toBe('1 小时 1 分钟')
})
})
describe('formatRelativeTime', () => {
it('按分钟 / 小时 / 天分档', () => {
const ago = (minutes: number) => new Date(Date.now() - minutes * 60_000).toISOString()
expect(formatRelativeTime(undefined)).toBe('-')
expect(formatRelativeTime(ago(0))).toBe('刚刚')
expect(formatRelativeTime(ago(5))).toBe('5 分钟前')
expect(formatRelativeTime(ago(3 * 60))).toBe('3 小时前')
expect(formatRelativeTime(ago(2 * 24 * 60))).toBe('2 天前')
})
it('无法解析的字符串原样返回,不显示 NaN', () => {
expect(formatRelativeTime('not-a-date')).toBe('not-a-date')
})
})
describe('formatLogTime', () => {
it('三种日志时间格式统一成 YYYY-MM-DD HH:mm:ss', () => {
expect(formatLogTime('2026-09-24 18:30:05')).toBe('2026-09-24 18:30:05')
expect(formatLogTime('2026-09-24T18:30:05Z')).toBe('2026-09-24 18:30:05')
expect(formatLogTime('2026/09/24 18:30:05')).toBe('2026-09-24 18:30:05')
expect(formatLogTime('')).toBe('-')
})
})
describe('formatLogLine', () => {
it('只改写行首时间戳,保留其余内容', () => {
expect(formatLogLine('2026/09/24 18:30:05 [error] boom')).toBe('2026-09-24 18:30:05 [error] boom')
expect(formatLogLine('2026-09-24T18:30:05Z upstream timeout')).toBe('2026-09-24 18:30:05 upstream timeout')
})
it('JSON 日志只替换 time 字段', () => {
expect(formatLogLine('{"time":"2026-09-24T18:30:05Z","level":"info"}')).toBe('{"time":"2026-09-24 18:30:05","level":"info"}')
})
})
+40
View File
@@ -0,0 +1,40 @@
import { describe, expect, it } from 'vitest'
import { httpStatusKind, statusKind, statusLabel } from './status'
describe('statusKind', () => {
it('把后端状态值映射成 StatusKind', () => {
expect(statusKind('ok')).toBe('success')
expect(statusKind('running')).toBe('success')
expect(statusKind('warning')).toBe('warning')
expect(statusKind('error')).toBe('error')
expect(statusKind('stopped')).toBe('disabled')
expect(statusKind('none')).toBe('disabled')
expect(statusKind(undefined)).toBe('unknown')
expect(statusKind('')).toBe('unknown')
})
it('回归:n-tag 的 type 值不是状态值,必须落到 unknown', () => {
// 曾把 serverStartupType() 返回的 'success'/'default' 传给 StatusBadge :value,
// 导致服务端详情弹窗「运行中」也显示灰点。
expect(statusKind('success')).toBe('unknown')
expect(statusKind('default')).toBe('unknown')
})
})
describe('statusLabel', () => {
it('已知值给中文,未知值原样返回', () => {
expect(statusLabel('ok')).toBe('正常')
expect(statusLabel('stopped')).toBe('已停止')
expect(statusLabel('weird')).toBe('weird')
expect(statusLabel()).toBe('未知')
})
})
describe('httpStatusKind', () => {
it('按状态码分段', () => {
expect(httpStatusKind(200)).toBe('success')
expect(httpStatusKind(301)).toBe('unknown')
expect(httpStatusKind(404)).toBe('warning')
expect(httpStatusKind(503)).toBe('error')
})
})