Files
doodle-mini/tests/preview-assets.test.mjs
T
2026-10-19 17:49:19 +08:00

137 lines
5.2 KiB
JavaScript

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/api')
return {
getApiConfig: () => ({ version: 'release' }),
apiContextKey: () => 'test-production',
isCurrentApiConfig: () => true,
};
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, 'doodle_page_config_v2:test-production');
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);
}
});