|
| 1 | +#!/bin/bash |
| 2 | +# Test: Sessions that fail with 401 and have no OAuth profile stay 'unauthorized' |
| 3 | +# across `mcpc` invocations, even after the auto-retry cooldown has elapsed. |
| 4 | +# Also verifies the auth error message points at the bridge log file. |
| 5 | + |
| 6 | +source "$(dirname "$0")/../../lib/framework.sh" |
| 7 | +test_init "sessions/unauthorized-persist" --isolated |
| 8 | + |
| 9 | +# Start test server requiring auth. The server's auth check accepts any |
| 10 | +# "Bearer <anything>" value, so sending an Authorization header that doesn't |
| 11 | +# match that shape is enough to trigger 401. |
| 12 | +start_test_server REQUIRE_AUTH=true |
| 13 | + |
| 14 | +SESSION=$(session_name "unauth") |
| 15 | +_SESSIONS_CREATED+=("$SESSION") |
| 16 | + |
| 17 | +# ============================================================================= |
| 18 | +# Test: bearer-only session fails with auth error containing log path |
| 19 | +# ============================================================================= |
| 20 | + |
| 21 | +test_case "connect with bad bearer token fails with auth error + log path" |
| 22 | +run_mcpc connect "$TEST_SERVER_URL" "$SESSION" \ |
| 23 | + --header "X-Test: true" \ |
| 24 | + --header "Authorization: InvalidScheme not-a-bearer-token" |
| 25 | +assert_failure |
| 26 | +assert_exit_code 4 "should fail with auth exit code (4)" |
| 27 | +assert_contains "$STDERR" "Authentication required by server" |
| 28 | +# The error should point at the bridge log file so the user can investigate |
| 29 | +assert_contains "$STDERR" "check logs at" |
| 30 | +assert_contains "$STDERR" "bridge-${SESSION}.log" |
| 31 | +test_pass |
| 32 | + |
| 33 | +# ============================================================================= |
| 34 | +# Test: session is marked unauthorized right after the failed connect |
| 35 | +# ============================================================================= |
| 36 | + |
| 37 | +test_case "failed bearer session reports 'unauthorized' status" |
| 38 | +run_mcpc --json |
| 39 | +assert_success |
| 40 | +session_status=$(echo "$STDOUT" | jq -r ".sessions[] | select(.name == \"$SESSION\") | .status") |
| 41 | +if [[ "$session_status" != "unauthorized" ]]; then |
| 42 | + test_fail "expected status 'unauthorized', got: '$session_status'" |
| 43 | + exit 1 |
| 44 | +fi |
| 45 | +test_pass |
| 46 | + |
| 47 | +# ============================================================================= |
| 48 | +# Test: unauthorized bearer session does NOT flip back to 'connecting' |
| 49 | +# after the auto-retry cooldown expires. |
| 50 | +# |
| 51 | +# Before the fix, consolidateSessions() treated all unauthorized sessions as |
| 52 | +# retry candidates and reset the status to 'connecting' on every `mcpc` call |
| 53 | +# (after the 10s cooldown), which both hid the real state from the user and |
| 54 | +# triggered pointless background bridge restarts. Bearer-only sessions cannot |
| 55 | +# self-heal because the token never changes. |
| 56 | +# ============================================================================= |
| 57 | + |
| 58 | +test_case "unauthorized bearer session stays unauthorized past the cooldown" |
| 59 | +sessions_file="$MCPC_HOME_DIR/sessions.json" |
| 60 | +assert_file_exists "$sessions_file" |
| 61 | + |
| 62 | +# Age the lastConnectionAttemptAt timestamp well past the 10s cooldown and |
| 63 | +# clear the bridge pid to simulate the bridge having exited — that's the real |
| 64 | +# state consolidateSessions() sees when the user runs `mcpc` well after the |
| 65 | +# original failed connect. Leaving pid set would short-circuit the retry path |
| 66 | +# via `!session.pid`, hiding the bug we are trying to catch. |
| 67 | +old_iso="2000-01-01T00:00:00.000Z" |
| 68 | +tmp_file="$TEST_TMP/sessions.json.$$" |
| 69 | +jq --arg name "$SESSION" --arg when "$old_iso" \ |
| 70 | + '.sessions[$name].lastConnectionAttemptAt = $when |
| 71 | + | del(.sessions[$name].pid)' \ |
| 72 | + "$sessions_file" > "$tmp_file" |
| 73 | +mv "$tmp_file" "$sessions_file" |
| 74 | + |
| 75 | +# Re-run the list command — consolidateSessions() runs on every list. |
| 76 | +run_mcpc --json |
| 77 | +assert_success |
| 78 | +session_status=$(echo "$STDOUT" | jq -r ".sessions[] | select(.name == \"$SESSION\") | .status") |
| 79 | +if [[ "$session_status" != "unauthorized" ]]; then |
| 80 | + test_fail "expected status to stay 'unauthorized' after cooldown, got: '$session_status'" |
| 81 | + exit 1 |
| 82 | +fi |
| 83 | +test_pass |
| 84 | + |
| 85 | +# ============================================================================= |
| 86 | +# Test: using an unauthorized session surfaces the auth error + log path |
| 87 | +# ============================================================================= |
| 88 | + |
| 89 | +test_case "using unauthorized session surfaces auth error with log path" |
| 90 | +run_mcpc "$SESSION" tools-list |
| 91 | +assert_failure |
| 92 | +assert_exit_code 4 "should fail with auth exit code (4)" |
| 93 | +assert_contains "$STDERR" "Authentication required by server" |
| 94 | +assert_contains "$STDERR" "check logs at" |
| 95 | +assert_contains "$STDERR" "bridge-${SESSION}.log" |
| 96 | +test_pass |
| 97 | + |
| 98 | +test_done |
0 commit comments