import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; import { test } from 'node:test'; import vm from 'node:vm'; import ts from 'typescript'; import { assetConfig, resolvePreviewUrl, normalizePreviewImages, } from '../miniprogram/config/assets.ts'; const legacyRoot = 'cloud://cloud1-9gifs7a2756e2c87.636c-cloud1-9gifs7a2756e2c87-1351593184/'; const preview = 'assets/previews/math/number-find.jpg'; const expected = `https://cdn.joeyone.cn/doodle-assets/${preview}`; test('预览图:旧 fileID 和云存储临时链接映射到统一七牛配置', () => { assert.equal(resolvePreviewUrl(`${legacyRoot}${preview}`), expected); assert.equal( resolvePreviewUrl( `https://636c-cloud1-9gifs7a2756e2c87-1351593184.tcb.qcloud.la/${preview}?sign=old&t=123`, ), expected, ); assert.equal(resolvePreviewUrl(expected), expected); const previous = { ...assetConfig }; try { assetConfig.publicDomain = 'https://assets.example.test'; assetConfig.keyPrefix = 'next/'; assert.equal( resolvePreviewUrl(`${legacyRoot}${preview}`), `https://assets.example.test/next/${preview}`, ); } finally { Object.assign(assetConfig, previous); } }); test('预览图:外部地址、本地图片和头像不替换,畸形预览不产生 CDN 请求', () => { for (const raw of [ '/assets/local.png', 'https://elsewhere.test/assets/previews/a.jpg', 'https://636c-cloud1-9gifs7a2756e2c87-1351593184.tcb.qcloud.la.evil.test/assets/previews/a.jpg', `${legacyRoot}user-avatars/u.png`, 'cloud://another-env/assets/previews/a.jpg', '', ]) assert.equal(resolvePreviewUrl(raw), raw); for (const raw of [ `${legacyRoot}assets/previews/../private.jpg`, `${legacyRoot}assets/previews/%2e%2e/private.jpg`, `${legacyRoot}assets/previews/%broken.jpg`, ]) assert.equal(resolvePreviewUrl(raw), ''); assert.equal(resolvePreviewUrl(null), ''); }); test('预览图:嵌套首页分区、收藏及下载记录转换且不修改原数据', () => { const raw = { home: { featured: [{ previewImg: `${legacyRoot}${preview}` }], sections: [{ items: [{ previewImg: `${legacyRoot}${preview}` }] }], }, records: [{ worksheet: { previewImg: `${legacyRoot}${preview}` } }], avatarUrl: `${legacyRoot}user-avatars/u.png`, }; const converted = normalizePreviewImages(raw); assert.equal(converted.home.featured[0].previewImg, expected); assert.equal(converted.home.sections[0].items[0].previewImg, expected); assert.equal(converted.records[0].worksheet.previewImg, expected); assert.equal(raw.home.featured[0].previewImg, `${legacyRoot}${preview}`); assert.equal(converted.avatarUrl, raw.avatarUrl); }); test('首页配置:预拉取、旧 storage 缓存及新后端兜底均写回七牛预览图', async () => { const source = readFileSync( new URL('../miniprogram/app.ts', import.meta.url), 'utf8', ); const code = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020, }, }).outputText; for (const entry of ['prefetch', 'storage', 'backend']) { const old = { home: { featured: [{ previewImg: `${legacyRoot}${preview}` }] }, }; let app; let stored; const sandbox = { exports: {}, getCurrentPages: () => [], require(name) { if (name === './services/site-api') return { fetchPageConfig: async () => old }; if (name === './config/assets') return { normalizePreviewImages }; return {}; }, App(definition) { app = definition; }, wx: { getBackgroundFetchData({ success, fail }) { if (entry === 'prefetch') success({ fetchedData: JSON.stringify({ data: old }) }); else fail(new Error('not available')); }, getStorageSync() { return entry === 'storage' ? old : null; }, setStorageSync(key, value) { assert.equal(key, 'pageConfig'); stored = value; }, }, }; vm.runInNewContext(code, sandbox); const waiting = app.getPageConfig(); await app.loadPageConfig(); assert.equal((await waiting).home.featured[0].previewImg, expected); assert.equal( app.globalData.pageConfig.home.featured[0].previewImg, expected, ); assert.equal(stored.home.featured[0].previewImg, expected); } });