agentic-loop 3.1.6 → 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -306,7 +306,7 @@ For frontend stories, acceptance criteria MUST include:
306
306
  - "Required elements render" (specify which: header, form, button, etc.)
307
307
  - "Works on mobile viewport (375px)"
308
308
 
309
- These get verified by Playwright, not just code review.
309
+ These get verified by Playwright tests and MCP browser tools.
310
310
 
311
311
  ### Test Steps - CRITICAL
312
312
  **Test steps MUST be executable shell commands.** Ralph runs them with bash.
@@ -352,7 +352,7 @@ The Playwright test file can check:
352
352
  ]
353
353
  ```
354
354
 
355
- **If a step can't be automated**, leave it out of testSteps and put it in acceptanceCriteria instead. Ralph will verify acceptanceCriteria via code review, not by running commands.
355
+ **If a step can't be automated**, leave it out of testSteps and put it in acceptanceCriteria instead. Claude will verify acceptanceCriteria visually using MCP browser tools.
356
356
 
357
357
  ### Architecture Guidelines
358
358
  - **Domain-driven directories** - Group by feature (`src/contact/`) not type (`src/components/`)
@@ -283,4 +283,4 @@ The Playwright test file can check:
283
283
  ]
284
284
  ```
285
285
 
286
- **If a step can't be automated**, leave it out of testSteps and put it in acceptanceCriteria instead. Ralph will verify acceptanceCriteria via code review, not by running commands.
286
+ **If a step can't be automated**, leave it out of testSteps and put it in acceptanceCriteria instead. Claude will verify acceptanceCriteria visually using MCP browser tools.
package/README.md CHANGED
@@ -45,8 +45,7 @@ claude --dangerously-skip-permissions
45
45
 
46
46
  **Terminal 2 - Ralph Loop:**
47
47
  ```bash
48
- npx agentic-loop run # Execute PRDs autonomously
49
- npx agentic-loop run --fast # Skip code review (~2x faster)
48
+ npx agentic-loop run # Execute PRDs autonomously
50
49
  ```
51
50
 
52
51
  > **Tip:** Use two terminals. Plan with Claude in one, run Ralph in the other.
@@ -61,8 +60,8 @@ npx agentic-loop run --fast # Skip code review (~2x faster)
61
60
  ├─────────────────────────────────────────────────────────────┤
62
61
  │ 1. Read prd.json → find next story where passes=false │
63
62
  │ 2. Build prompt (story + context + failures + signs) │
64
- │ 3. Spawn Claude with prompt
65
- │ 4. Run verification (lint, tests, browser, code review)
63
+ │ 3. Spawn Claude with prompt + MCP browser tools
64
+ │ 4. Run verification (lint, tests, testSteps)
66
65
  │ 5. Pass? → commit, next story │
67
66
  │ Fail? → save error, retry with failure context │
68
67
  │ 6. Repeat until all stories pass │
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-loop",
3
- "version": "3.1.6",
3
+ "version": "3.2.1",
4
4
  "description": "Autonomous AI coding loop - PRD-driven development with Claude Code",
5
5
  "author": "Allie Jones <allie@allthrive.ai>",
6
6
  "license": "MIT",
package/ralph/setup.sh CHANGED
@@ -340,7 +340,7 @@ EOF
340
340
  fi
341
341
  }
342
342
 
