Curl: expand test coverage
This adds more tests to `curl_spec.rb` to increase test coverage. This brings almost all of the methods that don't make network requests up to 100% line and branch coverage (the exception being some guards in `parse_curl_output` that shouldn't happen under normal circumstances). In the process of writing more tests for `parse_curl_response`, I made some tweaks to remove checks for conditions that shouldn't ever be true (e.g., `match["code"]` isn't optional, so it will be present if `HTTP_STATUS_LINE_REGEX` matches) and to refactor some others. I contributed this method a while back (9171eb2), so this is me coming back to clarify some behavior.
This commit is contained in:
parent
cf22382921
commit
d8125322e1
@ -138,6 +138,12 @@ RSpec.describe "Utils::Curl" do
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
response_hash[:ok_no_status_text] = response_hash[:ok].deep_dup
|
||||||
|
response_hash[:ok_no_status_text].delete(:status_text)
|
||||||
|
|
||||||
|
response_hash[:ok_blank_header_value] = response_hash[:ok].deep_dup
|
||||||
|
response_hash[:ok_blank_header_value][:headers]["range"] = ""
|
||||||
|
|
||||||
response_hash[:redirection] = {
|
response_hash[:redirection] = {
|
||||||
status_code: "301",
|
status_code: "301",
|
||||||
status_text: "Moved Permanently",
|
status_text: "Moved Permanently",
|
||||||
@ -257,6 +263,16 @@ RSpec.describe "Utils::Curl" do
|
|||||||
\r
|
\r
|
||||||
EOS
|
EOS
|
||||||
|
|
||||||
|
response_text[:ok_no_status_text] = response_text[:ok].sub(" #{response_hash[:ok][:status_text]}", "")
|
||||||
|
response_text[:ok_blank_header_name] = response_text[:ok].sub(
|
||||||
|
"#{response_hash[:ok][:headers]["date"]}\r\n",
|
||||||
|
"#{response_hash[:ok][:headers]["date"]}\r\n: Test\r\n",
|
||||||
|
)
|
||||||
|
response_text[:ok_blank_header_value] = response_text[:ok].sub(
|
||||||
|
"#{response_hash[:ok][:headers]["date"]}\r\n",
|
||||||
|
"#{response_hash[:ok][:headers]["date"]}\r\nRange:\r\n",
|
||||||
|
)
|
||||||
|
|
||||||
response_text[:redirection] = response_text[:ok].sub(
|
response_text[:redirection] = response_text[:ok].sub(
|
||||||
"HTTP/1.1 #{response_hash[:ok][:status_code]} #{response_hash[:ok][:status_text]}\r",
|
"HTTP/1.1 #{response_hash[:ok][:status_code]} #{response_hash[:ok][:status_text]}\r",
|
||||||
"HTTP/1.1 #{response_hash[:redirection][:status_code]} #{response_hash[:redirection][:status_text]}\r\n" \
|
"HTTP/1.1 #{response_hash[:redirection][:status_code]} #{response_hash[:redirection][:status_text]}\r\n" \
|
||||||
@ -306,7 +322,27 @@ RSpec.describe "Utils::Curl" do
|
|||||||
body
|
body
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "curl_args" do
|
describe "::curl_executable" do
|
||||||
|
it "returns `HOMEBREW_BREWED_CURL_PATH` when `use_homebrew_curl` is `true`" do
|
||||||
|
expect(curl_executable(use_homebrew_curl: true)).to eq(HOMEBREW_BREWED_CURL_PATH)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns curl shim path when `use_homebrew_curl` is `false` or omitted" do
|
||||||
|
curl_shim_path = HOMEBREW_SHIMS_PATH/"shared/curl"
|
||||||
|
expect(curl_executable(use_homebrew_curl: false)).to eq(curl_shim_path)
|
||||||
|
expect(curl_executable).to eq(curl_shim_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "::curl_path" do
|
||||||
|
it "returns a curl path string" do
|
||||||
|
expect(curl_path).to match(%r{[^/]+(?:/[^/]+)*})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "::curl_args" do
|
||||||
|
include Context
|
||||||
|
|
||||||
let(:args) { ["foo"] }
|
let(:args) { ["foo"] }
|
||||||
let(:user_agent_string) { "Lorem ipsum dolor sit amet" }
|
let(:user_agent_string) { "Lorem ipsum dolor sit amet" }
|
||||||
|
|
||||||
@ -388,6 +424,11 @@ RSpec.describe "Utils::Curl" do
|
|||||||
expect { curl_args(*args, retry_max_time: "test") }.to raise_error(TypeError)
|
expect { curl_args(*args, retry_max_time: "test") }.to raise_error(TypeError)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "uses `--show-error` when :show_error is `true`" do
|
||||||
|
expect(curl_args(*args, show_error: true)).to include("--show-error")
|
||||||
|
expect(curl_args(*args, show_error: false)).not_to include("--show-error")
|
||||||
|
end
|
||||||
|
|
||||||
it "uses `--referer` when :referer is present" do
|
it "uses `--referer` when :referer is present" do
|
||||||
expect(curl_args(*args, referer: "https://brew.sh").join(" ")).to include("--referer https://brew.sh")
|
expect(curl_args(*args, referer: "https://brew.sh").join(" ")).to include("--referer https://brew.sh")
|
||||||
end
|
end
|
||||||
@ -426,9 +467,62 @@ RSpec.describe "Utils::Curl" do
|
|||||||
expect(curl_args(*args).join(" ")).to include("--fail")
|
expect(curl_args(*args).join(" ")).to include("--fail")
|
||||||
expect(curl_args(*args, show_output: true).join(" ")).not_to include("--fail")
|
expect(curl_args(*args, show_output: true).join(" ")).not_to include("--fail")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "uses `--progress-bar` outside of a `--verbose` context" do
|
||||||
|
expect(curl_args(*args).join(" ")).to include("--progress-bar")
|
||||||
|
with_context verbose: true do
|
||||||
|
expect(curl_args(*args).join(" ")).not_to include("--progress-bar")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when `EnvConfig.curl_verbose?` is `true`" do
|
||||||
|
before do
|
||||||
|
allow(Homebrew::EnvConfig).to receive(:curl_verbose?).and_return(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "uses `--verbose`" do
|
||||||
|
expect(curl_args(*args).join(" ")).to include("--verbose")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when `EnvConfig.curl_verbose?` is `false`" do
|
||||||
|
before do
|
||||||
|
allow(Homebrew::EnvConfig).to receive(:curl_verbose?).and_return(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "doesn't use `--verbose`" do
|
||||||
|
expect(curl_args(*args).join(" ")).not_to include("--verbose")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when `$stdout.tty?` is `false`" do
|
||||||
|
before do
|
||||||
|
allow($stdout).to receive(:tty?).and_return(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "uses `--silent`" do
|
||||||
|
expect(curl_args(*args).join(" ")).to include("--silent")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when `$stdout.tty?` is `true`" do
|
||||||
|
before do
|
||||||
|
allow($stdout).to receive(:tty?).and_return(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "doesn't use `--silent` outside of a `--quiet` context" do
|
||||||
|
with_context quiet: false do
|
||||||
|
expect(curl_args(*args).join(" ")).not_to include("--silent")
|
||||||
|
end
|
||||||
|
|
||||||
|
with_context quiet: true do
|
||||||
|
expect(curl_args(*args).join(" ")).to include("--silent")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "url_protected_by_cloudflare?" do
|
describe "::url_protected_by_cloudflare?" do
|
||||||
it "returns `true` when a URL is protected by Cloudflare" do
|
it "returns `true` when a URL is protected by Cloudflare" do
|
||||||
expect(url_protected_by_cloudflare?(details[:cloudflare][:single_cookie])).to be(true)
|
expect(url_protected_by_cloudflare?(details[:cloudflare][:single_cookie])).to be(true)
|
||||||
expect(url_protected_by_cloudflare?(details[:cloudflare][:multiple_cookies])).to be(true)
|
expect(url_protected_by_cloudflare?(details[:cloudflare][:multiple_cookies])).to be(true)
|
||||||
@ -448,7 +542,7 @@ RSpec.describe "Utils::Curl" do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "url_protected_by_incapsula?" do
|
describe "::url_protected_by_incapsula?" do
|
||||||
it "returns `true` when a URL is protected by Cloudflare" do
|
it "returns `true` when a URL is protected by Cloudflare" do
|
||||||
expect(url_protected_by_incapsula?(details[:incapsula][:single_cookie_visid_incap])).to be(true)
|
expect(url_protected_by_incapsula?(details[:incapsula][:single_cookie_visid_incap])).to be(true)
|
||||||
expect(url_protected_by_incapsula?(details[:incapsula][:single_cookie_incap_ses])).to be(true)
|
expect(url_protected_by_incapsula?(details[:incapsula][:single_cookie_incap_ses])).to be(true)
|
||||||
@ -468,7 +562,54 @@ RSpec.describe "Utils::Curl" do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#parse_curl_output" do
|
describe "::curl_version" do
|
||||||
|
it "returns a curl version string" do
|
||||||
|
expect(curl_version).to match(/^v?(\d+(?:\.\d+)+)$/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "::curl_supports_fail_with_body?" do
|
||||||
|
it "returns `true` if curl version is 7.76.0 or higher" do
|
||||||
|
allow_any_instance_of(Utils::Curl).to receive(:curl_version).and_return(Version.new("7.76.0"))
|
||||||
|
expect(curl_supports_fail_with_body?).to be(true)
|
||||||
|
|
||||||
|
allow_any_instance_of(Utils::Curl).to receive(:curl_version).and_return(Version.new("7.76.1"))
|
||||||
|
expect(curl_supports_fail_with_body?).to be(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns `false` if curl version is lower than 7.76.0" do
|
||||||
|
allow_any_instance_of(Utils::Curl).to receive(:curl_version).and_return(Version.new("7.75.0"))
|
||||||
|
expect(curl_supports_fail_with_body?).to be(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "::curl_supports_tls13?" do
|
||||||
|
it "returns `true` if curl command is successful" do
|
||||||
|
allow_any_instance_of(Kernel).to receive(:quiet_system).and_return(true)
|
||||||
|
expect(curl_supports_tls13?).to be(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns `false` if curl command is not successful" do
|
||||||
|
allow_any_instance_of(Kernel).to receive(:quiet_system).and_return(false)
|
||||||
|
expect(curl_supports_tls13?).to be(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "::http_status_ok?" do
|
||||||
|
it "returns `true` when `status` is 1xx or 2xx" do
|
||||||
|
expect(http_status_ok?("200")).to be(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns `false` when `status` is not 1xx or 2xx" do
|
||||||
|
expect(http_status_ok?("301")).to be(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns `false` when `status` is `nil`" do
|
||||||
|
expect(http_status_ok?(nil)).to be(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "::parse_curl_output" do
|
||||||
it "returns a correct hash when curl output contains response(s) and body" do
|
it "returns a correct hash when curl output contains response(s) and body" do
|
||||||
expect(parse_curl_output("#{response_text[:ok]}#{body[:default]}"))
|
expect(parse_curl_output("#{response_text[:ok]}#{body[:default]}"))
|
||||||
.to eq({ responses: [response_hash[:ok]], body: body[:default] })
|
.to eq({ responses: [response_hash[:ok]], body: body[:default] })
|
||||||
@ -505,21 +646,33 @@ RSpec.describe "Utils::Curl" do
|
|||||||
it "returns correct hash when curl output is blank" do
|
it "returns correct hash when curl output is blank" do
|
||||||
expect(parse_curl_output("")).to eq({ responses: [], body: "" })
|
expect(parse_curl_output("")).to eq({ responses: [], body: "" })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "errors if response count exceeds `max_iterations`" do
|
||||||
|
expect do
|
||||||
|
parse_curl_output(response_text[:redirections_to_ok], max_iterations: 1)
|
||||||
|
end.to raise_error("Too many redirects (max = 1)")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#parse_curl_response" do
|
describe "::parse_curl_response" do
|
||||||
it "returns a correct hash when given HTTP response text" do
|
it "returns a correct hash when given HTTP response text" do
|
||||||
expect(parse_curl_response(response_text[:ok])).to eq(response_hash[:ok])
|
expect(parse_curl_response(response_text[:ok])).to eq(response_hash[:ok])
|
||||||
|
expect(parse_curl_response(response_text[:ok_no_status_text])).to eq(response_hash[:ok_no_status_text])
|
||||||
|
expect(parse_curl_response(response_text[:ok_blank_header_value])).to eq(response_hash[:ok_blank_header_value])
|
||||||
expect(parse_curl_response(response_text[:redirection])).to eq(response_hash[:redirection])
|
expect(parse_curl_response(response_text[:redirection])).to eq(response_hash[:redirection])
|
||||||
expect(parse_curl_response(response_text[:duplicate_header])).to eq(response_hash[:duplicate_header])
|
expect(parse_curl_response(response_text[:duplicate_header])).to eq(response_hash[:duplicate_header])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "skips over response header lines with blank header name" do
|
||||||
|
expect(parse_curl_response(response_text[:ok_blank_header_name])).to eq(response_hash[:ok])
|
||||||
|
end
|
||||||
|
|
||||||
it "returns an empty hash when given an empty string" do
|
it "returns an empty hash when given an empty string" do
|
||||||
expect(parse_curl_response("")).to eq({})
|
expect(parse_curl_response("")).to eq({})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#curl_response_last_location" do
|
describe "::curl_response_last_location" do
|
||||||
it "returns the last location header when given an array of HTTP response hashes" do
|
it "returns the last location header when given an array of HTTP response hashes" do
|
||||||
expect(curl_response_last_location([
|
expect(curl_response_last_location([
|
||||||
response_hash[:redirection],
|
response_hash[:redirection],
|
||||||
@ -577,12 +730,20 @@ RSpec.describe "Utils::Curl" do
|
|||||||
).to eq(response_hash[:redirection_parent_relative][:headers]["location"].sub(/^\./, "https://brew.sh/test1"))
|
).to eq(response_hash[:redirection_parent_relative][:headers]["location"].sub(/^\./, "https://brew.sh/test1"))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "skips response hashes without a `:headers` value" do
|
||||||
|
expect(curl_response_last_location([
|
||||||
|
response_hash[:redirection],
|
||||||
|
{ status_code: "404", status_text: "Not Found" },
|
||||||
|
response_hash[:ok],
|
||||||
|
])).to eq(response_hash[:redirection][:headers]["location"])
|
||||||
|
end
|
||||||
|
|
||||||
it "returns nil when the response hash doesn't contain a location header" do
|
it "returns nil when the response hash doesn't contain a location header" do
|
||||||
expect(curl_response_last_location([response_hash[:ok]])).to be_nil
|
expect(curl_response_last_location([response_hash[:ok]])).to be_nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#curl_response_follow_redirections" do
|
describe "::curl_response_follow_redirections" do
|
||||||
it "returns the original URL when there are no location headers" do
|
it "returns the original URL when there are no location headers" do
|
||||||
expect(
|
expect(
|
||||||
curl_response_follow_redirections(
|
curl_response_follow_redirections(
|
||||||
@ -634,5 +795,18 @@ RSpec.describe "Utils::Curl" do
|
|||||||
),
|
),
|
||||||
).to eq("#{location_urls[0]}example/")
|
).to eq("#{location_urls[0]}example/")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "skips response hashes without a `:headers` value" do
|
||||||
|
expect(
|
||||||
|
curl_response_follow_redirections(
|
||||||
|
[
|
||||||
|
response_hash[:redirection_root_relative],
|
||||||
|
{ status_code: "404", status_text: "Not Found" },
|
||||||
|
response_hash[:ok],
|
||||||
|
],
|
||||||
|
"https://brew.sh/test1/test2",
|
||||||
|
),
|
||||||
|
).to eq("https://brew.sh/example/")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -702,11 +702,10 @@ module Utils
|
|||||||
sig { params(response_text: String).returns(T::Hash[Symbol, T.untyped]) }
|
sig { params(response_text: String).returns(T::Hash[Symbol, T.untyped]) }
|
||||||
def parse_curl_response(response_text)
|
def parse_curl_response(response_text)
|
||||||
response = {}
|
response = {}
|
||||||
return response unless response_text.match?(HTTP_STATUS_LINE_REGEX)
|
return response unless (match = response_text.match(HTTP_STATUS_LINE_REGEX))
|
||||||
|
|
||||||
# Parse the status line and remove it
|
# Parse the status line and remove it
|
||||||
match = T.must(response_text.match(HTTP_STATUS_LINE_REGEX))
|
response[:status_code] = match["code"]
|
||||||
response[:status_code] = match["code"] if match["code"].present?
|
|
||||||
response[:status_text] = match["text"] if match["text"].present?
|
response[:status_text] = match["text"] if match["text"].present?
|
||||||
response_text = response_text.sub(%r{^HTTP/.* (\d+).*$\s*}, "")
|
response_text = response_text.sub(%r{^HTTP/.* (\d+).*$\s*}, "")
|
||||||
|
|
||||||
@ -714,18 +713,18 @@ module Utils
|
|||||||
response[:headers] = {}
|
response[:headers] = {}
|
||||||
response_text.split("\r\n").each do |line|
|
response_text.split("\r\n").each do |line|
|
||||||
header_name, header_value = line.split(/:\s*/, 2)
|
header_name, header_value = line.split(/:\s*/, 2)
|
||||||
next if header_name.blank?
|
next if header_name.blank? || header_value.nil?
|
||||||
|
|
||||||
header_name = header_name.strip.downcase
|
header_name = header_name.strip.downcase
|
||||||
header_value&.strip!
|
header_value.strip!
|
||||||
|
|
||||||
case response[:headers][header_name]
|
case response[:headers][header_name]
|
||||||
when nil
|
|
||||||
response[:headers][header_name] = header_value
|
|
||||||
when String
|
when String
|
||||||
response[:headers][header_name] = [response[:headers][header_name], header_value]
|
response[:headers][header_name] = [response[:headers][header_name], header_value]
|
||||||
when Array
|
when Array
|
||||||
response[:headers][header_name].push(header_value)
|
response[:headers][header_name].push(header_value)
|
||||||
|
else
|
||||||
|
response[:headers][header_name] = header_value
|
||||||
end
|
end
|
||||||
|
|
||||||
response[:headers][header_name]
|
response[:headers][header_name]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user