ai-chatbot/tests/routes/retired-storage.test.ts
WuYongJie 1daf08a964 停用旧图片存储并更新主题 Logo
s3.1080p.hk 已永久停用且文件不可恢复。首页查询排除旧源并统一分页统计,历史图册、聊天和画布保留记录并显示失效说明;下载和代理返回 410,停止旧源上传与归档。使用本地黑白 Logo 适配主题。

提供保存原配置后停用旧存储、取消失效精选、替换 Logo 的整理脚本。七牛 10 月 14 日上传核查记录见 docs/retired-storage.md。

验证:7 项旧存储检查、41 项下载和归档回归测试、TypeScript 检查通过。
2026-11-03 13:36:44 +08:00

77 lines
4.3 KiB
TypeScript

import { test, expect } from '@playwright/test';
import { NextRequest, NextResponse } from 'next/server';
import { serverLoader } from '../helpers/canvas-download-server-loader';
import { isRetiredStorageUrl, RETIRED_STORAGE_MESSAGE } from '@/lib/storage/retired-storage';
import { resolveSiteBranding, DEFAULT_SITE_LOGO, DEFAULT_SITE_LOGO_DARK } from '@/lib/site-branding';
const retired = 'https://s3.1080p.hk/jsxiangche/2025/01/image.png';
const current = 'https://scqnycdn.jiansun.vip/uploads/current.png';
test('retired URLs include stored proxy URLs but do not include live storage', () => {
for (const url of [retired, retired.replace('https:', 'http:'), '/api/image-proxy?url=' + encodeURIComponent(retired)]) {
expect(isRetiredStorageUrl(url)).toBe(true);
}
for (const url of [current, '/brand/logo-black.png', 'https://s3.jiansun.vip/gongxiangpan/a.png', 'https://example.com/s3.1080p.hk/a.png']) {
expect(isRetiredStorageUrl(url)).toBe(false);
}
});
test('original image proxy returns permanent failure without redirecting or fetching', async () => {
let fetches = 0;
const route = serverLoader({ 'next/server': { NextResponse }, fetch: () => { fetches++; throw Error('Unexpected network'); } })('app/api/image-proxy/route.ts');
const response = await route.GET(new NextRequest('https://ai.yydd.ai/api/image-proxy?original=1&url=' + encodeURIComponent(retired), {headers:{'sec-fetch-dest':'image'}}));
expect(response.status).toBe(410);
expect(await response.json()).toMatchObject({code:'storage_retired',error:RETIRED_STORAGE_MESSAGE});
expect(fetches).toBe(0);
});
test('byte cache never fetches retired resources', async () => {
let fetches = 0;
const module = serverLoader({ fetch: () => { fetches++; throw Error('Unexpected network'); } })('lib/storage/image-byte-cache.ts');
await expect(module.fetchImageBytes(retired)).rejects.toThrow('HTTP 410');
expect(fetches).toBe(0);
});
test('retired sources cannot enter the archive queue', async () => {
let inserts = 0;
const module = serverLoader({
'@/lib/db': {db:{insert:()=>{inserts++;throw Error('Unexpected insert');}}},
'@/lib/db/schema': {},
})('lib/storage/image-archive-db.ts');
expect(await module.enqueueImageArchiveTask({sourceUrl:retired})).toBeNull();
expect(inserts).toBe(0);
});
test('single downloads reject retired sources before async preparation, while live candidates remain usable', async () => {
const fetched: string[] = [];
const route = serverLoader({
'@/app/(auth)/auth':{auth:async()=>({user:{id:'owner'}})},
'next/server':{NextResponse,after:()=>{}},
'@/lib/storage/canvas-cdn-download':{},
'@/lib/storage/image-download-preparation':{},
'@/lib/canvas/photoshop-safe-download':{
fetchCanvasDownloadAsset:async({file}:any)=>{fetched.push(file.url);return {ok:true,bytes:Buffer.from('original'),filename:'original.png',contentType:'image/png'};},
sanitizeCanvasDownloadFileName:()=> 'original', buildAttachmentContentDisposition:()=> 'attachment',
},
})('app/api/canvas/download-image/route.ts');
const request = (body:unknown) => new NextRequest('https://ai.yydd.ai/api/canvas/download-image',{method:'POST',body:JSON.stringify(body)});
const failure = await route.POST(request({url:retired,preparation:'async',preferDirect:true}));
expect(failure.status).toBe(410);
expect(fetched).toEqual([]);
const success = await route.POST(request({urls:[retired,current]}));
expect(success.status).toBe(200);
expect(fetched).toEqual([current]);
expect(await success.text()).toBe('original');
});
test('ZIP asset preparation reports permanent failure without retrying the source', async () => {
const module = serverLoader({})( 'lib/canvas/photoshop-safe-download.ts');
const result = await module.fetchCanvasDownloadAsset({file:{url:retired},index:0,requestOrigin:'https://ai.yydd.ai',cookie:''});
expect(result).toMatchObject({ok:false,error:RETIRED_STORAGE_MESSAGE});
});
test('branding replaces unavailable storage with theme variants and preserves custom live branding', () => {
expect(resolveSiteBranding({siteLogo:retired,siteLogoDark:retired,favicon:retired})).toEqual({siteLogo:DEFAULT_SITE_LOGO,siteLogoDark:DEFAULT_SITE_LOGO_DARK,favicon:DEFAULT_SITE_LOGO});
expect(resolveSiteBranding({siteLogo:current,siteLogoDark:current}).siteLogo).toBe(current);
});