|
1 | 1 | import { describe, expect, it } from 'vitest' |
| 2 | +import { fileManageWriteBodySchema } from '@/lib/api/contracts/tools/file' |
2 | 3 | import { FileV4Block, FileV5Block } from '@/blocks/blocks/file' |
| 4 | +import { fileWriteTool } from '@/tools/file/write' |
3 | 5 |
|
4 | 6 | describe('FileV4Block', () => { |
5 | 7 | const buildParams = FileV4Block.tools.config.params |
@@ -54,6 +56,72 @@ describe('FileV4Block', () => { |
54 | 56 | describe('FileV5Block', () => { |
55 | 57 | const buildParams = FileV5Block.tools.config.params |
56 | 58 |
|
| 59 | + it.each([ |
| 60 | + [null, null], |
| 61 | + ['', null], |
| 62 | + [undefined, undefined], |
| 63 | + ['', 'image/png'], |
| 64 | + ])( |
| 65 | + 'writes a produced file with blank content %s and MIME override %s', |
| 66 | + (content, contentType) => { |
| 67 | + const file = { |
| 68 | + id: 'file_execution-output', |
| 69 | + name: 'test-card-160x90.png', |
| 70 | + url: '/api/files/serve/execution-output.png', |
| 71 | + key: 'execution/workspace-1/workflow-1/run-1/output.png', |
| 72 | + context: 'execution', |
| 73 | + type: 'image/png', |
| 74 | + size: 514, |
| 75 | + } |
| 76 | + const inputs = { |
| 77 | + operation: 'file_write', |
| 78 | + fileName: 'test-card-160x90.png', |
| 79 | + writeFolderRef: '/Media%20Studio%20Tests', |
| 80 | + writeFileInput: file, |
| 81 | + content, |
| 82 | + contentType, |
| 83 | + overwrite: false, |
| 84 | + _context: { workspaceId: 'workspace-1' }, |
| 85 | + } |
| 86 | + // GenericBlockHandler retains raw inputs when applying a block's transform. |
| 87 | + const params = { ...inputs, ...buildParams(inputs) } |
| 88 | + const parsed = fileManageWriteBodySchema.parse(fileWriteTool.operation.input(params)) |
| 89 | + expect(parsed.fileInput).toEqual(file) |
| 90 | + expect(parsed.content).toBeUndefined() |
| 91 | + expect(parsed.contentType).toBe(contentType ?? undefined) |
| 92 | + expect(parsed.folderPath).toBe('/Media%20Studio%20Tests') |
| 93 | + } |
| 94 | + ) |
| 95 | + |
| 96 | + it('preserves empty text writes and rejects a file combined with nonempty text', () => { |
| 97 | + const text = { operation: 'file_write', fileName: 'empty.txt', content: '', contentType: null } |
| 98 | + const parsed = fileManageWriteBodySchema.parse( |
| 99 | + fileWriteTool.operation.input({ ...text, ...buildParams(text) }) |
| 100 | + ) |
| 101 | + expect(parsed.content).toBe('') |
| 102 | + expect(parsed.fileInput).toBeUndefined() |
| 103 | + const both = { ...text, content: 'conflict', writeFileInput: { id: 'file-output' } } |
| 104 | + expect( |
| 105 | + fileManageWriteBodySchema.safeParse( |
| 106 | + fileWriteTool.operation.input({ ...both, ...buildParams(both) }) |
| 107 | + ).success |
| 108 | + ).toBe(false) |
| 109 | + }) |
| 110 | + |
| 111 | + it('rejects invalid non-string MIME values at the existing contract', () => { |
| 112 | + const inputs = { |
| 113 | + operation: 'file_write', |
| 114 | + fileName: 'note.txt', |
| 115 | + content: 'keep this text', |
| 116 | + contentType: 42, |
| 117 | + } |
| 118 | + expect( |
| 119 | + fileManageWriteBodySchema.safeParse( |
| 120 | + fileWriteTool.operation.input({ ...inputs, ...buildParams(inputs) }) |
| 121 | + ).success |
| 122 | + ).toBe(false) |
| 123 | + }) |
| 124 | + |
57 | 125 | it('maps each operation directly to its tool', () => { |
58 | 126 | expect(FileV5Block.tools.config.tool({ operation: 'file_read' })).toBe('file_read') |
59 | 127 | expect(FileV5Block.tools.config.tool({ operation: 'file_get_content' })).toBe( |
|
0 commit comments