validate-test-suites.sh 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. #!/bin/bash
  2. # validate-test-suites.sh
  3. # Validates all test suite JSON files against schema and checks paths exist
  4. #
  5. # Usage:
  6. # ./scripts/validation/validate-test-suites.sh [agent]
  7. # ./scripts/validation/validate-test-suites.sh openagent
  8. # ./scripts/validation/validate-test-suites.sh --all
  9. #
  10. # Exit codes:
  11. # 0 - All suites valid
  12. # 1 - Validation errors found
  13. # 2 - Missing dependencies
  14. set -e
  15. # Colors
  16. RED='\033[0;31m'
  17. GREEN='\033[0;32m'
  18. YELLOW='\033[1;33m'
  19. BLUE='\033[0;34m'
  20. NC='\033[0m' # No Color
  21. # Get script directory
  22. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  23. PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
  24. AJV_CMD=(pnpm --dir "$PROJECT_ROOT/evals/framework" exec ajv)
  25. # Check if ajv-cli is installed
  26. if ! "${AJV_CMD[@]}" help > /dev/null 2>&1; then
  27. echo -e "${RED}❌ Error: ajv-cli not found${NC}"
  28. echo ""
  29. echo "Install workspace dependencies with: pnpm install"
  30. echo "Or add ajv-cli to the eval workspace with: pnpm --dir evals/framework add -D ajv-cli"
  31. exit 2
  32. fi
  33. # Parse arguments
  34. AGENT="${1:-openagent}"
  35. VALIDATE_ALL=false
  36. if [[ "$1" == "--all" ]]; then
  37. VALIDATE_ALL=true
  38. fi
  39. # Counters
  40. TOTAL_SUITES=0
  41. VALID_SUITES=0
  42. INVALID_SUITES=0
  43. TOTAL_ERRORS=0
  44. TOTAL_WARNINGS=0
  45. echo -e "${BLUE}🔍 Validating Test Suites${NC}"
  46. echo ""
  47. # Function to validate a single suite
  48. validate_suite() {
  49. local agent=$1
  50. local suite_file=$2
  51. local suite_name
  52. suite_name=$(basename "$suite_file" .json)
  53. TOTAL_SUITES=$((TOTAL_SUITES + 1))
  54. echo -e "${BLUE}Validating:${NC} $agent/$suite_name"
  55. local schema_file="$PROJECT_ROOT/evals/agents/$agent/config/suite-schema.json"
  56. local tests_dir="$PROJECT_ROOT/evals/agents/$agent/tests"
  57. local suite_valid=true
  58. local suite_errors=0
  59. local suite_warnings=0
  60. # 1. Validate JSON syntax
  61. if ! jq empty "$suite_file" 2>/dev/null; then
  62. echo -e " ${RED}❌ Invalid JSON syntax${NC}"
  63. suite_valid=false
  64. suite_errors=$((suite_errors + 1))
  65. INVALID_SUITES=$((INVALID_SUITES + 1))
  66. TOTAL_ERRORS=$((TOTAL_ERRORS + 1))
  67. return
  68. fi
  69. # 2. Validate against schema
  70. if [[ -f "$schema_file" ]]; then
  71. validation_output=$("${AJV_CMD[@]}" validate -s "$schema_file" -d "$suite_file" --strict=false 2>&1)
  72. if ! echo "$validation_output" | grep -q "valid"; then
  73. echo -e " ${RED}❌ Schema validation failed${NC}"
  74. echo "$validation_output" | grep -v "valid" | sed 's/^/ /'
  75. suite_valid=false
  76. suite_errors=$((suite_errors + 1))
  77. fi
  78. else
  79. echo -e " ${YELLOW}⚠️ Schema not found: $schema_file${NC}"
  80. suite_warnings=$((suite_warnings + 1))
  81. fi
  82. # 3. Validate test paths exist
  83. local missing_tests=()
  84. local test_count=0
  85. while IFS= read -r test_path; do
  86. test_count=$((test_count + 1))
  87. local full_path="$tests_dir/$test_path"
  88. if [[ ! -f "$full_path" ]]; then
  89. missing_tests+=("$test_path")
  90. fi
  91. done < <(jq -r '.tests[].path' "$suite_file")
  92. # 4. Check test count matches
  93. local declared_count
  94. declared_count=$(jq -r '.totalTests' "$suite_file")
  95. if [[ "$test_count" -ne "$declared_count" ]]; then
  96. echo -e " ${YELLOW}⚠️ Test count mismatch: found $test_count, declared $declared_count${NC}"
  97. suite_warnings=$((suite_warnings + 1))
  98. fi
  99. # 5. Report missing tests
  100. if [[ ${#missing_tests[@]} -gt 0 ]]; then
  101. echo -e " ${RED}❌ Missing test files (${#missing_tests[@]}):${NC}"
  102. for missing in "${missing_tests[@]}"; do
  103. echo -e " - $missing"
  104. # Suggest similar files
  105. local dir
  106. dir=$(dirname "$missing")
  107. local filename
  108. filename=$(basename "$missing")
  109. if [[ -d "$tests_dir/$dir" ]]; then
  110. local similar
  111. # shellcheck disable=SC2001
  112. similar=$(find "$tests_dir/$dir" -name "*.yaml" -type f -exec basename {} \; | grep -i "$(echo "$filename" | cut -d'-' -f1)" | head -3)
  113. if [[ -n "$similar" ]]; then
  114. echo -e " ${YELLOW}Did you mean?${NC}"
  115. echo "$similar" | sed 's/^/ - /'
  116. fi
  117. fi
  118. done
  119. suite_valid=false
  120. suite_errors=$((suite_errors + ${#missing_tests[@]}))
  121. fi
  122. # 6. Summary for this suite
  123. if [[ "$suite_valid" == true ]]; then
  124. echo -e " ${GREEN}✅ Valid${NC} ($test_count tests)"
  125. VALID_SUITES=$((VALID_SUITES + 1))
  126. else
  127. echo -e " ${RED}❌ Invalid${NC} ($suite_errors errors, $suite_warnings warnings)"
  128. INVALID_SUITES=$((INVALID_SUITES + 1))
  129. fi
  130. TOTAL_ERRORS=$((TOTAL_ERRORS + suite_errors))
  131. TOTAL_WARNINGS=$((TOTAL_WARNINGS + suite_warnings))
  132. echo ""
  133. }
  134. # Validate suites
  135. if [[ "$VALIDATE_ALL" == true ]]; then
  136. # Validate all agents
  137. for agent_dir in "$PROJECT_ROOT/evals/agents"/*; do
  138. if [[ -d "$agent_dir" ]]; then
  139. agent=$(basename "$agent_dir")
  140. # Check for suites directory
  141. suites_dir="$agent_dir/config/suites"
  142. if [[ -d "$suites_dir" ]]; then
  143. for suite_file in "$suites_dir"/*.json; do
  144. if [[ -f "$suite_file" ]]; then
  145. validate_suite "$agent" "$suite_file"
  146. fi
  147. done
  148. fi
  149. # Check for legacy core-tests.json
  150. legacy_file="$agent_dir/config/core-tests.json"
  151. if [[ -f "$legacy_file" ]]; then
  152. validate_suite "$agent" "$legacy_file"
  153. fi
  154. fi
  155. done
  156. else
  157. # Validate specific agent
  158. agent_dir="$PROJECT_ROOT/evals/agents/$AGENT"
  159. if [[ ! -d "$agent_dir" ]]; then
  160. echo -e "${RED}❌ Agent not found: $AGENT${NC}"
  161. exit 1
  162. fi
  163. # Check for suites directory
  164. suites_dir="$agent_dir/config/suites"
  165. if [[ -d "$suites_dir" ]]; then
  166. for suite_file in "$suites_dir"/*.json; do
  167. if [[ -f "$suite_file" ]]; then
  168. validate_suite "$AGENT" "$suite_file"
  169. fi
  170. done
  171. fi
  172. # Check for legacy core-tests.json
  173. legacy_file="$agent_dir/config/core-tests.json"
  174. if [[ -f "$legacy_file" ]]; then
  175. validate_suite "$AGENT" "$legacy_file"
  176. fi
  177. if [[ $TOTAL_SUITES -eq 0 ]]; then
  178. echo -e "${YELLOW}⚠️ No test suites found for agent: $AGENT${NC}"
  179. echo ""
  180. echo "Expected locations:"
  181. echo " - $suites_dir/*.json"
  182. echo " - $legacy_file"
  183. exit 1
  184. fi
  185. fi
  186. # Final summary
  187. echo -e "${BLUE}═══════════════════════════════════════════════════════${NC}"
  188. echo -e "${BLUE}Summary${NC}"
  189. echo -e "${BLUE}═══════════════════════════════════════════════════════${NC}"
  190. echo -e "Total suites: $TOTAL_SUITES"
  191. echo -e "${GREEN}Valid suites: $VALID_SUITES${NC}"
  192. if [[ $INVALID_SUITES -gt 0 ]]; then
  193. echo -e "${RED}Invalid suites: $INVALID_SUITES${NC}"
  194. fi
  195. if [[ $TOTAL_ERRORS -gt 0 ]]; then
  196. echo -e "${RED}Total errors: $TOTAL_ERRORS${NC}"
  197. fi
  198. if [[ $TOTAL_WARNINGS -gt 0 ]]; then
  199. echo -e "${YELLOW}Total warnings: $TOTAL_WARNINGS${NC}"
  200. fi
  201. echo ""
  202. # Exit with appropriate code
  203. if [[ $INVALID_SUITES -gt 0 ]] || [[ $TOTAL_ERRORS -gt 0 ]]; then
  204. echo -e "${RED}❌ Validation failed${NC}"
  205. exit 1
  206. else
  207. echo -e "${GREEN}✅ All suites valid${NC}"
  208. exit 0
  209. fi