From 63b7cd82a6582f936726b06ad0cf321a8fd6fbc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E8=87=AA=E8=BE=BE?= <980324341@qq.com> Date: Sat, 5 Sep 2026 00:22:48 +0800 Subject: [PATCH] =?UTF-8?q?task-215:=20=E4=B8=8B=E8=BD=BD=20URL=20?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=A5=91=E7=BA=A6=EF=BC=88=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=20node=20--test=EF=BC=9A=E7=BB=9D=E5=AF=B9=E7=9B=B4=E9=93=BE?= =?UTF-8?q?=E4=BF=9D=E7=95=99/origin=20=E8=A1=A5=E9=BD=90/schema=20?= =?UTF-8?q?=E5=AE=8C=E6=95=B4/&=E6=8B=BC=E6=8E=A5=20user=5Fid/=E4=BB=85?= =?UTF-8?q?=E5=B8=A6=20user=5Fid=20=E6=97=A0=E7=AD=BE=E5=90=8D=EF=BC=89+?= =?UTF-8?q?=209=20=E6=9D=A1=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend-vue/tests/download-url.test.ts | 78 +++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 frontend-vue/tests/download-url.test.ts diff --git a/frontend-vue/tests/download-url.test.ts b/frontend-vue/tests/download-url.test.ts new file mode 100644 index 00000000..6ec2cf1d --- /dev/null +++ b/frontend-vue/tests/download-url.test.ts @@ -0,0 +1,78 @@ +import { test } from 'node:test' +import assert from 'node:assert/strict' +import { getJavaDownloadUrl } from '../src/shared/api/download-url.ts' + +/** task-215:下载 URL 格式契约(公开直链、绝对 schema、origin 补齐、user_id 附加、query 拼接)。 */ + +function setup(uid = '42', origin = 'http://localhost:5173') { + ;(globalThis as Record).window = { + localStorage: { getItem: (k: string) => (k === 'uid' ? uid : null) }, + location: { origin }, + } +} + +test('绝对 https 直链被保留并追加 user_id', () => { + setup() + const url = getJavaDownloadUrl('https://api.example.com/result/a.xlsx') + assert.ok(url.startsWith('https://api.example.com/result/a.xlsx'), url) + assert.ok(url.includes('?user_id=42'), url) +}) + +test('相对路径在 window 环境补齐 origin(schema 完整)', () => { + setup() + const url = getJavaDownloadUrl('/newApi/download/x') + assert.ok(url.startsWith('http://localhost:5173'), url) + assert.ok(url.includes('user_id=42'), url) +}) + +test('绝对 http 直链不被 origin 包裹', () => { + setup() + const url = getJavaDownloadUrl('http://cdn.example.com/result/b.xlsx') + assert.ok(url.startsWith('http://cdn.example.com/result/b.xlsx'), url) + assert.ok(!url.startsWith('http://localhost:5173'), url) +}) + +test('已带 query 的 URL 用 & 拼接 user_id', () => { + setup() + const url = getJavaDownloadUrl('https://api.example.com/result/c.xlsx?k=1') + assert.ok(url.includes('k=1&user_id=42'), url) + assert.ok(!/\?user_id=42/.test(url), '不应出现第二个 ?') +}) + +test('user_id 为当前 uid 且 URL 编码可解析', () => { + setup('42') + const url = getJavaDownloadUrl('https://api.example.com/result/d.xlsx') + const params = new URL(url).searchParams + assert.equal(params.get('user_id'), '42') +}) + +test('相对产物 query 仅含 user_id(无签名/多余参数)', () => { + setup('42') + const url = getJavaDownloadUrl('/api/result/e.xlsx') + const params = new URL(url).searchParams + const keys = [...params.keys()] + assert.deepEqual(keys, ['user_id'], url) + assert.ok(!url.includes('X-Amz-Signature'), '不应出现签名参数') +}) + +test('不含重复问号', () => { + setup() + const url = getJavaDownloadUrl('https://api.example.com/result/e.xlsx') + const questionMarks = (url.match(/\?/g) ?? []).length + assert.equal(questionMarks, 1, url) +}) + +test('返回 URL 始终带 schema(save_file 兼容)', () => { + setup() + for (const p of ['/a', 'relative', '/newApi/result/f.xlsx']) { + const url = getJavaDownloadUrl(p) + assert.ok(url.startsWith('http://') || url.startsWith('https://'), `${p} -> ${url}`) + } +}) + +test('结果稳定可复现', () => { + setup() + const a = getJavaDownloadUrl('https://api.example.com/result/g.xlsx') + const b = getJavaDownloadUrl('https://api.example.com/result/g.xlsx') + assert.equal(a, b) +})