|
1 | 1 | import { describe, expect, it } from 'vitest' |
2 | 2 | import { z } from 'zod' |
| 3 | +import { compileMcpToolSchema } from '@/lib/mcp/tool-schema' |
3 | 4 | import { generateToolInputSchema } from '@/lib/mcp/workflow-tool-schema' |
4 | 5 | import { generateWorkflowInputShape } from '@/lib/workflows/input-schema' |
5 | 6 | import type { InputFormatField } from '@/lib/workflows/types' |
@@ -29,7 +30,7 @@ describe('workflow input schemas', () => { |
29 | 30 | type: 'array', |
30 | 31 | items: { |
31 | 32 | type: 'object', |
32 | | - required: ['id', 'name', 'url', 'size', 'type'], |
| 33 | + required: ['id', 'name', 'url', 'size', 'type', 'key'], |
33 | 34 | properties: { |
34 | 35 | key: { type: 'string' }, |
35 | 36 | type: { type: 'string' }, |
@@ -62,12 +63,34 @@ describe('workflow input schemas', () => { |
62 | 63 | expect(schema.safeParse({ files: [invalidFile] }).success).toBe(false) |
63 | 64 | }) |
64 | 65 |
|
65 | | - it('accepts an internal file URL whose storage key can be recovered', () => { |
66 | | - const { key, ...internalFile } = file |
67 | | - internalFile.url = `/api/files/serve/s3/${encodeURIComponent(key)}?context=workspace` |
68 | | - expect(schema.parse({ files: [internalFile] })).toEqual({ files: [internalFile] }) |
| 66 | + it.each([ |
| 67 | + { value: file, valid: true }, |
| 68 | + { value: { ...file, key: '' }, valid: false }, |
| 69 | + { value: { ...file, key: undefined }, valid: false }, |
| 70 | + { |
| 71 | + value: { |
| 72 | + ...file, |
| 73 | + key: undefined, |
| 74 | + url: `/api/files/serve/s3/${encodeURIComponent(file.key)}?context=workspace`, |
| 75 | + }, |
| 76 | + valid: false, |
| 77 | + }, |
| 78 | + ])('advertises the same storage-key requirement it validates: $valid', ({ value, valid }) => { |
| 79 | + const validate = compileMcpToolSchema(generateToolInputSchema(inputFormat)) |
| 80 | + const input = { files: [value] } |
| 81 | + expect(validate(input)).toBe(valid) |
| 82 | + expect(schema.safeParse(input).success).toBe(valid) |
69 | 83 | }) |
70 | 84 |
|
| 85 | + it.each(['__proto__', 'constructor', 'prototype'])( |
| 86 | + 'rejects reserved input name %s before schema construction', |
| 87 | + (name) => { |
| 88 | + const fields: InputFormatField[] = [{ name, type: 'string' }] |
| 89 | + expect(() => generateWorkflowInputShape(fields)).toThrow('is reserved') |
| 90 | + expect(() => generateToolInputSchema(fields)).toThrow('is reserved') |
| 91 | + } |
| 92 | + ) |
| 93 | + |
71 | 94 | it('shares required fields, descriptions, and arbitrary array items across schemas', () => { |
72 | 95 | const fields: InputFormatField[] = [ |
73 | 96 | { name: ' message ', type: 'string', description: ' User message ' }, |
|
0 commit comments