/** @jest-environment node */ import sharp from 'sharp' import { assertBrandKnowledgePreviewMetadata, prepareBrandKnowledgePreview, validateBrandKnowledgeUploadedPreviewSlices, } from '@/lib/brand-knowledge-preview' describe('brand knowledge long preview processing', () => { test('keeps previews at or below 3000px as one image and slices above the boundary', async () => { const exactBoundary = await sharp({ create: { width: 800, height: 3_000, channels: 3, background: { r: 238, g: 241, b: 244 }, }, }).png().toBuffer() const overBoundary = await sharp({ create: { width: 800, height: 3_001, channels: 3, background: { r: 238, g: 241, b: 244 }, }, }).png().toBuffer() const exactResult = await prepareBrandKnowledgePreview(exactBoundary, 'image/png') const overResult = await prepareBrandKnowledgePreview(overBoundary, 'image/png') await expect(sharp(exactResult.slices[0]).metadata()).resolves.toMatchObject({ format: 'webp', width: 800, height: 3_000, }) expect(exactResult.slices).toHaveLength(1) expect(overResult.slices).toHaveLength(3) }) test('still slices images above 3000px when width scaling makes the output shorter than 1500px', async () => { const wideOverBoundary = await sharp({ create: { width: 4_000, height: 3_001, channels: 3, background: { r: 238, g: 241, b: 244 }, }, }).png().toBuffer() const result = await prepareBrandKnowledgePreview(wideOverBoundary, 'image/png') const metadata = await Promise.all(result.slices.map((slice) => sharp(slice).metadata())) expect(result.slices).toHaveLength(2) expect(metadata.map((item) => [item.width, item.height])).toEqual([ [1_600, 600], [1_600, 600], ]) }) test('slices a long image into ordered WebP segments suitable for seamless rendering', async () => { const source = await sharp({ create: { width: 800, height: 4_100, channels: 3, background: { r: 238, g: 241, b: 244 }, }, }).png().toBuffer() const result = await prepareBrandKnowledgePreview(source, 'image/png') const metadata = await Promise.all(result.slices.map((slice) => sharp(slice).metadata())) expect(result.source).toMatchObject({ contentType: 'image/png', width: 800, height: 4_100 }) expect(result.slices).toHaveLength(3) expect(metadata.map((item) => item.height)).toEqual([1_500, 1_500, 1_100]) expect(metadata.every((item) => item.format === 'webp' && item.width === 800)).toBe(true) }) test('rejects MIME mismatches and animated previews', async () => { const png = await sharp({ create: { width: 20, height: 20, channels: 3, background: '#fff' }, }).png().toBuffer() await expect(prepareBrandKnowledgePreview(png, 'image/jpeg')).rejects.toThrow('内容与声明格式不一致') expect(() => assertBrandKnowledgePreviewMetadata({ format: 'webp', width: 20, height: 40, pages: 2, }, 'image/webp')).toThrow('不支持动画或多页图片') }) test('enforces the 60 million pixel and long-edge limits before slicing', () => { expect(() => assertBrandKnowledgePreviewMetadata({ format: 'png', width: 10_000, height: 6_001, pages: 1, }, 'image/png')).toThrow('6000 万像素') expect(() => assertBrandKnowledgePreviewMetadata({ format: 'webp', width: 800, height: 90_001, pages: 1, }, 'image/webp')).toThrow('尺寸过大') }) test('validates ordered browser WebP slices before storage', async () => { const first = await sharp({ create: { width: 1_600, height: 1_500, channels: 3, background: '#fff' }, }).webp().toBuffer() const last = await sharp({ create: { width: 1_600, height: 800, channels: 3, background: '#fff' }, }).webp().toBuffer() const result = await validateBrandKnowledgeUploadedPreviewSlices([ { buffer: first, declaredContentType: 'image/webp' }, { buffer: last, declaredContentType: 'image/webp' }, ]) expect(result.slices.map((slice) => [slice.width, slice.height])).toEqual([ [1_600, 1_500], [1_600, 800], ]) }) test('accepts one browser preview up to 3000px without treating it as a sliced image', async () => { const single = await sharp({ create: { width: 1_000, height: 3_000, channels: 3, background: '#fff' }, }).webp().toBuffer() const result = await validateBrandKnowledgeUploadedPreviewSlices([ { buffer: single, declaredContentType: 'image/webp' }, ]) expect(result.slices.map((slice) => [slice.width, slice.height])).toEqual([[1_000, 3_000]]) }) test('accepts a short equal-height pair created after scaling a wide source image', async () => { const first = await sharp({ create: { width: 1_600, height: 600, channels: 3, background: '#fff' }, }).webp().toBuffer() const last = await sharp({ create: { width: 1_600, height: 600, channels: 3, background: '#fff' }, }).webp().toBuffer() const result = await validateBrandKnowledgeUploadedPreviewSlices([ { buffer: first, declaredContentType: 'image/webp' }, { buffer: last, declaredContentType: 'image/webp' }, ]) expect(result.slices.map((slice) => [slice.width, slice.height])).toEqual([ [1_600, 600], [1_600, 600], ]) }) test('rejects forged or geometrically inconsistent browser slices', async () => { await expect(validateBrandKnowledgeUploadedPreviewSlices([ { buffer: Buffer.from('not a webp'), declaredContentType: 'image/webp' }, ])).rejects.toThrow('无法解码') const shortFirst = await sharp({ create: { width: 1_600, height: 1_000, channels: 3, background: '#fff' }, }).webp().toBuffer() const last = await sharp({ create: { width: 1_600, height: 800, channels: 3, background: '#fff' }, }).webp().toBuffer() await expect(validateBrandKnowledgeUploadedPreviewSlices([ { buffer: shortFirst, declaredContentType: 'image/webp' }, { buffer: last, declaredContentType: 'image/webp' }, ])).rejects.toThrow('切片高度不一致') await expect(validateBrandKnowledgeUploadedPreviewSlices([ { buffer: shortFirst, declaredContentType: 'image/webp' }, { buffer: shortFirst, declaredContentType: 'image/webp' }, ])).rejects.toThrow('切片高度不一致') }) })