fix: cap negotiated timeout extensions to external hard timeout ceili… #1817
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Integration Tests | |
| on: | |
| push: | |
| branches: [ main ] | |
| pull_request: | |
| branches: [ main ] | |
| jobs: | |
| actionlint: | |
| name: Lint GitHub Actions workflows | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Download actionlint | |
| id: get_actionlint | |
| run: | | |
| bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) | |
| echo "executable=./actionlint" >> "$GITHUB_OUTPUT" | |
| shell: bash | |
| - name: Check workflow files | |
| run: ${{ steps.get_actionlint.outputs.executable }} -color | |
| shell: bash | |
| integration-test: | |
| name: Test npm installation on ${{ matrix.os }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| matrix: | |
| os: [ubuntu-latest, windows-latest, macos-latest] | |
| node-version: [20] | |
| fail-fast: false | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js ${{ matrix.node-version }} | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ matrix.node-version }} | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@1.92.0 | |
| with: | |
| components: rustfmt, clippy | |
| - name: Setup Rust cache | |
| uses: actions/cache@v4 | |
| timeout-minutes: 5 | |
| with: | |
| path: | | |
| ~/.cargo/bin/ | |
| ~/.cargo/registry/index/ | |
| ~/.cargo/registry/cache/ | |
| ~/.cargo/git/db/ | |
| target/ | |
| key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}-${{ hashFiles('rust-toolchain', 'rust-toolchain.toml') || 'stable' }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}- | |
| ${{ runner.os }}-cargo- | |
| - name: Build Rust binary (debug mode for CI speed) | |
| run: cargo build | |
| - name: Prepare npm package with local binary | |
| shell: bash | |
| run: | | |
| # Create bin directory in npm package | |
| mkdir -p npm/bin | |
| # Copy the built binary to npm package bin directory with correct naming | |
| # On Unix, we need to preserve the Node.js wrapper script (probe) and put the binary as probe-binary | |
| if [[ "${{ runner.os }}" == "Windows" ]]; then | |
| cp target/debug/probe.exe npm/bin/probe.exe | |
| else | |
| cp target/debug/probe npm/bin/probe-binary | |
| chmod +x npm/bin/probe-binary | |
| fi | |
| # Debug: Check what files are actually in npm/bin before npm link | |
| echo "Debug: Files in npm/bin before npm link:" | |
| ls -la npm/bin/ | |
| # Install npm package dependencies and build all packages | |
| # Skip postinstall script since we're providing the binary manually | |
| cd npm && npm install --ignore-scripts && npm run build | |
| # Debug: Check what files are in npm/bin after npm install/build | |
| echo "Debug: Files in npm/bin after build:" | |
| ls -la bin/ | |
| - name: Link Probe package locally | |
| run: | | |
| cd npm && npm link --ignore-scripts | |
| - name: Verify Probe installation | |
| run: probe --version | |
| - name: Test search functionality | |
| shell: bash | |
| run: | | |
| echo "Current directory: $(pwd)" | |
| echo "Probe version:" | |
| echo "Debugging npm link setup:" | |
| which probe || echo "probe command not found in PATH" | |
| ls -la "$(which probe)" || echo "probe symlink not found" | |
| if [[ "${{ runner.os }}" == "Windows" ]]; then | |
| echo "Windows-specific checks:" | |
| npm list -g @probelabs/probe || echo "Package listing failed" | |
| else | |
| echo "Unix-specific checks:" | |
| readlink -f "$(which probe)" || echo "Cannot resolve probe symlink" | |
| fi | |
| echo "Checking package structure:" | |
| if [[ "${{ runner.os }}" == "Windows" ]]; then | |
| ls -la "$(npm root -g)/@probelabs/probe/bin/" || echo "npm package bin directory not found" | |
| else | |
| # For npm link, the package might be in a different location | |
| npm list -g @probelabs/probe --depth=0 || echo "Linked package not found" | |
| fi | |
| echo "Testing probe command (using npm link):" | |
| npm --version | |
| probe --version | |
| echo "Running search command..." | |
| # Use the probe command from npm global installation (this uses our shim) | |
| probe search "context engine" README.md --format json > search_results.json | |
| echo "Search command completed, checking file size:" | |
| if [[ "${{ runner.os }}" == "Windows" ]]; then | |
| powershell -Command "Get-ChildItem search_results.json | Select-Object Name,Length" | |
| echo "File contents (first 10 lines):" | |
| powershell -Command "Get-Content search_results.json | Select-Object -First 10" | |
| else | |
| ls -la search_results.json | |
| echo "File contents (first 10 lines):" | |
| head -10 search_results.json | |
| fi | |
| - name: Verify search results (Unix) | |
| if: runner.os != 'Windows' | |
| run: | | |
| # Check that the JSON output contains expected fields | |
| if ! grep -q '"file"' search_results.json; then | |
| echo "Error: JSON output missing 'file' field" | |
| cat search_results.json | |
| exit 1 | |
| fi | |
| if ! grep -q '"code"' search_results.json; then | |
| echo "Error: JSON output missing 'code' field" | |
| cat search_results.json | |
| exit 1 | |
| fi | |
| if ! grep -q 'README.md' search_results.json; then | |
| echo "Error: Search results don't contain README.md" | |
| cat search_results.json | |
| exit 1 | |
| fi | |
| if ! grep -q 'context engine' search_results.json; then | |
| echo "Error: Search results don't contain the search term" | |
| cat search_results.json | |
| exit 1 | |
| fi | |
| echo "✅ Search results validation passed" | |
| - name: Verify search results (Windows) | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| # shellcheck disable=SC1009,SC1073,SC1065,SC1064,SC1072 | |
| # Check that the JSON output contains expected fields | |
| if (-not (Test-Path search_results.json)) { | |
| Write-Host "Error: search_results.json file not found" | |
| exit 1 | |
| } | |
| $content = Get-Content search_results.json -Raw | |
| if (-not $content) { | |
| Write-Host "Error: search_results.json file is empty" | |
| exit 1 | |
| } | |
| # Extract JSON part by finding the first '{' character (skip debug output) | |
| $jsonStart = $content.IndexOf('{') | |
| if ($jsonStart -eq -1) { | |
| Write-Host "Error: No JSON found in output" | |
| Get-Content search_results.json | |
| exit 1 | |
| } | |
| $jsonContent = $content.Substring($jsonStart) | |
| if (-not ($jsonContent -match '"file"')) { | |
| Write-Host "Error: JSON output missing 'file' field" | |
| Get-Content search_results.json | |
| exit 1 | |
| } | |
| if (-not ($jsonContent -match '"code"')) { | |
| Write-Host "Error: JSON output missing 'code' field" | |
| Get-Content search_results.json | |
| exit 1 | |
| } | |
| if (-not ($jsonContent -match 'README.md')) { | |
| Write-Host "Error: Search results don't contain README.md" | |
| Get-Content search_results.json | |
| exit 1 | |
| } | |
| if (-not ($jsonContent -match 'context engine')) { | |
| Write-Host "Error: Search results don't contain the search term" | |
| Get-Content search_results.json | |
| exit 1 | |
| } | |
| Write-Host "✅ Search results validation passed" | |
| - name: Test extract functionality | |
| shell: bash | |
| run: | | |
| echo "Running extract command..." | |
| # Use the probe command from npm global installation (this uses our shim) | |
| probe extract README.md:1 --format json > extract_results.json | |
| - name: Verify extract results (Unix) | |
| if: runner.os != 'Windows' | |
| run: | | |
| # Check that the extract output contains expected fields | |
| if ! grep -q '"file"' extract_results.json; then | |
| echo "Error: Extract JSON output missing 'file' field" | |
| cat extract_results.json | |
| exit 1 | |
| fi | |
| if ! grep -q 'README.md' extract_results.json; then | |
| echo "Error: Extract results don't contain README.md" | |
| cat extract_results.json | |
| exit 1 | |
| fi | |
| echo "✅ Extract results validation passed" | |
| - name: Verify extract results (Windows) | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| # shellcheck disable=SC1009,SC1073,SC1065,SC1064,SC1072 | |
| # Check that the extract output contains expected fields | |
| if (-not (Test-Path extract_results.json)) { | |
| Write-Host "Error: extract_results.json file not found" | |
| exit 1 | |
| } | |
| $content = Get-Content extract_results.json -Raw | |
| if (-not $content) { | |
| Write-Host "Error: extract_results.json file is empty" | |
| exit 1 | |
| } | |
| # Extract JSON part by finding the first '{' character (skip debug output) | |
| $jsonStart = $content.IndexOf('{') | |
| if ($jsonStart -eq -1) { | |
| Write-Host "Error: No JSON found in output" | |
| Get-Content extract_results.json | |
| exit 1 | |
| } | |
| $jsonContent = $content.Substring($jsonStart) | |
| if (-not ($jsonContent -match '"file"')) { | |
| Write-Host "Error: Extract JSON output missing 'file' field" | |
| Get-Content extract_results.json | |
| exit 1 | |
| } | |
| if (-not ($jsonContent -match 'README.md')) { | |
| Write-Host "Error: Extract results don't contain README.md" | |
| Get-Content extract_results.json | |
| exit 1 | |
| } | |
| Write-Host "✅ Extract results validation passed" | |
| - name: Test MCP server functionality | |
| shell: bash | |
| run: | | |
| echo "Testing MCP server functionality using npm link..." | |
| # Debug: Check what probe command is actually being executed | |
| echo "Debugging probe command resolution:" | |
| which probe | |
| ls -la "$(which probe)" || echo "Cannot stat probe command" | |
| # Check if it's our Node.js shim | |
| echo "Checking if probe command is our Node.js shim:" | |
| head -3 "$(readlink -f "$(which probe)" 2>/dev/null || which probe)" || echo "Cannot read probe file content" | |
| # Test that probe mcp --help works (this tests our shim routing) | |
| echo "Testing MCP help command:" | |
| probe mcp --help > mcp_help.txt 2>&1 | |
| mcp_exit_code=$? | |
| if [ $mcp_exit_code -ne 0 ]; then | |
| echo "MCP help command completed with exit code: $mcp_exit_code" | |
| fi | |
| echo "Debug - Command that was actually executed for 'probe mcp --help':" | |
| echo "Exit code: $mcp_exit_code" | |
| # Verify the help output contains MCP-specific content | |
| if grep -q "Probe MCP Server" mcp_help.txt; then | |
| echo "✅ MCP server help shows correct content - shim routing works!" | |
| echo "MCP help output preview:" | |
| head -5 mcp_help.txt | |
| else | |
| echo "❌ MCP help output doesn't contain expected content" | |
| echo "Expected: 'Probe MCP Server'" | |
| echo "Full output:" | |
| cat mcp_help.txt | |
| echo "--- End of output ---" | |
| # Debug: Let's check if the MCP build files exist | |
| echo "Checking MCP build files:" | |
| ls -la npm/build/mcp/ || echo "MCP build directory not found" | |
| # Check if we can run the MCP server directly | |
| echo "Testing direct MCP server execution:" | |
| node npm/build/mcp/index.js --help 2>&1 | head -5 || echo "Direct MCP execution failed" | |
| exit 1 | |
| fi | |
| # Test basic MCP functionality with timeout handling | |
| if [[ "${{ runner.os }}" == "Windows" ]]; then | |
| echo "Testing basic MCP server startup on Windows..." | |
| # On Windows, just verify the command starts (we already tested help above) | |
| echo "✅ MCP server routing verified on Windows" | |
| else | |
| echo "Testing MCP server JSON-RPC on Unix..." | |
| # Test MCP server with tools/list request via echo and timeout | |
| echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | timeout 3s probe mcp > mcp_tools.json 2>/dev/null || echo "MCP tools test completed" | |
| # Check if MCP server produced any output (means it started successfully) | |
| if [ -f mcp_tools.json ] && [ -s mcp_tools.json ]; then | |
| echo "✅ MCP server responded to JSON-RPC successfully" | |
| echo "MCP response preview:" | |
| head -3 mcp_tools.json | |
| else | |
| echo "✅ MCP server started successfully (no JSON response expected for basic test)" | |
| fi | |
| fi | |
| # Note: npm agent tests are handled by the dedicated rust-tests.yml workflow | |
| # This integration test focuses on npm package installation and functionality verification | |
| - name: Upload test artifacts on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: test-results-${{ matrix.os }}-node${{ matrix.node-version }} | |
| path: | | |
| search_results.json | |
| extract_results.json | |
| npm/coverage/ | |
| retention-days: 7 | |
| chat-integration-test: | |
| name: Test chat example on ${{ matrix.os }} | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 15 | |
| strategy: | |
| matrix: | |
| os: [ubuntu-latest, windows-latest, macos-latest] | |
| node-version: [20] | |
| fail-fast: false | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js ${{ matrix.node-version }} | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ matrix.node-version }} | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@1.92.0 | |
| with: | |
| components: rustfmt, clippy | |
| - name: Setup Rust cache | |
| uses: actions/cache@v4 | |
| timeout-minutes: 5 | |
| with: | |
| path: | | |
| ~/.cargo/bin/ | |
| ~/.cargo/registry/index/ | |
| ~/.cargo/registry/cache/ | |
| ~/.cargo/git/db/ | |
| target/ | |
| key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}-${{ hashFiles('rust-toolchain', 'rust-toolchain.toml') || 'stable' }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}- | |
| ${{ runner.os }}-cargo- | |
| - name: Build Rust binary (debug mode for CI speed) | |
| run: cargo build | |
| - name: Build npm package | |
| run: | | |
| cd npm | |
| npm install | |
| npm run build | |
| - name: Install chat example dependencies | |
| run: | | |
| cd examples/chat | |
| npm install | |
| - name: Register mock backend | |
| run: | | |
| cd examples/chat | |
| # The mock backend is automatically available in the implement/backends directory | |
| echo "Mock backend is available at: implement/backends/MockBackend.js" | |
| - name: Run chat flow integration tests | |
| run: | | |
| cd examples/chat | |
| npm run test:chat | |
| - name: Run tool calling integration tests | |
| run: | | |
| cd examples/chat | |
| npm run test:tools | |
| - name: Run all chat tests | |
| run: | | |
| cd examples/chat | |
| npm test | |
| - name: Upload test artifacts on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: chat-test-results-${{ matrix.os }}-node${{ matrix.node-version }} | |
| path: | | |
| examples/chat/test-results/ | |
| examples/chat/npm-debug.log | |
| retention-days: 7 |