50 lines
4.6 KiB
TypeScript
50 lines
4.6 KiB
TypeScript
/** @jest-environment node */
|
|
import sharp from 'sharp'
|
|
import { createMultiReferenceChallenge, multiReferenceGrid, multiReferenceImages, multiReferencePrompt, readMultiReferenceChallenge, validateMultiReferenceOutput } from '@/lib/ai-model-multireference'
|
|
import { verifiedReferenceImageCount } from '@/lib/ai-model-verification-state'
|
|
|
|
jest.mock('next-auth', () => ({ getServerSession: jest.fn() }))
|
|
jest.mock('@/lib/auth', () => ({ authOptions: {} }))
|
|
jest.mock('@/lib/prisma', () => ({ prisma: {} }))
|
|
|
|
async function composite(challenge: ReturnType<typeof createMultiReferenceChallenge>, replacement?: 'repeat-first' | 'reverse' | 'omit-last') {
|
|
const { columns, rows } = multiReferenceGrid(challenge.references.length)
|
|
let images = await multiReferenceImages(challenge)
|
|
if (replacement === 'repeat-first') images = images.map(() => images[0])
|
|
if (replacement === 'reverse') images.reverse()
|
|
if (replacement === 'omit-last') images.pop()
|
|
return sharp({ create: { width: columns * 512, height: rows * 512, channels: 3, background: '#fff' } }).composite(images.map((image, i) => ({ input: image.buffer, left: i % columns * 512, top: Math.floor(i / columns) * 512 }))).png().toBuffer()
|
|
}
|
|
test.each([2, 4, 8])('all %i independently supplied references must match their ordered tiles', async count => {
|
|
const challenge = createMultiReferenceChallenge(count)
|
|
expect(readMultiReferenceChallenge(JSON.parse(JSON.stringify(challenge)))).toEqual(challenge)
|
|
expect(new Set(challenge.references.map(value => `${value.topLeft}/${value.bottomRight}`)).size).toBe(count)
|
|
const prompt = multiReferencePrompt(challenge)
|
|
expect(prompt).not.toMatch(/red|blue|green|yellow|红色|蓝色|绿色|黄色/i)
|
|
const correct = await composite(challenge)
|
|
await expect(validateMultiReferenceOutput(correct, challenge)).resolves.toMatchObject({ referenceImageCount: count, allReferenceImagesMatched: true, referenceMatches: Array(count).fill(true) })
|
|
const padded = await sharp(correct).extend({ top: 100, bottom: 700, left: 60, right: 60, background: '#fff' }).png().toBuffer()
|
|
await expect(validateMultiReferenceOutput(padded, challenge)).resolves.toMatchObject({ referenceImageCount: count, allReferenceImagesMatched: true })
|
|
for (const mutation of ['repeat-first', 'reverse', 'omit-last'] as const) await expect(validateMultiReferenceOutput(await composite(challenge, mutation), challenge)).rejects.toMatchObject({ code: 'verification_output_invalid' })
|
|
})
|
|
test('invalid counts and duplicated checkpoints are rejected before a model request', () => {
|
|
for (const count of [0, 1, 9, 2.5]) expect(() => createMultiReferenceChallenge(count)).toThrow()
|
|
for (const value of [null, { version: 1, references: [] }, { version: 1, references: [{ topLeft: 'red', bottomRight: 'blue' }, { topLeft: 'red', bottomRight: 'blue' }] }]) expect(() => readMultiReferenceChallenge(value)).toThrow()
|
|
})
|
|
|
|
test('square-panel layout may include unused cells but cannot omit or replace an input', async () => {
|
|
const challenge = createMultiReferenceChallenge(2), images = await multiReferenceImages(challenge)
|
|
const background = Buffer.from('<svg width="1024" height="1024"><rect width="1024" height="1024" fill="white"/><path d="M512 0V1024 M0 512H1024" stroke="black" stroke-width="2"/></svg>')
|
|
const image = await sharp(background).composite(images.map((value, i) => ({ input: value.buffer, left: i * 512, top: 0 }))).png().toBuffer()
|
|
await expect(validateMultiReferenceOutput(image, challenge)).resolves.toMatchObject({ referenceImageCount: 2, referenceMatches: [true, true], referenceGrid: { columns: 2, rows: 2 } })
|
|
const missing = await sharp(background).composite([{ input: images[0].buffer, left: 0, top: 0 }]).png().toBuffer()
|
|
await expect(validateMultiReferenceOutput(missing, challenge)).rejects.toThrow()
|
|
})
|
|
test('a count assertion without per-reference pixel evidence cannot expand the runtime allowance', () => {
|
|
const base: any = { stage: 'image', status: 'passed', evidence: { referenceImageCount: 8 }, deadlineAt: new Date() }
|
|
expect(verifiedReferenceImageCount(base)).toBeNull()
|
|
expect(verifiedReferenceImageCount({ ...base, evidence: { referenceImageCount: 8, multiReferenceRuleVersion: 1, allReferenceImagesMatched: true, referenceMatches: [true] } })).toBeNull()
|
|
expect(verifiedReferenceImageCount({ ...base, evidence: { referenceImageCount: 4, multiReferenceRuleVersion: 1, allReferenceImagesMatched: true, referenceMatches: [true, true, false, true] } })).toBeNull()
|
|
expect(verifiedReferenceImageCount({ ...base, evidence: { referenceImageCount: 4, multiReferenceRuleVersion: 1, allReferenceImagesMatched: true, referenceMatches: Array(4).fill(true) } })).toBe(4)
|
|
})
|