288 lines
7.9 KiB
TypeScript
288 lines
7.9 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
import { z } from '@bpinternal/zui'
|
|
import { JsonParsingError } from '../src/operations/errors'
|
|
|
|
describe('JsonParsingError', () => {
|
|
it('formats simple zod validation error in LLM-friendly way', () => {
|
|
const schema = z.object({
|
|
name: z.string(),
|
|
age: z.number(),
|
|
})
|
|
|
|
try {
|
|
schema.parse({ name: 'John', age: 'not a number' })
|
|
} catch (error) {
|
|
const jsonParsingError = new JsonParsingError('{"name":"John","age":"not a number"}', error as Error)
|
|
|
|
expect(jsonParsingError.message).toMatchInlineSnapshot(`
|
|
"Error parsing JSON:
|
|
|
|
---JSON---
|
|
{"name":"John","age":"not a number"}
|
|
|
|
---Validation Errors---
|
|
|
|
1. Field: "age"
|
|
Problem: Expected number, but received string
|
|
Message: Expected number, received string
|
|
"
|
|
`)
|
|
}
|
|
})
|
|
|
|
it('formats complex nested zod validation error in LLM-friendly way', () => {
|
|
const schema = z.object({
|
|
name: z.string().min(3),
|
|
email: z.string().email(),
|
|
age: z.number().positive(),
|
|
address: z.object({
|
|
street: z.string(),
|
|
city: z.string(),
|
|
zipCode: z.string().length(5),
|
|
}),
|
|
tags: z.array(z.string()),
|
|
})
|
|
|
|
try {
|
|
schema.parse({
|
|
name: 'Jo',
|
|
email: 'invalid-email',
|
|
age: -5,
|
|
address: {
|
|
street: 'Main St',
|
|
city: '',
|
|
zipCode: '1234',
|
|
},
|
|
tags: ['valid', 123, null],
|
|
})
|
|
} catch (error) {
|
|
const jsonParsingError = new JsonParsingError(
|
|
JSON.stringify({
|
|
name: 'Jo',
|
|
email: 'invalid-email',
|
|
age: -5,
|
|
address: { street: 'Main St', city: '', zipCode: '1234' },
|
|
tags: ['valid', 123, null],
|
|
}),
|
|
error as Error
|
|
)
|
|
|
|
expect(jsonParsingError.message).toMatchInlineSnapshot(`
|
|
"Error parsing JSON:
|
|
|
|
---JSON---
|
|
{"name":"Jo","email":"invalid-email","age":-5,"address":{"street":"Main St","city":"","zipCode":"1234"},"tags":["valid",123,null]}
|
|
|
|
---Validation Errors---
|
|
|
|
1. Field: "name"
|
|
Problem: String must be at least 3 characters
|
|
Message: String must contain at least 3 character(s)
|
|
|
|
2. Field: "email"
|
|
Problem: Invalid email format
|
|
Message: Invalid email
|
|
|
|
3. Field: "age"
|
|
Problem: Number must be greater than 0
|
|
Message: Number must be greater than 0
|
|
|
|
4. Field: "address.zipCode"
|
|
Problem: String must be exactly 5 characters
|
|
Message: String must contain exactly 5 character(s)
|
|
|
|
5. Field: "tags.1"
|
|
Problem: Expected string, but received number
|
|
Message: Expected string, received number
|
|
|
|
6. Field: "tags.2"
|
|
Problem: Expected string, but received null
|
|
Message: Expected string, received null
|
|
"
|
|
`)
|
|
}
|
|
})
|
|
|
|
it('formats array validation error in LLM-friendly way', () => {
|
|
const schema = z.array(
|
|
z.object({
|
|
id: z.number(),
|
|
name: z.string(),
|
|
})
|
|
)
|
|
|
|
try {
|
|
schema.parse([
|
|
{ id: 1, name: 'Valid' },
|
|
{ id: 'invalid', name: 'Invalid ID' },
|
|
{ id: 3, name: 123 },
|
|
])
|
|
} catch (error) {
|
|
const jsonParsingError = new JsonParsingError(
|
|
JSON.stringify([
|
|
{ id: 1, name: 'Valid' },
|
|
{ id: 'invalid', name: 'Invalid ID' },
|
|
{ id: 3, name: 123 },
|
|
]),
|
|
error as Error
|
|
)
|
|
|
|
expect(jsonParsingError.message).toMatchInlineSnapshot(`
|
|
"Error parsing JSON:
|
|
|
|
---JSON---
|
|
[{"id":1,"name":"Valid"},{"id":"invalid","name":"Invalid ID"},{"id":3,"name":123}]
|
|
|
|
---Validation Errors---
|
|
|
|
1. Field: "1.id"
|
|
Problem: Expected number, but received string
|
|
Message: Expected number, received string
|
|
|
|
2. Field: "2.name"
|
|
Problem: Expected string, but received number
|
|
Message: Expected string, received number
|
|
"
|
|
`)
|
|
}
|
|
})
|
|
|
|
it('formats missing required field error in LLM-friendly way', () => {
|
|
const schema = z.object({
|
|
requiredField: z.string(),
|
|
optionalField: z.string().optional(),
|
|
})
|
|
|
|
try {
|
|
schema.parse({ optionalField: 'present' })
|
|
} catch (error) {
|
|
const jsonParsingError = new JsonParsingError('{"optionalField":"present"}', error as Error)
|
|
|
|
expect(jsonParsingError.message).toMatchInlineSnapshot(`
|
|
"Error parsing JSON:
|
|
|
|
---JSON---
|
|
{"optionalField":"present"}
|
|
|
|
---Validation Errors---
|
|
|
|
1. Field: "requiredField"
|
|
Problem: Expected string, but received undefined
|
|
Message: Required
|
|
"
|
|
`)
|
|
}
|
|
})
|
|
|
|
it('formats union/enum validation error in LLM-friendly way', () => {
|
|
const schema = z.object({
|
|
status: z.enum(['pending', 'approved', 'rejected']),
|
|
priority: z.union([z.literal('low'), z.literal('medium'), z.literal('high')]),
|
|
})
|
|
|
|
try {
|
|
schema.parse({ status: 'invalid-status', priority: 'urgent' })
|
|
} catch (error) {
|
|
const jsonParsingError = new JsonParsingError('{"status":"invalid-status","priority":"urgent"}', error as Error)
|
|
|
|
expect(jsonParsingError.message).toMatchInlineSnapshot(`
|
|
"Error parsing JSON:
|
|
|
|
---JSON---
|
|
{"status":"invalid-status","priority":"urgent"}
|
|
|
|
---Validation Errors---
|
|
|
|
1. Field: "status"
|
|
Problem: Invalid value "invalid-status"
|
|
Allowed values: "pending", "approved", "rejected"
|
|
Message: Invalid enum value. Expected 'pending' | 'approved' | 'rejected', received 'invalid-status'
|
|
|
|
2. Field: "priority"
|
|
Problem: Value doesn't match any of the expected formats
|
|
Message: Invalid input
|
|
"
|
|
`)
|
|
}
|
|
})
|
|
|
|
it('formats type mismatch errors in LLM-friendly way', () => {
|
|
const schema = z.object({
|
|
string: z.string(),
|
|
number: z.number(),
|
|
boolean: z.boolean(),
|
|
array: z.array(z.string()),
|
|
object: z.object({ nested: z.string() }),
|
|
})
|
|
|
|
try {
|
|
schema.parse({
|
|
string: 123,
|
|
number: 'not a number',
|
|
boolean: 'yes',
|
|
array: 'not an array',
|
|
object: 'not an object',
|
|
})
|
|
} catch (error) {
|
|
const jsonParsingError = new JsonParsingError(
|
|
JSON.stringify({
|
|
string: 123,
|
|
number: 'not a number',
|
|
boolean: 'yes',
|
|
array: 'not an array',
|
|
object: 'not an object',
|
|
}),
|
|
error as Error
|
|
)
|
|
|
|
expect(jsonParsingError.message).toMatchInlineSnapshot(`
|
|
"Error parsing JSON:
|
|
|
|
---JSON---
|
|
{"string":123,"number":"not a number","boolean":"yes","array":"not an array","object":"not an object"}
|
|
|
|
---Validation Errors---
|
|
|
|
1. Field: "string"
|
|
Problem: Expected string, but received number
|
|
Message: Expected string, received number
|
|
|
|
2. Field: "number"
|
|
Problem: Expected number, but received string
|
|
Message: Expected number, received string
|
|
|
|
3. Field: "boolean"
|
|
Problem: Expected boolean, but received string
|
|
Message: Expected boolean, received string
|
|
|
|
4. Field: "array"
|
|
Problem: Expected array, but received string
|
|
Message: Expected array, received string
|
|
|
|
5. Field: "object"
|
|
Problem: Expected object, but received string
|
|
Message: Expected object, received string
|
|
"
|
|
`)
|
|
}
|
|
})
|
|
|
|
it('handles non-zod errors gracefully', () => {
|
|
const regularError = new Error('This is a regular parsing error')
|
|
const jsonParsingError = new JsonParsingError('{"invalid json"}', regularError)
|
|
|
|
expect(jsonParsingError.message).toMatchInlineSnapshot(`
|
|
"Error parsing JSON:
|
|
|
|
---JSON---
|
|
{"invalid json"}
|
|
|
|
---Error---
|
|
|
|
The JSON provided is not valid JSON.
|
|
Details: This is a regular parsing error
|
|
"
|
|
`)
|
|
})
|
|
})
|