// services/api/test/ads-extensions.spec.ts —— 广告图扩展名白名单一致性守护 // (ads-carousel Task 7 质量审查遗留项,随 Task 8 提交)。三处白名单必须恒等: // · uploads/support.ts UPLOAD_CONTENT_TYPES(契约 contentType 白名单); // · uploads/support.ts EXTENSION_WHITELIST(模块私有:filename→归一扩展名); // · ads/ads.service.ts AD_IMAGE_EXTENSIONS(staging/终键扩展名合法集)。 // 漂移场景(守护标的):上传域新增 contentType(如 image/webp)而广告域未跟 // 进——init 会放行 .webp staging 键(extOfFilename 走共享白名单),但 // verifyStaging 的 AD_IMAGE_EXTENSIONS 核验会拒绝,创建/换图在链路中段断裂。 // 纯函数 spec(无 DB/网络):从导出的 UPLOAD_CONTENT_TYPES 派生归一化扩展名 // 集合(jpeg→jpg,同 EXTENSION_WHITELIST 归一关系),断言与 AD_IMAGE_EXTENSIONS // 恒等;EXTENSION_WHITELIST 不可导出,经 extOfFilename 行为面覆盖(每个合法 // 扩展名归一通过、jpeg 双扩展名归一 jpg、白名单外拒绝)。 import { AD_IMAGE_EXTENSIONS } from '../src/ads/ads.service'; import { UPLOAD_CONTENT_TYPES, extOfFilename } from '../src/uploads/support'; /** UPLOAD_CONTENT_TYPES → 归一化扩展名集合(jpeg→jpg;子类型段即扩展名)。 */ function derivedExtensions(): Set { return new Set( UPLOAD_CONTENT_TYPES.map((contentType) => { const sub = contentType.split('/')[1] ?? ''; return sub === 'jpeg' ? 'jpg' : sub; }), ); } describe('AD_IMAGE_EXTENSIONS 与上传域白名单一致性(守护断言)', () => { it('AD_IMAGE_EXTENSIONS === 从 UPLOAD_CONTENT_TYPES 派生的归一化扩展名集合', () => { expect(AD_IMAGE_EXTENSIONS).toEqual(derivedExtensions()); }); it('每个合法扩展名经 extOfFilename 归一通过(staging 键后缀恒落在合法集内)', () => { for (const ext of derivedExtensions()) { expect(extOfFilename(`banner.${ext}`)).toBe(ext); } // jpeg 双扩展名归一 jpg(EXTENSION_WHITELIST 映射,大小写不敏感)。 expect(extOfFilename('banner.JPEG')).toBe('jpg'); // 白名单外/无扩展名 → null(init 与 verifyStaging 均拒绝)。 expect(extOfFilename('banner.gif')).toBeNull(); expect(extOfFilename('banner')).toBeNull(); }); });