gongxiangp2/lib/__tests__/brand-knowledge-preview.test.ts
2026-09-16 11:27:06 +08:00

170 lines
6.3 KiB
TypeScript

/** @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('切片高度不一致')
})
})