// services/api/test/ads-extensions.spec.ts —— 广告图扩展名白名单守护 // (ads-carousel Task 7 质量审查遗留项,随 Task 8 提交;终审 Fix 3 改为 // 广告域自洽守护)。守护标的: // · AD_IMAGE_EXTENSIONS(ads.service,staging/终键扩展名合法集)必须恒等 // 于从 AD_IMAGE_CONTENT_TYPES(ads/dto,契约 contentType 白名单)派生 // 的归一扩展名集合(jpeg→jpg)——contentType 与键扩展名两端口永不漂移; // · heic 剔除是永久约束:广告无变体流水线、原图直出,Chromium/Firefox // 桌面浏览器不渲染 heic(=破图)。 // 不再对照片域 UPLOAD_CONTENT_TYPES/EXTENSION_WHITELIST 做一致性断言—— // 两域白名单自此分离(照片直传保留 heic,变体流水线兜底解码)。共享的 // uploads/support.extOfFilename 仍放行 .heic 文件名(照片域依赖),广告 // init 以 AD_IMAGE_EXTENSIONS 二次核验拒绝(见 ads.service.initUpload)。 // 纯函数 spec(无 DB/网络)。 import { AD_IMAGE_EXTENSIONS } from '../src/ads/ads.service'; import { AD_IMAGE_CONTENT_TYPES } from '../src/ads/dto'; import { extOfFilename } from '../src/uploads/support'; /** AD_IMAGE_CONTENT_TYPES → 归一化扩展名集合(jpeg→jpg;子类型段即扩展名)。 */ function derivedExtensions(): Set { return new Set( AD_IMAGE_CONTENT_TYPES.map((contentType) => { const sub = contentType.split('/')[1] ?? ''; return sub === 'jpeg' ? 'jpg' : sub; }), ); } describe('AD_IMAGE_EXTENSIONS ↔ AD_IMAGE_CONTENT_TYPES 一致性(守护断言)', () => { it('AD_IMAGE_EXTENSIONS === 从 AD_IMAGE_CONTENT_TYPES 派生的归一化扩展名集合', () => { expect(AD_IMAGE_EXTENSIONS).toEqual(derivedExtensions()); }); it('heic 已永久剔除:contentType 集与扩展名集均不含 heic', () => { expect(AD_IMAGE_CONTENT_TYPES).not.toContain('image/heic'); expect(AD_IMAGE_EXTENSIONS.has('heic')).toBe(false); }); it('每个合法扩展名经 extOfFilename 归一通过(staging 键后缀恒落在合法集内)', () => { for (const ext of derivedExtensions()) { expect(extOfFilename(`banner.${ext}`)).toBe(ext); } // jpeg 双扩展名归一 jpg(共享 EXTENSION_WHITELIST 映射,大小写不敏感)。 expect(extOfFilename('banner.JPEG')).toBe('jpg'); // 共享 extOfFilename 仍放行 .heic(照片域白名单,两域分离后各自演进); // 广告 init 的拒绝点在服务层 AD_IMAGE_EXTENSIONS 二次核验,非此处。 expect(extOfFilename('banner.heic')).toBe('heic'); // 白名单外/无扩展名 → null(init 与 verifyStaging 均拒绝)。 expect(extOfFilename('banner.gif')).toBeNull(); expect(extOfFilename('banner')).toBeNull(); }); });