diaspora/spec/lib/connection_tester_spec.rb
Benjamin Neff 78b28c3d54
Handle nodeinfo timeouts gracefully
some (especially bigger pods) are sometimes slow to respond with
statistics, so lets handle that gracefully and not mark the pods as
down.
2022-07-24 17:19:04 +02:00

198 lines
8.1 KiB
Ruby

# frozen_string_literal: true
describe ConnectionTester do
let(:url) { "https://pod.example.com" }
let(:result) { ConnectionTester::Result.new }
let(:tester) { ConnectionTester.new(url, result) }
describe "::check" do
it "takes a http url and returns a result object" do
res = ConnectionTester.check("https://pod.example.com")
expect(res).to be_a(ConnectionTester::Result)
end
it "still returns a result object, even for invalid urls" do
res = ConnectionTester.check("i:am/not)a+url")
expect(res).to be_a(ConnectionTester::Result)
expect(res.error).to be_a(ConnectionTester::Failure)
end
end
describe "#initialize" do
it "accepts the http protocol" do
expect {
ConnectionTester.new("https://pod.example.com")
}.not_to raise_error
end
it "rejects unexpected protocols" do
expect {
ConnectionTester.new("xmpp:user@example.com")
}.to raise_error(ConnectionTester::AddressFailure)
end
end
describe "#resolve" do
it "resolves the IP address" do
expect(IPSocket).to receive(:getaddress).with("pod.example.com").and_return("192.168.1.2")
tester.resolve
expect(result.ip).to eq("192.168.1.2")
end
it "raises DNSFailure if host is unknown" do
expect(IPSocket).to receive(:getaddress).with("pod.example.com").and_raise(SocketError.new("Error!"))
expect { tester.resolve }.to raise_error(ConnectionTester::DNSFailure, "'pod.example.com' - Error!")
end
end
describe "#request" do
it "performs a successful GET request on '/'" do
stub_request(:get, url).to_return(status: 200, body: "Hello World!")
tester.request
expect(result.rt).to be > -1
expect(result.reachable).to be_truthy
expect(result.ssl).to be_truthy
end
it "receives a 'normal' 301 redirect" do
stub_request(:get, url).to_return(status: 301, headers: {"Location" => "#{url}/redirect"})
stub_request(:get, "#{url}/redirect").to_return(status: 200, body: "Hello World!")
tester.request
end
it "updates ssl after https redirect" do
tester = ConnectionTester.new("http://pod.example.com/", result)
stub_request(:get, "http://pod.example.com/").to_return(status: 301, headers: {"Location" => url})
stub_request(:get, url).to_return(status: 200, body: "Hello World!")
tester.request
expect(result.ssl).to be_truthy
end
it "rejects other hostname after redirect" do
stub_request(:get, url).to_return(status: 301, headers: {"Location" => "https://example.com/"})
stub_request(:get, "https://example.com/").to_return(status: 200, body: "Hello World!")
expect { tester.request }.to raise_error(ConnectionTester::HTTPFailure)
end
it "receives too many 301 redirects" do
stub_request(:get, url).to_return(status: 301, headers: {"Location" => "#{url}/redirect"})
stub_request(:get, "#{url}/redirect").to_return(status: 301, headers: {"Location" => "#{url}/redirect1"})
stub_request(:get, "#{url}/redirect1").to_return(status: 301, headers: {"Location" => "#{url}/redirect2"})
stub_request(:get, "#{url}/redirect2").to_return(status: 301, headers: {"Location" => "#{url}/redirect3"})
stub_request(:get, "#{url}/redirect3").to_return(status: 200, body: "Hello World!")
expect { tester.request }.to raise_error(ConnectionTester::HTTPFailure)
end
it "receives a 404 not found" do
stub_request(:get, url).to_return(status: 404, body: "Not Found!")
expect { tester.request }.to raise_error(ConnectionTester::HTTPFailure)
end
it "cannot connect" do
stub_request(:get, url).to_raise(Faraday::ConnectionFailed.new("Error!"))
expect { tester.request }.to raise_error(ConnectionTester::NetFailure)
end
it "encounters an invalid SSL setup" do
stub_request(:get, url).to_raise(Faraday::SSLError.new("Error!"))
expect { tester.request }.to raise_error(ConnectionTester::SSLFailure)
end
end
describe "#nodeinfo" do
def build_ni_document(version)
NodeInfo.build do |doc|
doc.version = version
doc.open_registrations = true
doc.protocols.protocols << "diaspora"
doc.software.name = "diaspora"
doc.software.version = "a.b.c.d"
end
end
NodeInfo::VERSIONS.each do |version|
context "with version #{version}" do
let(:ni_wellknown) {
{links: [{rel: "http://nodeinfo.diaspora.software/ns/schema/#{version}", href: "/nodeinfo/#{version}"}]}
}
it "reads the version from the nodeinfo document" do
ni_document = build_ni_document(version)
stub_request(:get, "#{url}#{ConnectionTester::NODEINFO_FRAGMENT}")
.to_return(status: 200, body: JSON.generate(ni_wellknown))
stub_request(:get, "#{url}/nodeinfo/#{version}")
.to_return(status: 200, body: JSON.generate(ni_document.as_json))
tester.nodeinfo
expect(result.software_version).to eq("diaspora a.b.c.d")
end
end
end
it "uses the latest commonly supported version" do
ni_wellknown = {links: [
{rel: "http://nodeinfo.diaspora.software/ns/schema/1.0", href: "/nodeinfo/1.0"},
{rel: "http://nodeinfo.diaspora.software/ns/schema/1.1", href: "/nodeinfo/1.1"},
{rel: "http://nodeinfo.diaspora.software/ns/schema/2.0", href: "/nodeinfo/2.0"},
{rel: "http://nodeinfo.diaspora.software/ns/schema/9.0", href: "/nodeinfo/9.0"}
]}
ni_document = build_ni_document("2.0")
stub_request(:get, "#{url}#{ConnectionTester::NODEINFO_FRAGMENT}")
.to_return(status: 200, body: JSON.generate(ni_wellknown))
stub_request(:get, "#{url}/nodeinfo/2.0").to_return(status: 200, body: JSON.generate(ni_document.as_json))
tester.nodeinfo
expect(result.software_version).to eq("diaspora a.b.c.d")
end
it "handles no common version gracefully" do
ni_wellknown = {links: [{rel: "http://nodeinfo.diaspora.software/ns/schema/1.1", href: "/nodeinfo/1.1"}]}
stub_request(:get, "#{url}#{ConnectionTester::NODEINFO_FRAGMENT}")
.to_return(status: 200, body: JSON.generate(ni_wellknown))
expect { tester.nodeinfo }.to raise_error(ConnectionTester::NodeInfoFailure)
end
it "fails the nodeinfo document is missing" do
stub_request(:get, "#{url}#{ConnectionTester::NODEINFO_FRAGMENT}").to_return(status: 404, body: "Not Found")
expect { tester.nodeinfo }.to raise_error(ConnectionTester::HTTPFailure)
end
it "handles a malformed document gracefully" do
stub_request(:get, "#{url}#{ConnectionTester::NODEINFO_FRAGMENT}")
.to_return(status: 200, body: '{"json"::::"malformed"}')
expect { tester.nodeinfo }.to raise_error(ConnectionTester::NodeInfoFailure)
end
it "handles timeout gracefully" do
ni_wellknown = {links: [{rel: "http://nodeinfo.diaspora.software/ns/schema/1.0", href: "/nodeinfo/1.0"}]}
stub_request(:get, "#{url}#{ConnectionTester::NODEINFO_FRAGMENT}")
.to_return(status: 200, body: JSON.generate(ni_wellknown))
stub_request(:get, "#{url}/nodeinfo/1.0").to_raise(Faraday::TimeoutError.new)
expect { tester.nodeinfo }.to raise_error(ConnectionTester::NodeInfoFailure)
end
it "handles a invalid jrd document gracefully" do
invalid_wellknown = {links: {rel: "http://nodeinfo.diaspora.software/ns/schema/1.0", href: "/nodeinfo/1.0"}}
stub_request(:get, "#{url}#{ConnectionTester::NODEINFO_FRAGMENT}")
.to_return(status: 200, body: JSON.generate(invalid_wellknown))
expect { tester.nodeinfo }.to raise_error(ConnectionTester::NodeInfoFailure)
end
it "handles a invalid nodeinfo document gracefully" do
ni_wellknown = {links: [{rel: "http://nodeinfo.diaspora.software/ns/schema/1.0", href: "/nodeinfo/1.0"}]}
stub_request(:get, "#{url}#{ConnectionTester::NODEINFO_FRAGMENT}")
.to_return(status: 200, body: JSON.generate(ni_wellknown))
stub_request(:get, "#{url}/nodeinfo/1.0").to_return(status: 200, body: '{"software": "invalid nodeinfo"}')
expect { tester.nodeinfo }.to raise_error(ConnectionTester::NodeInfoFailure)
end
end
end