/** @jest-environment node */ import { desktopProjectBody } from '@/lib/desktop-client/contract.mjs' const hash = 'a'.repeat(64) function fixture() { return { revision: 0, status: 'draft', document: { version: 3, id: 'project', name: '顾家五屏', screens: [{ id: 'one', width: 1440, height: 2200, previewSource: null }], native: { format: 'photocraft', schemaVersion: 1, documentSource: null as string | null, files: [] as any[] } } } } test('accepts an explicit incomplete draft but never labels it completed', () => { const value = fixture(); expect(desktopProjectBody(value).document.version).toBe(3) value.status = 'completed'; expect(() => desktopProjectBody(value)).toThrow('完结项目') }) test('requires a complete safe manifest and strips unrecognized fields from native metadata', () => { const value = fixture() value.document.native.files = ['canvas.pcraft','context.json','session-backup.json'].map(path => ({ path, source: 'native:' + hash, sha256: hash, size: 100 })) value.document.native.documentSource = 'native:' + hash expect(desktopProjectBody(value).document.native.files).toHaveLength(3) value.document.native.files[0].path = '../credentials.json'; expect(() => desktopProjectBody(value)).toThrow('原生工程文件') }) test('rejects unhashed native sources, missing session backup, duplicate screens and oversized geometry', () => { const value = fixture() value.document.native.files = [{ path: 'canvas.pcraft', source: 'native:' + hash, sha256: hash, size: 100 }] value.document.native.documentSource = 'native:' + hash expect(() => desktopProjectBody(value)).toThrow('会话恢复文件') value.document.native.files[0].source = 'native:uuid'; expect(() => desktopProjectBody(value)).toThrow('工程文件无效') const other = fixture(); other.document.screens[0].height = 30001; expect(() => desktopProjectBody(other)).toThrow('画布超出限制') })