Add basic HTTP client support with pluggable transports
This commit is contained in:
parent
eb0d9c0e6d
commit
8b764428b1
1 changed files with 609 additions and 8 deletions
212
test/mcp/client/http_test.rb
Normal file
212
test/mcp/client/http_test.rb
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
require "faraday"
|
||||
require "webmock/minitest"
|
||||
require "mcp/client/http"
|
||||
require "mcp/client/tool"
|
||||
require "mcp/client"
|
||||
|
||||
module MCP
|
||||
class Client
|
||||
class HTTPTest < Minitest::Test
|
||||
def test_raises_load_error_when_faraday_not_available
|
||||
client = HTTP.new(url: url)
|
||||
|
||||
# simulate Faraday not being available
|
||||
HTTP.any_instance.stubs(:require).with("faraday").raises(LoadError, "cannot load such file -- faraday")
|
||||
|
||||
error = assert_raises(LoadError) do
|
||||
# This should immediately try to instantiate the client and fail
|
||||
client.send_request(request: {})
|
||||
end
|
||||
|
||||
assert_includes(error.message, "The 'faraday' gem is required to use the MCP client HTTP transport")
|
||||
assert_includes(error.message, "Add it to your Gemfile: gem 'faraday', '>= 2.0'")
|
||||
end
|
||||
|
||||
def test_headers_are_added_to_the_request
|
||||
headers = { "Authorization" => "Bearer token" }
|
||||
client = HTTP.new(url: url, headers: headers)
|
||||
|
||||
request = {
|
||||
jsonrpc: "2.0",
|
||||
id: "test_id",
|
||||
method: "tools/list",
|
||||
}
|
||||
|
||||
stub_request(:post, url)
|
||||
.with(
|
||||
headers: {
|
||||
"Authorization" => "Bearer token",
|
||||
"Content-Type" => "application/json",
|
||||
},
|
||||
body: request.to_json,
|
||||
)
|
||||
.to_return(
|
||||
status: 200,
|
||||
headers: { "Content-Type" => "application/json" },
|
||||
body: { result: { tools: [] } }.to_json,
|
||||
)
|
||||
|
||||
# The test passes if the request is made with the correct headers
|
||||
# If headers are wrong, the stub_request won't match and will raise
|
||||
client.send_request(request: request)
|
||||
end
|
||||
|
||||
def test_send_request_returns_faraday_response
|
||||
request = {
|
||||
jsonrpc: "2.0",
|
||||
id: "test_id",
|
||||
method: "tools/list",
|
||||
}
|
||||
|
||||
stub_request(:post, url)
|
||||
.with(body: request.to_json)
|
||||
.to_return(
|
||||
status: 200,
|
||||
headers: { "Content-Type" => "application/json" },
|
||||
body: { result: { tools: [] } }.to_json,
|
||||
)
|
||||
|
||||
response = client.send_request(request: request)
|
||||
assert_instance_of(Hash, response)
|
||||
assert_equal({ "result" => { "tools" => [] } }, response)
|
||||
end
|
||||
|
||||
def test_send_request_raises_bad_request_error
|
||||
request = {
|
||||
jsonrpc: "2.0",
|
||||
id: "test_id",
|
||||
method: "tools/list",
|
||||
}
|
||||
|
||||
stub_request(:post, url)
|
||||
.with(body: request.to_json)
|
||||
.to_return(status: 400)
|
||||
|
||||
error = assert_raises(RequestHandlerError) do
|
||||
client.send_request(request: request)
|
||||
end
|
||||
|
||||
assert_equal("The tools/list request is invalid", error.message)
|
||||
assert_equal(:bad_request, error.error_type)
|
||||
assert_equal({ method: "tools/list", params: nil }, error.request)
|
||||
end
|
||||
|
||||
def test_send_request_raises_unauthorized_error
|
||||
request = {
|
||||
jsonrpc: "2.0",
|
||||
id: "test_id",
|
||||
method: "tools/list",
|
||||
}
|
||||
|
||||
stub_request(:post, url)
|
||||
.with(body: request.to_json)
|
||||
.to_return(status: 401)
|
||||
|
||||
error = assert_raises(RequestHandlerError) do
|
||||
client.send_request(request: request)
|
||||
end
|
||||
|
||||
assert_equal("You are unauthorized to make tools/list requests", error.message)
|
||||
assert_equal(:unauthorized, error.error_type)
|
||||
assert_equal({ method: "tools/list", params: nil }, error.request)
|
||||
end
|
||||
|
||||
def test_send_request_raises_forbidden_error
|
||||
request = {
|
||||
jsonrpc: "2.0",
|
||||
id: "test_id",
|
||||
method: "tools/list",
|
||||
}
|
||||
|
||||
stub_request(:post, url)
|
||||
.with(body: request.to_json)
|
||||
.to_return(status: 403)
|
||||
|
||||
error = assert_raises(RequestHandlerError) do
|
||||
client.send_request(request: request)
|
||||
end
|
||||
|
||||
assert_equal("You are forbidden to make tools/list requests", error.message)
|
||||
assert_equal(:forbidden, error.error_type)
|
||||
assert_equal({ method: "tools/list", params: nil }, error.request)
|
||||
end
|
||||
|
||||
def test_send_request_raises_not_found_error
|
||||
request = {
|
||||
jsonrpc: "2.0",
|
||||
id: "test_id",
|
||||
method: "tools/list",
|
||||
}
|
||||
|
||||
stub_request(:post, url)
|
||||
.with(body: request.to_json)
|
||||
.to_return(status: 404)
|
||||
|
||||
error = assert_raises(RequestHandlerError) do
|
||||
client.send_request(request: request)
|
||||
end
|
||||
|
||||
assert_equal("The tools/list request is not found", error.message)
|
||||
assert_equal(:not_found, error.error_type)
|
||||
assert_equal({ method: "tools/list", params: nil }, error.request)
|
||||
end
|
||||
|
||||
def test_send_request_raises_unprocessable_entity_error
|
||||
request = {
|
||||
jsonrpc: "2.0",
|
||||
id: "test_id",
|
||||
method: "tools/list",
|
||||
}
|
||||
|
||||
stub_request(:post, url)
|
||||
.with(body: request.to_json)
|
||||
.to_return(status: 422)
|
||||
|
||||
error = assert_raises(RequestHandlerError) do
|
||||
client.send_request(request: request)
|
||||
end
|
||||
|
||||
assert_equal("The tools/list request is unprocessable", error.message)
|
||||
assert_equal(:unprocessable_entity, error.error_type)
|
||||
assert_equal({ method: "tools/list", params: nil }, error.request)
|
||||
end
|
||||
|
||||
def test_send_request_raises_internal_error
|
||||
request = {
|
||||
jsonrpc: "2.0",
|
||||
id: "test_id",
|
||||
method: "tools/list",
|
||||
}
|
||||
|
||||
stub_request(:post, url)
|
||||
.with(body: request.to_json)
|
||||
.to_return(status: 500)
|
||||
|
||||
error = assert_raises(RequestHandlerError) do
|
||||
client.send_request(request: request)
|
||||
end
|
||||
|
||||
assert_equal("Internal error handling tools/list request", error.message)
|
||||
assert_equal(:internal_error, error.error_type)
|
||||
assert_equal({ method: "tools/list", params: nil }, error.request)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def stub_request(method, url)
|
||||
WebMock.stub_request(method, url)
|
||||
end
|
||||
|
||||
def url
|
||||
"http://example.com"
|
||||
end
|
||||
|
||||
def client
|
||||
@client ||= HTTP.new(url: url)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in a new issue