343
- # Configure MCP (Chrome DevTools)
343
+ # Configure MCP (Browser tools for verification)
344
344
  setup_mcp() {
345
345
  local claude_json="$HOME/.claude.json"
346
346
 
@@ -350,17 +350,82 @@ setup_mcp() {
350
350
  # Create claude.json if it doesn't exist
351
351
  [[ ! -f "$claude_json" ]] && echo '{}' > "$claude_json"
352
352
 
353
- # Skip if already configured
354
- jq -e '.mcpServers["chrome-devtools"]' "$claude_json" > /dev/null 2>&1 && return 0
353
+ local added_any=false
354
+
355
+ # Add Playwright MCP if not configured
356
+ if ! jq -e '.mcpServers["playwright"]' "$claude_json" > /dev/null 2>&1; then
357
+ echo "Configuring MCP servers..."
358
+ local tmp
359
+ tmp=$(mktemp)
360
+ jq '.mcpServers["playwright"] = {
361
+ "command": "npx",
362
+ "args": ["-y", "@anthropic-ai/mcp-server-playwright"]
363
+ }' "$claude_json" > "$tmp" && mv "$tmp" "$claude_json"
364
+ echo " Added playwright MCP server (browser automation & testing)"
365
+ added_any=true
366
+ fi
355
367
 
356
- echo "Configuring MCP servers..."
357
- local tmp
358
- tmp=$(mktemp)
359
- jq '.mcpServers["chrome-devtools"] = {
360
- "command": "npx",
361
- "args": ["-y", "@anthropic-ai/mcp-server-chrome-devtools@0.0.5"]
362
- }' "$claude_json" > "$tmp" && mv "$tmp" "$claude_json"
363
- echo " Added chrome-devtools MCP server"
368
+ # Add Chrome DevTools MCP if not configured
369
+ if ! jq -e '.mcpServers["chrome-devtools"]' "$claude_json" > /dev/null 2>&1; then
370
+ [[ "$added_any" == "false" ]] && echo "Configuring MCP servers..."
371
+ local tmp
372
+ tmp=$(mktemp)
373
+ jq '.mcpServers["chrome-devtools"] = {
374
+ "command": "npx",
375
+ "args": ["-y", "@anthropic-ai/mcp-server-chrome-devtools@0.0.5"]
376
+ }' "$claude_json" > "$tmp" && mv "$tmp" "$claude_json"
377
+ echo " Added chrome-devtools MCP server (debugging & inspection)"
378
+ added_any=true
379
+ fi
380
+
381
+ # Ask about test credentials
382
+ if [[ "$added_any" == "true" ]]; then
383
+ setup_test_credentials
384
+ fi
385
+ }
386
+
387
+ # Set up test credentials for browser automation
388
+ setup_test_credentials() {
389
+ echo ""
390
+ echo " Browser automation often needs login credentials for testing."
391
+ echo ""
392
+ read -r -p " Do you have test user credentials to configure? [y/N] " response
393
+
394
+ if [[ "$response" =~ ^[Yy]$ ]]; then
395
+ echo ""
396
+ echo " These will be stored in .env (already in .gitignore - never committed)"
397
+ echo ""
398
+
399
+ # Create .env if it doesn't exist
400
+ if [[ ! -f ".env" ]]; then
401
+ touch ".env"
402
+ echo " Created .env file"
403
+ fi
404
+
405
+ # Get credentials
406
+ read -r -p " Test user email: " test_email
407
+ read -r -s -p " Test user password: " test_password
408
+ echo ""
409
+
410
+ # Add to .env (append or update)
411
+ if grep -q "^RALPH_TEST_USER=" .env 2>/dev/null; then
412
+ # Update existing
413
+ local tmp
414
+ tmp=$(mktemp)
415
+ sed "s/^RALPH_TEST_USER=.*/RALPH_TEST_USER=$test_email/" .env > "$tmp" && mv "$tmp" .env
416
+ sed "s/^RALPH_TEST_PASSWORD=.*/RALPH_TEST_PASSWORD=$test_password/" .env > "$tmp" && mv "$tmp" .env
417
+ else
418
+ # Append new
419
+ echo "" >> .env
420
+ echo "# Test credentials for browser automation (auto-added by agentic-loop)" >> .env
421
+ echo "RALPH_TEST_USER=$test_email" >> .env
422
+ echo "RALPH_TEST_PASSWORD=$test_password" >> .env
423
+ fi
424
+
425
+ echo " Saved credentials to .env"
426
+ echo ""
427
+ echo " Note: .env is gitignored - your password will never be committed to git."
428
+ fi
364
429
  }
365
430
 
366
431
  # Set up pre-commit hooks
package/ralph/verify.sh CHANGED
@@ -1,35 +1,14 @@
1
1
  #!/usr/bin/env bash
2
2
  # shellcheck shell=bash
3
- # verify.sh - Full UAT verification pipeline for ralph
3
+ # verify.sh - Lightweight verification for ralph
4
4
  #
