Files
huangzd1997 8803e22f39 feat(version): 桌面端更新面板支持指定版本安装
- 新增公开接口 GET /api/version/list(最近 50 条,字段口径同管理端列表),
  桌面端下拉不再拼 OSS 地址(版本包被删后拼地址会 404)
- 更新面板(登录页 + 首页)加「指定版本更新」选择器:默认选中最新版,
  可选全部已发布版本(含回退),回退/同版给出明确文案与二次确认
- 客户端无需发版:do_update_app(file_url) 本就接受任意版本包直链
2026-09-14 11:05:30 +08:00

87 lines
4.3 KiB
TypeScript

import { test } from 'node:test'
import assert from 'node:assert/strict'
import {
classifyTargetVersion,
describeTargetVersion,
parseVersionList,
} from '../src/shared/utils/version-picker.ts'
/** /api/version/list 响应样例:接口按 created_at DESC 返回(旧版本重传会排最前) */
function listResponse(items: Array<Record<string, unknown>>) {
return { items }
}
test('版本列表解析:同一版本重复上传只保留先出现(最新)的一条记录', () => {
const items = parseVersionList(listResponse([
{ id: 9, version: '4.0.13', file_url: 'https://oss/4.0.13-new.zip', created_at: '2026-09-14 10:00' },
{ id: 5, version: '4.0.14', file_url: 'https://oss/4.0.14.zip', created_at: '2026-09-14 09:00' },
{ id: 2, version: '4.0.13', file_url: 'https://oss/4.0.13-old.zip', created_at: '2026-09-13 10:00' },
]))
assert.equal(items.length, 2)
const reuploaded = items.find((item) => item.version === '4.0.13')
assert.equal(reuploaded?.fileUrl, 'https://oss/4.0.13-new.zip', '同版本应取最新上传的包')
})
test('版本列表解析:无下载地址或空版本号的行被丢弃(选了也装不了)', () => {
const items = parseVersionList(listResponse([
{ id: 1, version: '4.0.14', file_url: 'https://oss/4.0.14.zip', created_at: '2026-09-14 10:36' },
{ id: 2, version: '4.0.13', file_url: '', created_at: '2026-09-13 20:00' },
{ id: 3, version: ' ', file_url: 'https://oss/blank.zip', created_at: '2026-09-13 19:00' },
{ id: 4, version: null, file_url: 'https://oss/null.zip', created_at: '2026-09-13 18:00' },
null,
'not-an-object',
]))
assert.deepEqual(items.map((item) => item.version), ['4.0.14'])
})
test('版本列表解析:按版本号从高到低排序,旧版本重传不会排到最前', () => {
const items = parseVersionList(listResponse([
// 旧版本刚重传:created_at 最新,但版本号最低
{ id: 30, version: '4.0.9', file_url: 'https://oss/4.0.9.zip', created_at: '2026-09-14 11:00' },
{ id: 29, version: '4.0.14', file_url: 'https://oss/4.0.14.zip', created_at: '2026-09-14 10:00' },
{ id: 28, version: '4.0.13', file_url: 'https://oss/4.0.13.zip', created_at: '2026-09-13 20:00' },
]))
assert.deepEqual(items.map((item) => item.version), ['4.0.14', '4.0.13', '4.0.9'])
})
test('版本列表解析:兼容驼峰字段、v 前缀与空/异常响应', () => {
const items = parseVersionList(listResponse([
{ id: 1, version: 'v4.0.14', fileUrl: 'https://oss/4.0.14.zip', createdAt: '2026-09-14 10:36' },
]))
assert.deepEqual(items, [
{ id: 1, version: '4.0.14', fileUrl: 'https://oss/4.0.14.zip', createdAt: '2026-09-14 10:36' },
])
assert.deepEqual(parseVersionList(null), [])
assert.deepEqual(parseVersionList({}), [])
assert.deepEqual(parseVersionList({ items: 'oops' }), [])
})
test('目标版本判定:高于本机=更新,相等=同版,低于本机=回退', () => {
assert.equal(classifyTargetVersion('4.0.12', '4.0.14'), 'newer')
assert.equal(classifyTargetVersion('4.0.14', '4.0.14'), 'same')
assert.equal(classifyTargetVersion('4.0.14', '4.0.13'), 'older')
// 多段补零后仍相等
assert.equal(classifyTargetVersion('4.0.14', '4.0.14.0'), 'same')
})
test('目标版本判定:本机版本未知时返回 unknown,不误判为升级', () => {
assert.equal(classifyTargetVersion('', '4.0.14'), 'unknown')
assert.equal(classifyTargetVersion(undefined as unknown as string, '4.0.14'), 'unknown')
assert.equal(classifyTargetVersion('4.0.14', ''), 'unknown')
})
test('回退文案必须写明低于当前版本(回退是显式操作,不得静默降级)', () => {
const text = describeTargetVersion('older', '4.0.14', '4.0.13')
assert.ok(text.includes('4.0.13'), '文案要带目标版本号')
assert.ok(text.includes('低于当前版本'), '回退必须明确提示低于本机版本')
assert.ok(text.includes('4.0.14'), '文案要带本机版本号')
})
test('同版与更新文案各自说明动作,本机版本未知时如实说明无法比较', () => {
assert.ok(describeTargetVersion('same', '4.0.14', '4.0.14').includes('与当前版本相同'))
const newer = describeTargetVersion('newer', '4.0.12', '4.0.14')
assert.ok(newer.includes('4.0.12') && newer.includes('4.0.14'))
assert.ok(describeTargetVersion('unknown', '', '4.0.14').includes('未读到本机版本'))
})