data-processing.sh 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. #!/bin/bash
  2. # Functional tests for data-processing skill
  3. # Tests jq and yq CLI tools
  4. set -euo pipefail
  5. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  6. FIXTURES="$SCRIPT_DIR/../fixtures"
  7. # Colors
  8. RED='\033[0;31m'
  9. GREEN='\033[0;32m'
  10. YELLOW='\033[1;33m'
  11. NC='\033[0m'
  12. PASSED=0
  13. FAILED=0
  14. SKIPPED=0
  15. pass() { ((PASSED++)); echo -e "${GREEN}✓${NC} $1"; }
  16. fail() { ((FAILED++)); echo -e "${RED}✗${NC} $1: $2"; }
  17. skip() { ((SKIPPED++)); echo -e "${YELLOW}○${NC} $1 (skipped: $2)"; }
  18. # Check prerequisites
  19. HAS_JQ=false
  20. HAS_YQ=false
  21. check_prereqs() {
  22. local missing=()
  23. if command -v jq >/dev/null 2>&1; then
  24. HAS_JQ=true
  25. else
  26. missing+=("jq")
  27. fi
  28. if command -v yq >/dev/null 2>&1; then
  29. HAS_YQ=true
  30. else
  31. missing+=("yq")
  32. fi
  33. if [[ ${#missing[@]} -gt 0 ]]; then
  34. echo -e "${YELLOW}Missing tools: ${missing[*]}${NC}"
  35. echo "Install with: brew install ${missing[*]}"
  36. echo "Some tests will be skipped."
  37. echo ""
  38. fi
  39. }
  40. # Test helper
  41. assert_eq() {
  42. local name="$1"
  43. local expected="$2"
  44. local actual="$3"
  45. if [[ "$expected" == "$actual" ]]; then
  46. pass "$name"
  47. else
  48. fail "$name" "expected '$expected', got '$actual'"
  49. fi
  50. }
  51. assert_contains() {
  52. local name="$1"
  53. local needle="$2"
  54. local haystack="$3"
  55. if [[ "$haystack" == *"$needle"* ]]; then
  56. pass "$name"
  57. else
  58. fail "$name" "output does not contain '$needle'"
  59. fi
  60. }
  61. # === jq Tests ===
  62. test_jq_extract_field() {
  63. if [[ $HAS_JQ != true ]]; then
  64. skip "jq: extract single field" "jq not installed"
  65. return
  66. fi
  67. local result
  68. result=$(echo '{"name": "test-app"}' | jq -r '.name')
  69. assert_eq "jq: extract single field" "test-app" "$result"
  70. }
  71. test_jq_nested_field() {
  72. [[ $HAS_JQ != true ]] && { skip "jq: extract nested field" "jq not installed"; return; }
  73. local result
  74. result=$(echo '{"scripts": {"build": "tsc"}}' | jq -r '.scripts.build')
  75. assert_eq "jq: extract nested field" "tsc" "$result"
  76. }
  77. test_jq_array_filter() {
  78. [[ $HAS_JQ != true ]] && { skip "jq: filter array by condition" "jq not installed"; return; }
  79. local result
  80. result=$(echo '{"users": [{"name": "Alice", "active": true}, {"name": "Bob", "active": false}]}' | jq -r '.users[] | select(.active == true) | .name')
  81. assert_eq "jq: filter array by condition" "Alice" "$result"
  82. }
  83. test_jq_array_length() {
  84. [[ $HAS_JQ != true ]] && { skip "jq: count array length" "jq not installed"; return; }
  85. local result
  86. result=$(echo '{"items": [1, 2, 3, 4, 5]}' | jq '.items | length')
  87. assert_eq "jq: count array length" "5" "$result"
  88. }
  89. test_jq_raw_output() {
  90. [[ $HAS_JQ != true ]] && { skip "jq: raw string output" "jq not installed"; return; }
  91. local quoted unquoted
  92. quoted=$(echo '{"name": "myapp"}' | jq '.name')
  93. unquoted=$(echo '{"name": "myapp"}' | jq -r '.name')
  94. if [[ "$quoted" == '"myapp"' && "$unquoted" == "myapp" ]]; then
  95. pass "jq: raw string output (-r flag)"
  96. else
  97. fail "jq: raw string output" "quoted='$quoted', unquoted='$unquoted'"
  98. fi
  99. }
  100. test_jq_map_transform() {
  101. [[ $HAS_JQ != true ]] && { skip "jq: map transformation" "jq not installed"; return; }
  102. local result
  103. result=$(echo '{"users": [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]}' | jq '[.users[] | {id, name}] | length')
  104. assert_eq "jq: map transformation" "2" "$result"
  105. }
  106. test_jq_package_json() {
  107. [[ $HAS_JQ != true ]] && { skip "jq: parse package.json" "jq not installed"; return; }
  108. if [[ -f "$FIXTURES/package.json" ]]; then
  109. local name version
  110. name=$(jq -r '.name' "$FIXTURES/package.json")
  111. version=$(jq -r '.version' "$FIXTURES/package.json")
  112. if [[ -n "$name" && -n "$version" ]]; then
  113. pass "jq: parse package.json fixture"
  114. else
  115. fail "jq: parse package.json" "name='$name', version='$version'"
  116. fi
  117. else
  118. skip "jq: parse package.json fixture" "fixture not found"
  119. fi
  120. }
  121. # === yq Tests ===
  122. test_yq_extract_field() {
  123. [[ $HAS_YQ != true ]] && { skip "yq: extract YAML field" "yq not installed"; return; }
  124. local result
  125. result=$(echo -e "name: myapp\nversion: 2.0.0" | yq -r '.name')
  126. assert_eq "yq: extract YAML field" "myapp" "$result"
  127. }
  128. test_yq_list_keys() {
  129. [[ $HAS_YQ != true ]] && { skip "yq: list keys count" "yq not installed"; return; }
  130. local result
  131. result=$(echo -e "database:\n host: localhost\n port: 5432" | yq '.database | keys | length')
  132. assert_eq "yq: list keys count" "2" "$result"
  133. }
  134. test_yq_docker_compose() {
  135. [[ $HAS_YQ != true ]] && { skip "yq: Docker Compose services" "yq not installed"; return; }
  136. local result
  137. result=$(echo -e "services:\n web:\n image: nginx\n db:\n image: postgres" | yq '.services | keys | length')
  138. assert_eq "yq: Docker Compose services" "2" "$result"
  139. }
  140. test_yq_toml_parsing() {
  141. [[ $HAS_YQ != true ]] && { skip "yq: TOML parsing" "yq not installed"; return; }
  142. local result
  143. result=$(echo -e '[package]\nname = "myapp"' | yq -p toml -r '.package.name')
  144. assert_eq "yq: TOML parsing" "myapp" "$result"
  145. }
  146. test_yq_config_fixture() {
  147. [[ $HAS_YQ != true ]] && { skip "yq: parse config.yaml" "yq not installed"; return; }
  148. if [[ -f "$FIXTURES/config.yaml" ]]; then
  149. local result
  150. result=$(yq -r '.name' "$FIXTURES/config.yaml")
  151. if [[ -n "$result" && "$result" != "null" ]]; then
  152. pass "yq: parse config.yaml fixture"
  153. else
  154. fail "yq: parse config.yaml" "got '$result'"
  155. fi
  156. else
  157. skip "yq: parse config.yaml fixture" "fixture not found"
  158. fi
  159. }
  160. # === Run Tests ===
  161. main() {
  162. echo "=== data-processing functional tests ==="
  163. echo ""
  164. check_prereqs
  165. echo "--- jq tests ---"
  166. test_jq_extract_field
  167. test_jq_nested_field
  168. test_jq_array_filter
  169. test_jq_array_length
  170. test_jq_raw_output
  171. test_jq_map_transform
  172. test_jq_package_json
  173. echo ""
  174. echo "--- yq tests ---"
  175. test_yq_extract_field
  176. test_yq_list_keys
  177. test_yq_docker_compose
  178. test_yq_toml_parsing
  179. test_yq_config_fixture
  180. echo ""
  181. echo "=== Results ==="
  182. echo -e "Passed: ${GREEN}$PASSED${NC}"
  183. echo -e "Failed: ${RED}$FAILED${NC}"
  184. echo -e "Skipped: ${YELLOW}$SKIPPED${NC}"
  185. [[ $FAILED -eq 0 ]]
  186. }
  187. main "$@"