5
- # This file orchestrates the verification pipeline by sourcing modular components:
6
- # - verify/review.sh - Code review logic
7
- # - verify/lint.sh - Auto-fix + lint checks
8
- # - verify/tests.sh - Unit tests + PRD criteria
9
- # - verify/browser.sh - Browser validation
10
-
11
- # Validate required source files exist before sourcing
12
- if [[ ! -f "$RALPH_LIB/playwright.sh" ]]; then
13
- echo "Error: Missing $RALPH_LIB/playwright.sh" >&2
14
- exit 1
15
- fi
16
- if [[ ! -f "$RALPH_LIB/api.sh" ]]; then
17
- echo "Error: Missing $RALPH_LIB/api.sh" >&2
18
- exit 1
19
- fi
5
+ # Philosophy: Claude verifies its own work using MCP browser tools.
6
+ # Ralph just runs lint, tests, and testSteps from the PRD.
20
7
 
21
8
  # Source verification modules
22
- source "$RALPH_LIB/playwright.sh"
23
- source "$RALPH_LIB/api.sh"
24
-
25
- # Determine the directory where this script lives
26
9
  VERIFY_DIR="${RALPH_LIB:-$(dirname "${BASH_SOURCE[0]}")}"
27
-
28
- # Source modular verification components
29
- source "$VERIFY_DIR/verify/review.sh"
30
10
  source "$VERIFY_DIR/verify/lint.sh"
31
11
  source "$VERIFY_DIR/verify/tests.sh"
32
- source "$VERIFY_DIR/verify/browser.sh"
33
12
 
