/** * Test Spec Validation * * Validates test specifications using Zod schemas. * Provides detailed error messages with field paths for easy debugging. */ import { ZodError } from "zod"; import type { TestSpec, SuiteConfig, SuiteSpec } from "@wavespec/types"; import { TestSpecSchema, SuiteConfigSchema, SuiteSpecSchema } from "./schemas.js"; /** * Validation result * * Indicates whether validation passed and provides detailed error messages. */ export interface ValidationResult { /** Whether validation passed */ valid: boolean; /** Validation error messages with field paths (if any) */ errors: string[]; } /** * Format Zod error into readable error messages * * Converts Zod validation errors into human-readable messages with field paths. * * @param error - Zod validation error * @returns Array of formatted error messages */ function formatZodError(error: ZodError): string[] { return error.errors.map((err) => { const path = err.path.length > 0 ? err.path.join(".") : "root"; return `${path}: ${err.message}`; }); } /** * Validate test specification * * Validates that an object conforms to the TestSpec schema. * Checks all required fields, validates types, and ensures constraints are met. * * @param spec - The object to validate as a TestSpec * @returns Validation result with detailed error messages * * @example * ```typescript * const result = validateTestSpec({ * name: "Test API call", * operation: "call", * params: { target: "echo" }, * assertions: [{ type: "success" }] * }); * * if (!result.valid) { * console.error("Test spec validation failed:"); * result.errors.forEach(err => console.error(` - ${err}`)); * } * ``` */ export function validateTestSpec(spec: unknown): ValidationResult { try { TestSpecSchema.parse(spec); return { valid: true, errors: [] }; } catch (error) { if (error instanceof ZodError) { return { valid: false, errors: formatZodError(error), }; } return { valid: false, errors: [`Unexpected validation error: ${error}`], }; } } /** * Validate suite configuration * * Validates that an object conforms to the SuiteConfig schema. * Checks suite name, tests array, and all configuration options. * * @param config - The object to validate as a SuiteConfig * @returns Validation result with detailed error messages * * @example * ```typescript * const result = validateSuiteConfig({ * name: "API Test Suite", * tests: [ * { name: "Test 1", operation: "call", params: {}, assertions: [] } * ] * }); * * if (!result.valid) { * console.error("Suite config validation failed:"); * result.errors.forEach(err => console.error(` - ${err}`)); * } * ``` */ export function validateSuiteConfig(config: unknown): ValidationResult { try { SuiteConfigSchema.parse(config); return { valid: true, errors: [] }; } catch (error) { if (error instanceof ZodError) { return { valid: false, errors: formatZodError(error), }; } return { valid: false, errors: [`Unexpected validation error: ${error}`], }; } } /** * Validate suite specification * * Validates that an object conforms to the SuiteSpec schema (top-level YAML/JSON format). * Checks version and suite configuration. * * @param spec - The object to validate as a SuiteSpec * @returns Validation result with detailed error messages * * @example * ```typescript * const result = validateSuiteSpec({ * version: "1.0", * suite: { * name: "My Test Suite", * tests: [] * } * }); * * if (!result.valid) { * console.error("Suite spec validation failed:"); * result.errors.forEach(err => console.error(` - ${err}`)); * } * ``` */ export function validateSuiteSpec(spec: unknown): ValidationResult { try { SuiteSpecSchema.parse(spec); return { valid: true, errors: [] }; } catch (error) { if (error instanceof ZodError) { return { valid: false, errors: formatZodError(error), }; } return { valid: false, errors: [`Unexpected validation error: ${error}`], }; } } /** * Type guard for TestSpec * * Checks if an object is a valid TestSpec at runtime. * * @param spec - The object to check * @returns True if the object is a valid TestSpec * * @example * ```typescript * if (isValidTestSpec(obj)) { * // TypeScript now knows obj is a TestSpec * console.log(obj.name); * } * ``` */ export function isValidTestSpec(spec: unknown): spec is TestSpec { const result = validateTestSpec(spec); return result.valid; } /** * Type guard for SuiteConfig * * Checks if an object is a valid SuiteConfig at runtime. * * @param config - The object to check * @returns True if the object is a valid SuiteConfig * * @example * ```typescript * if (isValidSuiteConfig(obj)) { * // TypeScript now knows obj is a SuiteConfig * console.log(obj.name); * } * ``` */ export function isValidSuiteConfig(config: unknown): config is SuiteConfig { const result = validateSuiteConfig(config); return result.valid; } /** * Type guard for SuiteSpec * * Checks if an object is a valid SuiteSpec at runtime. * * @param spec - The object to check * @returns True if the object is a valid SuiteSpec * * @example * ```typescript * if (isValidSuiteSpec(obj)) { * // TypeScript now knows obj is a SuiteSpec * console.log(obj.version); * } * ``` */ export function isValidSuiteSpec(spec: unknown): spec is SuiteSpec { const result = validateSuiteSpec(spec); return result.valid; }