test-non-interactive.sh 9.2 KB

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