34
13
  run_verification() {
35
14
  local story="$1"
@@ -38,142 +17,33 @@ run_verification() {
38
17
  print_info "=== Verification: $story ==="
39
18
  echo ""
40
19
 
41
- # Clear old failure logs (so smart-skip logic uses fresh state)
42
- rm -f "$RALPH_DIR/last_precommit_failure.log" \
43
- "$RALPH_DIR/last_test_failure.log" \
44
- "$RALPH_DIR/last_review_failure.json" 2>/dev/null
45
-
46
- # Check for fast mode
47
- local fast_mode="${RALPH_FAST_MODE:-false}"
48
- if [[ "$fast_mode" == "true" ]]; then
49
- echo " (fast mode - skipping code review)"
50
- fi
51
-
52
- # Determine story type (single jq call for all story info)
53
- local story_json
54
- story_json=$(jq --arg id "$story" '.stories[] | select(.id==$id)' "$RALPH_DIR/prd.json" 2>/dev/null)
55
-
56
- local story_type has_test_url has_api_endpoints
57
- story_type=$(echo "$story_json" | jq -r '.type // "frontend"')
58
- has_test_url=$(echo "$story_json" | jq -r '.testUrl // empty')
59
- has_api_endpoints=$(echo "$story_json" | jq -r '.apiEndpoints[0] // empty')
60
-
61
- # Auto-detect type if not specified
62
- if [[ -n "$has_api_endpoints" && -z "$has_test_url" ]]; then
63
- story_type="backend"
64
- fi
65
-
66
20
  local failed=0
67
- local lint_failed=0
68
- local test_failed=0
69
21
 
70
22
  # ========================================
71
- # STEP 1: Code review (skip in fast mode or if last failure was lint/test)
23
+ # STEP 1: Run lint checks
72
24
  # ========================================
73
- local skip_review=false
74
- if [[ "$fast_mode" == "true" ]]; then
75
- skip_review=true
76
- elif [[ -f "$RALPH_DIR/last_precommit_failure.log" ]] || [[ -f "$RALPH_DIR/last_test_failure.log" ]]; then
77
- # Skip review if last failure was lint/test - review won't help
78
- skip_review=true
79
- echo " [1/5] Skipping code review (last failure was lint/test)"
80
- fi
81
-
82
- if [[ "$skip_review" == "false" ]]; then
83
- echo " [1/5] Running code review..."
84
- if ! run_code_review "$story"; then
85
- failed=1
86
- fi
25
+ echo " [1/3] Running lint checks..."
26
+ if ! run_configured_checks; then
27
+ failed=1
87
28
  fi
88
29
 
89
30
  # ========================================
90
- # STEP 2+3: Run lint and tests IN PARALLEL
31
+ # STEP 2: Run unit tests
91
32
  # ========================================
92
33
  if [[ $failed -eq 0 ]]; then
93
34
  echo ""
94
- echo " [2/5] Running lint + tests (parallel)..."
95
-
96
- # Create temp files for results
97
- local lint_log test_log
98
- lint_log=$(mktemp)
99
- test_log=$(mktemp)
100
-
101
- # Run lint in background
102
- (run_configured_checks > "$lint_log" 2>&1; echo $? > "${lint_log}.exit") &
103
- local lint_pid=$!
104
-
105
- # Run tests in background
106
- (run_unit_tests > "$test_log" 2>&1; echo $? > "${test_log}.exit") &
107
- local test_pid=$!
108
-
109
- # Wait for both
110
- wait $lint_pid 2>/dev/null
111
- wait $test_pid 2>/dev/null
112
-
113
- # Check results
114
- lint_failed=$(cat "${lint_log}.exit" 2>/dev/null || echo "1")
115
- test_failed=$(cat "${test_log}.exit" 2>/dev/null || echo "1")
116
-
117
- # Show lint output
118
- echo " Lint:"
119
- cat "$lint_log" | sed 's/^/ /'
120
-
121
- # Show test output
122
- echo " Tests:"
123
- cat "$test_log" | sed 's/^/ /'
124
-
125
- # Cleanup
126
- rm -f "$lint_log" "${lint_log}.exit" "$test_log" "${test_log}.exit"
127
-
128
- if [[ "$lint_failed" != "0" ]] || [[ "$test_failed" != "0" ]]; then
35
+ echo " [2/3] Running tests..."
36
+ if ! run_unit_tests; then
129
37
  failed=1
130
38
  fi
131
39
  fi
132
40
 
133
41
  # ========================================
134
- # STEP 3: Run Playwright tests (frontend) or API tests (backend)
135
- # ========================================
136
- if [[ $failed -eq 0 ]]; then
137
- echo ""
138
- if [[ "$story_type" == "backend" ]]; then
139
- echo " [3/5] Running API tests..."
140
- if ! run_api_validation "$story"; then
141
- failed=1
142
- elif ! run_api_error_tests "$story"; then
143
- failed=1
144
- fi
145
- else
146
- echo " [3/5] Running Playwright tests..."
147
- if ! run_playwright_tests "$story"; then
148
- failed=1
149
- fi
150
- fi
151
- fi
152
-
153
- # ========================================
154
- # STEP 4: Run browser validation (frontend) or API validation (backend)
155
- # ========================================
156
- if [[ $failed -eq 0 ]]; then
157
- echo ""
158
- if [[ "$story_type" == "backend" ]]; then
159
- echo " [4/5] Running API validation..."
160
- if ! run_api_tests "$story"; then
161
- failed=1
162
- fi
163
- else
164
- echo " [4/5] Running browser validation..."
165
- if ! run_browser_validation "$story"; then
166
- failed=1
167
- fi
168
- fi
169
- fi
170
-
171
- # ========================================
172
- # STEP 5: Run PRD test steps
42
+ # STEP 3: Run PRD test steps
173
43
  # ========================================
174
44
  if [[ $failed -eq 0 ]]; then
175
45
  echo ""
176
- echo " [5/5] Running PRD test steps..."
46
+ echo " [3/3] Running PRD test steps..."
177
47
  if ! verify_prd_criteria "$story"; then
178
48
  failed=1
179
49
  fi
@@ -188,7 +58,6 @@ run_verification() {
188
58
  return 0
189
59
  else
190
60
  print_error "=== Verification failed ==="
191
- # Save failure context for next iteration
192
61
  save_failure_context "$story"
193
62
  return 1
194
63
  fi
@@ -14,36 +14,38 @@ Before writing any code, verify:
14
14
 
15
15
  For each story, you must:
16
16
 
17
- ### 1. Write Tests First
17
+ ### 1. Implement the Feature
18
18
 
19
- **For frontend stories:**
20
- - Write a Playwright test that validates the acceptance criteria
21
- - Include tests for error handling (API fails, validation errors)
22
- - Include tests for empty/loading states
23
- - Include accessibility checks (axe-core)
24
- - Include mobile viewport test (375px)
25
-
26
- **For backend stories:**
27
- - Write unit tests for the business logic
28
- - Write API tests that validate all endpoints
29
- - Test error responses (400, 401, 500)
30
- - Test validation rules
31
-
32
- ### 2. Implement the Feature
33
-
34
- - Write code to make all tests pass
35
19
  - Follow existing patterns in the codebase
36
20
  - Handle ALL error cases defined in the story
37
21
  - Implement loading states for async operations
38
22
 
23
+ ### 2. Write Tests
24
+
25
+ - Write unit tests for the business logic
26
+ - Write tests that validate acceptance criteria
27
+ - Test error cases and edge cases
28
+
39
29
  ### 3. Verify It Actually Works
40
30
 
31
+ **You have browser tools - USE THEM to verify your work:**
32
+
33
+ **Playwright MCP** (testing & automation):
34
+ - `browser_navigate` - Go to a URL and get page content
35
+ - `browser_screenshot` - Take a screenshot to verify UI
36
+ - `browser_click` - Click elements to test interactions
37
+ - `browser_type` - Fill in forms to test inputs
38
+ - `browser_snapshot` - Get accessibility tree for a11y testing
39
+
40
+ **Chrome DevTools MCP** (debugging & inspection):
41
+ - Inspect DOM, check console for errors
42
+ - Debug network requests
43
+ - Check element styles and computed properties
44
+
41
45
  **Do NOT say you're done until:**
42
46
  - All unit tests pass
43
- - All Playwright tests pass
44
- - You've opened the browser via MCP and visually verified
47
+ - You've opened the browser and visually verified the feature works
45
48
  - Console has no errors
46
- - It works on mobile (375px viewport)
47
49
  - Error states are handled gracefully
48
50
 
49
51
  ## Rules
@@ -60,33 +62,14 @@ For each story, you must:
60
62
 
61
63
  Before considering any story complete:
62
64
 
63
- ### Code
64
65
  - [ ] All acceptance criteria are met
65
66
  - [ ] All error handling from story is implemented
66
- - [ ] Loading states implemented (if frontend)
67
- - [ ] Validation implemented (if backend)
68
- - [ ] TypeScript compiles without errors
69
-
70
- ### Tests
67
+ - [ ] TypeScript/code compiles without errors
71
68
  - [ ] Unit tests written and passing
72
- - [ ] Playwright test written and passing (frontend)
73
- - [ ] API tests written and passing (backend)
74
- - [ ] Error cases tested
75
- - [ ] Edge cases tested (empty state, etc.)
76
-
77
- ### Browser/API Validation
78
- - [ ] Browser check passes (frontend) - no console errors
79
- - [ ] Mobile viewport works (375px)
80
- - [ ] Accessibility passes (can Tab through, focus visible)
81
- - [ ] API returns correct responses (backend)
82
-
83
- ### Documentation
84
- - [ ] Updated `.ralph/progress.txt` with files created/modified
85
- - [ ] Noted any key decisions or context for next story
86
-
87
- ### Quality
69
+ - [ ] **Browser verified** - used Playwright MCP to visually confirm it works
70
+ - [ ] No console errors
88
71
  - [ ] Linting passes
89
- - [ ] Existing tests still pass
72
+ - [ ] Updated `.ralph/progress.txt` with files created/modified
90
73
 
91
74
  ## If Verification Fails
92
75
 
package/ralph/api.sh DELETED
@@ -1,216 +0,0 @@
1
- #!/usr/bin/env bash
2
- # shellcheck shell=bash
3
- # api.sh - API validation for backend stories
4
-
5
- # Parse an endpoint string into method and path
6
- # Usage: parse_endpoint "POST /api/users" "http://localhost:3000"
7
- # Sets: ENDPOINT_METHOD, ENDPOINT_PATH, ENDPOINT_URL
8
- parse_endpoint() {
9
- local endpoint="$1"
10
- local base_url="${2:-}"
11
-
12
- # Defaults
13
- ENDPOINT_METHOD="GET"
14
- ENDPOINT_PATH="$endpoint"
15
- ENDPOINT_URL=""
16
-
17
- # Parse method if present (e.g., "POST /api/contact")
18
- if [[ "$endpoint" =~ ^(GET|POST|PUT|PATCH|DELETE)[[:space:]]+(.*) ]]; then
19
- ENDPOINT_METHOD="${BASH_REMATCH[1]}"
20
- ENDPOINT_PATH="${BASH_REMATCH[2]}"
21
- fi
22
-
23
- # Build full URL
24
- if [[ "$ENDPOINT_PATH" =~ ^https?:// ]]; then
25
- ENDPOINT_URL="$ENDPOINT_PATH"
26
- elif [[ -n "$base_url" ]]; then
27
- ENDPOINT_URL="${base_url}${ENDPOINT_PATH}"
28
- fi
29
- }
30
-
31
- # Check if endpoint is a WebSocket (can't be tested with HTTP)
32
- is_websocket_endpoint() {
33
- local endpoint="$1"
34
- [[ "$endpoint" =~ ^wss?:// ]] || [[ "$endpoint" =~ ^(GET|POST|PUT|PATCH|DELETE)[[:space:]]+wss?:// ]]
35
- }
36
-
37
- # Validate API endpoints for a backend story
38
- run_api_validation() {
39
- local story="$1"
40
-
41
- # Get API endpoints from story
42
- local endpoints
43
- endpoints=$(jq -r --arg id "$story" '.stories[] | select(.id==$id) | .apiEndpoints[]?' "$RALPH_DIR/prd.json" 2>/dev/null)
44
-
45
- if [[ -z "$endpoints" ]]; then
46
- echo " (no apiEndpoints defined, skipping API validation)"
47
- return 0
48
- fi
49
-
50
- # Get base URL from config or use default
51
- local base_url
52
- base_url=$(get_config '.api.baseUrl' "http://localhost:3000")
53
-
54
- local failed=0
55
-
56
- echo " Validating API endpoints..."
57
-
58
- while IFS= read -r endpoint; do
59
- [[ -z "$endpoint" ]] && continue
60
-
61
- # Skip WebSocket endpoints - they can't be tested with HTTP curl
62
- if is_websocket_endpoint "$endpoint"; then
63
- echo " Skipping WebSocket endpoint: $endpoint (use integration tests)"
64
- continue
65
- fi
66
-
67
- # Parse endpoint into method, path, and full URL
68
- parse_endpoint "$endpoint" "$base_url"
69
-
70
- echo -n " $ENDPOINT_METHOD $ENDPOINT_PATH... "
71
-
72
- # Make the request
73
- local response_code
74
- response_code=$(curl -sf -m "$CURL_TIMEOUT_SECONDS" -o /dev/null -w "%{http_code}" -X "$ENDPOINT_METHOD" "$ENDPOINT_URL" 2>/dev/null)
75
-
76
- if [[ "$response_code" =~ ^2[0-9][0-9]$ ]]; then
77
- print_success "$response_code"
78
- elif [[ "$response_code" == "000" ]]; then
79
- print_error "connection failed"
80
- failed=1
81
- elif [[ "$response_code" == "422" || "$response_code" == "400" ]]; then
82
- # 422/400 = endpoint exists but needs params - don't fail verification
83
- print_warning "$response_code (needs params)"
84
- elif [[ "$response_code" == "401" || "$response_code" == "403" ]]; then
85
- # Auth required - endpoint exists
86
- print_warning "$response_code (auth required)"
87
- else
88
- print_error "$response_code"
89
- failed=1
90
- fi
91
- done <<< "$endpoints"
92
-
93
- return $failed
94
- }
95
-
96
- # Run comprehensive API tests for a story
97
- run_api_tests() {
98
- local story="$1"
99
-
100
- # Get test steps that look like API calls
101
- local test_steps
102
- test_steps=$(jq -r --arg id "$story" '.stories[] | select(.id==$id) | .testSteps[]?' "$RALPH_DIR/prd.json" 2>/dev/null)
103
-
104
- if [[ -z "$test_steps" ]]; then
105
- return 0
106
- fi
107
-
108
- local failed=0
109
- local log_file
110
- log_file=$(create_temp_file ".log") || return 1
111
-
112
- echo " Running API test steps..."
113
-
114
- while IFS= read -r step; do
115
- [[ -z "$step" ]] && continue
116
-
117
- # Check if this looks like a curl command or API test
118
- if [[ "$step" =~ ^curl ]]; then
119
- echo -n " $step... "
120
-
121
- if safe_exec "$step" "$log_file"; then
122
- print_success "passed"
123
- else
124
- print_error "failed"
125
- echo ""
126
- echo " Response:"
127
- tail -"$MAX_OUTPUT_PREVIEW_LINES" "$log_file" | sed 's/^/ /'
128
- failed=1
129
- fi
130
- fi
131
- done <<< "$test_steps"
132
-
133
- rm -f "$log_file"
134
- return $failed
135
- }
136
-
137
- # Validate error handling for API
138
- run_api_error_tests() {
139
- local story="$1"
140
-
141
- # Get error handling requirements
142
- local error_handling
143
- error_handling=$(jq -r --arg id "$story" '.stories[] | select(.id==$id) | .errorHandling[]?' "$RALPH_DIR/prd.json" 2>/dev/null)
144
-
145
- if [[ -z "$error_handling" ]]; then
146
- return 0
147
- fi
148
-
149
- # Get base URL and endpoints
150
- local base_url
151
- base_url=$(get_config '.api.baseUrl' "http://localhost:3000")
152
-
153
- local endpoints
154
- endpoints=$(jq -r --arg id "$story" '.stories[] | select(.id==$id) | .apiEndpoints[0]?' "$RALPH_DIR/prd.json" 2>/dev/null)
155
-
156
- if [[ -z "$endpoints" ]]; then
157
- return 0
158
- fi
159
-
160
- # Skip WebSocket endpoints
161
- if is_websocket_endpoint "$endpoints"; then
162
- echo " Skipping error tests for WebSocket endpoint"
163
- return 0
164
- fi
165
-
166
- # Parse endpoint (default to POST for error tests)
167
- parse_endpoint "$endpoints" "$base_url"
168
- [[ "$ENDPOINT_METHOD" == "GET" ]] && ENDPOINT_METHOD="POST"
169
-
170
- local failed=0
171
-
172
- echo " Testing API error handling..."
173
-
174
- # Test common error cases
175
- while IFS= read -r error_case; do
176
- [[ -z "$error_case" ]] && continue
177
-
178
- # Check for 400 tests (bad input)
179
- if [[ "$error_case" =~ 400 ]]; then
180
- echo -n " Testing 400 (bad request)... "
181
-
182
- local response_code
183
- response_code=$(curl -sf -m "$CURL_TIMEOUT_SECONDS" -o /dev/null -w "%{http_code}" \
184
- -X "$ENDPOINT_METHOD" \
185
- -H "Content-Type: application/json" \
186
- -d '{}' \
187
- "$ENDPOINT_URL" 2>/dev/null)
188
-
189
- if [[ "$response_code" == "400" ]]; then
190
- print_success "correctly returns 400"
191
- else
192
- print_warning "got $response_code (expected 400)"
193
- failed=1
194
- fi
195
- fi
196
-
197
- # Check for 401 tests (unauthorized)
198
- if [[ "$error_case" =~ 401 ]]; then
199
- echo -n " Testing 401 (unauthorized)... "
200
-
201
- local response_code
202
- response_code=$(curl -sf -m "$CURL_TIMEOUT_SECONDS" -o /dev/null -w "%{http_code}" \
203
- -X "$ENDPOINT_METHOD" \
204
- "$ENDPOINT_URL" 2>/dev/null)
205
-
206
- if [[ "$response_code" == "401" ]]; then
207
- print_success "correctly returns 401"
208
- else
209
- print_warning "got $response_code (expected 401)"
210
- failed=1
211
- fi
212
- fi
213
- done <<< "$error_handling"
214
-
215
- return $failed
216
- }