test-non-interactive.sh 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. #!/usr/bin/env bash
  2. #############################################################################
  3. # Non-Interactive Mode Test Script
  4. # Tests the installer behavior when run via pipe (curl | bash)
  5. #
  6. # This test catches bugs like: https://github.com/darrenhinde/OpenAgentsControl/issues/XX
  7. # where interactive prompts fail silently in piped execution.
  8. #############################################################################
  9. set -e
  10. GREEN='\033[0;32m'
  11. RED='\033[0;31m'
  12. YELLOW='\033[1;33m'
  13. CYAN='\033[0;36m'
  14. BOLD='\033[1m'
  15. NC='\033[0m'
  16. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  17. REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
  18. TEST_DIR="/tmp/opencode-noninteractive-test-$$"
  19. PASSED=0
  20. FAILED=0
  21. pass() {
  22. echo -e "${GREEN}✓${NC} $1"
  23. PASSED=$((PASSED + 1))
  24. }
  25. fail() {
  26. echo -e "${RED}✗${NC} $1"
  27. FAILED=$((FAILED + 1))
  28. }
  29. warn() {
  30. echo -e "${YELLOW}⚠${NC} $1"
  31. }
  32. setup() {
  33. rm -rf "$TEST_DIR"
  34. mkdir -p "$TEST_DIR"
  35. }
  36. # shellcheck disable=SC2329
  37. cleanup() {
  38. rm -rf "$TEST_DIR"
  39. }
  40. trap cleanup EXIT
  41. print_header() {
  42. echo -e "${CYAN}${BOLD}"
  43. echo "╔════════════════════════════════════════════════════════════════╗"
  44. echo "║ Non-Interactive Mode Test Suite ║"
  45. echo "╚════════════════════════════════════════════════════════════════╝"
  46. echo -e "${NC}"
  47. }
  48. run_with_timeout() {
  49. local duration=$1
  50. shift
  51. if command -v timeout &> /dev/null; then
  52. timeout "$duration" "$@"
  53. elif command -v gtimeout &> /dev/null; then
  54. gtimeout "$duration" "$@"
  55. else
  56. # Fallback: run without timeout
  57. "$@"
  58. fi
  59. }
  60. #############################################################################
  61. # Test 1: Fresh install with piped input (simulates curl | bash)
  62. #############################################################################
  63. test_fresh_install_piped() {
  64. echo -e "\n${BOLD}Test 1: Fresh Install via Pipe${NC}"
  65. local install_dir="$TEST_DIR/fresh-install/.opencode"
  66. if echo "" | bash "$REPO_ROOT/install.sh" essential --install-dir="$install_dir" 2>&1 | grep -q "Installation complete"; then
  67. if [ -d "$install_dir" ]; then
  68. pass "Fresh install completed successfully via pipe"
  69. else
  70. fail "Install reported success but directory not created"
  71. fi
  72. else
  73. fail "Fresh install via pipe failed"
  74. fi
  75. }
  76. #############################################################################
  77. # Test 2: Install with existing files (collision scenario)
  78. #############################################################################
  79. test_collision_non_interactive() {
  80. echo -e "\n${BOLD}Test 2: Collision Handling in Non-Interactive Mode${NC}"
  81. local install_dir="$TEST_DIR/collision-test/.opencode"
  82. mkdir -p "$install_dir/agent/core"
  83. echo "existing content" > "$install_dir/agent/core/openagent.md"
  84. local output
  85. output=$(echo "" | bash "$REPO_ROOT/install.sh" essential --install-dir="$install_dir" 2>&1)
  86. if echo "$output" | grep -q "Installation cancelled by user"; then
  87. fail "BUG DETECTED: Non-interactive mode cancelled due to collision prompt"
  88. echo " This is the exact bug we're testing for!"
  89. return 1
  90. fi
  91. if echo "$output" | grep -q "skip.*strategy\|Skipped existing"; then
  92. pass "Collision handled correctly - used skip strategy"
  93. elif echo "$output" | grep -q "Installation complete"; then
  94. pass "Installation completed (no collision or handled silently)"
  95. else
  96. fail "Unexpected behavior during collision handling"
  97. echo " Output: $output"
  98. fi
  99. local original_content
  100. original_content=$(cat "$install_dir/agent/core/openagent.md" 2>/dev/null || echo "")
  101. if [ "$original_content" = "existing content" ]; then
  102. pass "Existing file preserved (skip strategy worked)"
  103. else
  104. warn "Existing file was overwritten (may be intentional in some modes)"
  105. fi
  106. }
  107. #############################################################################
  108. # Test 3: Essential profile in non-interactive mode (CI tests all profiles)
  109. #############################################################################
  110. test_profile_non_interactive() {
  111. echo -e "\n${BOLD}Test 3: Essential Profile Non-Interactive${NC}"
  112. local install_dir="$TEST_DIR/profile-essential/.opencode"
  113. local output
  114. output=$(echo "" | run_with_timeout 60 bash "$REPO_ROOT/install.sh" essential --install-dir="$install_dir" 2>&1) || true
  115. if echo "$output" | grep -q "Installation complete"; then
  116. pass "Profile 'essential' installed successfully"
  117. elif echo "$output" | grep -q "Installation cancelled"; then
  118. fail "Profile 'essential' failed - cancelled unexpectedly"
  119. else
  120. fail "Profile 'essential' had unexpected output"
  121. fi
  122. }
  123. #############################################################################
  124. # Test 4: Simulated curl | bash execution
  125. #############################################################################
  126. test_simulated_curl_pipe() {
  127. echo -e "\n${BOLD}Test 4: Simulated curl | bash Execution${NC}"
  128. local install_dir="$TEST_DIR/curl-sim/.opencode"
  129. cat "$REPO_ROOT/install.sh" | bash -s essential --install-dir="$install_dir" > "$TEST_DIR/curl-output.txt" 2>&1
  130. if grep -q "Installation complete\|Installed:" "$TEST_DIR/curl-output.txt"; then
  131. pass "Simulated 'curl | bash -s essential' worked"
  132. elif grep -q "cancelled" "$TEST_DIR/curl-output.txt"; then
  133. fail "Simulated curl pipe was cancelled unexpectedly"
  134. else
  135. fail "Simulated curl pipe had unexpected result"
  136. cat "$TEST_DIR/curl-output.txt"
  137. fi
  138. }
  139. #############################################################################
  140. # Test 5: stdin detection
  141. #############################################################################
  142. test_stdin_detection() {
  143. echo -e "\n${BOLD}Test 5: stdin Detection${NC}"
  144. if [ -t 0 ]; then
  145. pass "Running in terminal (stdin is TTY)"
  146. else
  147. warn "Running in non-interactive mode (stdin is not TTY)"
  148. fi
  149. local in_pipe
  150. in_pipe=$(echo "" | bash -c '[ -t 0 ] && echo "tty" || echo "pipe"')
  151. if [ "$in_pipe" = "pipe" ]; then
  152. pass "Pipe detection works correctly"
  153. else
  154. fail "Pipe detection failed"
  155. fi
  156. }
  157. #############################################################################
  158. # Test 6: NON_INTERACTIVE flag is set correctly
  159. #############################################################################
  160. test_non_interactive_flag() {
  161. echo -e "\n${BOLD}Test 6: NON_INTERACTIVE Flag Detection${NC}"
  162. local output
  163. output=$(bash "$REPO_ROOT/install.sh" essential --help 2>&1 | head -20)
  164. if echo "$output" | grep -q "Usage:"; then
  165. pass "Script executes and shows help"
  166. else
  167. fail "Script failed to show help"
  168. fi
  169. output=$(echo "" | bash "$REPO_ROOT/install.sh" essential --install-dir="$TEST_DIR/flag-test" 2>&1)
  170. if echo "$output" | grep -q "non-interactive\|automatically"; then
  171. pass "Non-interactive mode detected and reported"
  172. else
  173. pass "Installation proceeded (flag working silently)"
  174. fi
  175. }
  176. #############################################################################
  177. # Test 7: Error handling in non-interactive mode
  178. #############################################################################
  179. test_error_handling_non_interactive() {
  180. echo -e "\n${BOLD}Test 7: Error Handling in Non-Interactive Mode${NC}"
  181. local output
  182. output=$(echo "" | bash "$REPO_ROOT/install.sh" invalid_profile 2>&1) || true
  183. if echo "$output" | grep -q "Unknown option\|Invalid\|Error"; then
  184. pass "Invalid profile rejected with clear error"
  185. else
  186. fail "Invalid profile should produce error message"
  187. fi
  188. }
  189. #############################################################################
  190. # Main
  191. #############################################################################
  192. main() {
  193. print_header
  194. echo "Repository: $REPO_ROOT"
  195. echo "Test directory: $TEST_DIR"
  196. echo ""
  197. setup
  198. test_fresh_install_piped
  199. test_collision_non_interactive
  200. test_profile_non_interactive
  201. test_simulated_curl_pipe
  202. test_stdin_detection
  203. test_non_interactive_flag
  204. test_error_handling_non_interactive
  205. echo ""
  206. echo -e "${BOLD}═══════════════════════════════════════════════════════════════${NC}"
  207. echo -e "${BOLD}Test Summary${NC}"
  208. echo -e " ${GREEN}Passed: $PASSED${NC}"
  209. echo -e " ${RED}Failed: $FAILED${NC}"
  210. echo -e "${BOLD}═══════════════════════════════════════════════════════════════${NC}"
  211. if [ $FAILED -gt 0 ]; then
  212. echo -e "\n${RED}Some tests failed!${NC}"
  213. exit 1
  214. else
  215. echo -e "\n${GREEN}All tests passed!${NC}"
  216. exit 0
  217. fi
  218. }
  219. main "$@"