brew/Library/Homebrew/test/livecheck_spec.rb

301 lines
7.7 KiB
Ruby
Raw Normal View History

2020-03-16 01:37:49 +05:30
# frozen_string_literal: true
2020-05-31 00:10:46 +05:30
require "formula"
2020-03-16 01:37:49 +05:30
require "livecheck"
RSpec.describe Livecheck do
2020-05-31 00:10:46 +05:30
let(:f) do
formula do
homepage "https://brew.sh"
url "https://brew.sh/test-0.0.1.tgz"
head "https://github.com/Homebrew/brew.git"
2020-05-31 00:10:46 +05:30
end
end
let(:livecheck_f) { described_class.new(f.class) }
let(:c) do
Cask::CaskLoader.load(+<<-RUBY)
cask "test" do
version "0.0.1,2"
url "https://brew.sh/test-0.0.1.dmg"
name "Test"
desc "Test cask"
homepage "https://brew.sh"
end
RUBY
end
let(:livecheck_c) { described_class.new(c) }
2020-03-16 01:37:49 +05:30
livecheck: Add support for POST requests livecheck currently doesn't support `POST` requests but it wasn't entirely clear how best to handle that. I initially approached it as a `Post` strategy but unfortunately that would have required us to handle response body parsing (e.g., JSON, XML, etc.) in some fashion. We could borrow some of the logic from related strategies but we would still be stuck having to update `Post` whenever we add a strategy for a new format. Instead, this implements `POST` support by borrowing ideas from the `using: :post` and `data` `url` options found in formulae. This uses a `post_form` option to handle form data and `post_json` to handle JSON data, encoding the hash argument for each into the appropriate format. The presence of either option means that curl will use a `POST` request. With this approach, we can make a `POST` request using any strategy that calls `Strategy::page_headers` or `::page_content` (directly or indirectly) and everything else works the same as usual. The only change needed in related strategies was to pass the options through to the `Strategy` methods. For example, if we need to parse a JSON response from a `POST` request, we add a `post_data` or `post_json` hash to the `livecheck` block `url` and use `strategy :json` with a `strategy` block. This leans on existing patterns that we're already familiar with and shouldn't require any notable maintenance burden when adding new strategies, so it seems like a better approach than a `Post` strategy.
2025-02-04 10:30:16 -05:00
let(:post_hash) do
{
empty: "",
boolean: "true",
number: "1",
string: "a + b = c",
livecheck: Add support for POST requests livecheck currently doesn't support `POST` requests but it wasn't entirely clear how best to handle that. I initially approached it as a `Post` strategy but unfortunately that would have required us to handle response body parsing (e.g., JSON, XML, etc.) in some fashion. We could borrow some of the logic from related strategies but we would still be stuck having to update `Post` whenever we add a strategy for a new format. Instead, this implements `POST` support by borrowing ideas from the `using: :post` and `data` `url` options found in formulae. This uses a `post_form` option to handle form data and `post_json` to handle JSON data, encoding the hash argument for each into the appropriate format. The presence of either option means that curl will use a `POST` request. With this approach, we can make a `POST` request using any strategy that calls `Strategy::page_headers` or `::page_content` (directly or indirectly) and everything else works the same as usual. The only change needed in related strategies was to pass the options through to the `Strategy` methods. For example, if we need to parse a JSON response from a `POST` request, we add a `post_data` or `post_json` hash to the `livecheck` block `url` and use `strategy :json` with a `strategy` block. This leans on existing patterns that we're already familiar with and shouldn't require any notable maintenance burden when adding new strategies, so it seems like a better approach than a `Post` strategy.
2025-02-04 10:30:16 -05:00
}
end
describe "#formula" do
it "returns nil if not set" do
expect(livecheck_f.formula).to be_nil
end
it "returns the String if set" do
livecheck_f.formula("other-formula")
expect(livecheck_f.formula).to eq("other-formula")
end
it "raises a TypeError if the argument isn't a String" do
expect do
livecheck_f.formula(123)
2023-04-21 01:21:38 +02:00
end.to raise_error TypeError
end
end
describe "#cask" do
it "returns nil if not set" do
expect(livecheck_c.cask).to be_nil
end
it "returns the String if set" do
livecheck_c.cask("other-cask")
expect(livecheck_c.cask).to eq("other-cask")
end
end
2020-03-16 01:37:49 +05:30
describe "#regex" do
it "returns nil if not set" do
expect(livecheck_f.regex).to be_nil
2020-03-16 01:37:49 +05:30
end
it "returns the Regexp if set" do
livecheck_f.regex(/foo/)
expect(livecheck_f.regex).to eq(/foo/)
2020-03-16 01:37:49 +05:30
end
end
describe "#skip" do
it "sets @skip to true when no argument is provided" do
expect(livecheck_f.skip).to be true
expect(livecheck_f.instance_variable_get(:@skip)).to be true
expect(livecheck_f.instance_variable_get(:@skip_msg)).to be_nil
2020-03-16 01:37:49 +05:30
end
it "sets @skip to true and @skip_msg to the provided String" do
expect(livecheck_f.skip("foo")).to be true
expect(livecheck_f.instance_variable_get(:@skip)).to be true
expect(livecheck_f.instance_variable_get(:@skip_msg)).to eq("foo")
2020-03-16 01:37:49 +05:30
end
end
describe "#skip?" do
it "returns the value of @skip" do
expect(livecheck_f.skip?).to be false
livecheck_f.skip
expect(livecheck_f.skip?).to be true
2020-03-16 01:37:49 +05:30
end
end
2020-08-05 11:54:37 -04:00
describe "#strategy" do
block = proc { |page, regex| page.scan(regex).map { |match| match[0].tr("_", ".") } }
2020-08-05 11:54:37 -04:00
it "returns nil if not set" do
expect(livecheck_f.strategy).to be_nil
expect(livecheck_f.strategy_block).to be_nil
2020-08-05 11:54:37 -04:00
end
it "returns the Symbol if set" do
livecheck_f.strategy(:page_match)
expect(livecheck_f.strategy).to eq(:page_match)
expect(livecheck_f.strategy_block).to be_nil
end
it "sets `strategy_block` when provided" do
livecheck_f.strategy(:page_match, &block)
expect(livecheck_f.strategy).to eq(:page_match)
expect(livecheck_f.strategy_block).to eq(block)
2020-08-05 11:54:37 -04:00
end
end
2024-03-21 08:19:35 -04:00
describe "#throttle" do
it "returns nil if not set" do
expect(livecheck_f.throttle).to be_nil
2024-03-21 08:19:35 -04:00
end
it "returns the Integer if set" do
livecheck_f.throttle(10)
expect(livecheck_f.throttle).to eq(10)
2024-03-21 08:19:35 -04:00
end
end
2020-03-16 01:37:49 +05:30
describe "#url" do
let(:url_string) { "https://brew.sh" }
it "returns nil if not set" do
expect(livecheck_f.url).to be_nil
end
it "returns a string when set to a string" do
livecheck_f.url(url_string)
expect(livecheck_f.url).to eq(url_string)
2020-03-16 01:37:49 +05:30
end
it "returns the URL symbol if valid" do
livecheck_f.url(:head)
expect(livecheck_f.url).to eq(:head)
livecheck_f.url(:homepage)
expect(livecheck_f.url).to eq(:homepage)
livecheck_f.url(:stable)
expect(livecheck_f.url).to eq(:stable)
livecheck_c.url(:url)
expect(livecheck_c.url).to eq(:url)
end
livecheck: Add Options class This adds a `Livecheck::Options` class, which is intended to house various configuration options that are set in `livecheck` blocks, conditionally set by livecheck at runtime, etc. The general idea is that when we add features involving configurations options (e.g., for livecheck, strategies, curl, etc.), we can make changes to `Options` without needing to modify parameters for strategy `find_versions` methods, `Strategy` methods like `page_headers` and `page_content`, etc. This is something that I've been trying to improve over the years and `Options` should help to reduce maintenance overhead in this area while also strengthening type signatures. `Options` replaces the existing `homebrew_curl` option (which related strategies pass to `Strategy` methods and on to `curl_args`) and the new `url_options` (which contains `post_form` or `post_json` values that are used to make `POST` requests). I recently added `url_options` as a temporary way of enabling `POST` support without `Options` but this restores the original `Options`-based implementation. Along the way, I added a `homebrew_curl` parameter to the `url` DSL method, allowing us to set an explicit value in `livecheck` blocks. This is something that we've needed in some cases but I also intend to replace implicit/inferred `homebrew_curl` usage with explicit values in `livecheck` blocks once this is available for use. My intention is to eventually remove the implicit behavior and only rely on explicit values. That will align with how `homebrew_curl` options work for other URLs and makes the behavior clear just from looking at the `livecheck` block. Lastly, this removes the `unused` rest parameter from `find_versions` methods. I originally added `unused` as a way of handling parameters that some `find_versions` methods have but others don't (e.g., `cask` in `ExtractPlist`), as this allowed us to pass various arguments to `find_versions` methods without worrying about whether a particular parameter is available. This isn't an ideal solution and I originally wanted to handle this situation by only passing expected arguments to `find_versions` methods but there was a technical issue standing in the way. I recently found an answer to the issue, so this also replaces the existing `ExtractPlist` special case with generic logic that checks the parameters for a strategy's `find_versions` method and only passes expected arguments. Replacing the aforementioned `find_versions` parameters with `Options` ensures that the remaining parameters are fairly consistent across strategies and any differences are handled by the aforementioned logic. Outside of `ExtractPlist`, the only other difference is that some `find_versions` methods have a `provided_content` parameter but that's currently only used by tests (though it's intended for caching support in the future). I will be renaming that parameter to `content` in an upcoming PR and expanding it to the other strategies, which should make them all consistent outside of `ExtractPlist`.
2025-02-11 18:04:38 -05:00
it "sets `url` options when provided" do
# This test makes sure that we can set multiple options at once and
# options from subsequent `url` calls are merged with existing values
# (i.e. existing values aren't reset to `nil`). [We only call `url` once
# in a `livecheck` block but this should technically work due to how it's
# implemented.]
livecheck_f.url(url_string, homebrew_curl: true, post_form: post_hash)
livecheck_f.url(url_string, post_json: post_hash)
expect(livecheck_f.options.homebrew_curl).to be(true)
expect(livecheck_f.options.post_form).to eq(post_hash)
expect(livecheck_f.options.post_json).to eq(post_hash)
livecheck: Add support for POST requests livecheck currently doesn't support `POST` requests but it wasn't entirely clear how best to handle that. I initially approached it as a `Post` strategy but unfortunately that would have required us to handle response body parsing (e.g., JSON, XML, etc.) in some fashion. We could borrow some of the logic from related strategies but we would still be stuck having to update `Post` whenever we add a strategy for a new format. Instead, this implements `POST` support by borrowing ideas from the `using: :post` and `data` `url` options found in formulae. This uses a `post_form` option to handle form data and `post_json` to handle JSON data, encoding the hash argument for each into the appropriate format. The presence of either option means that curl will use a `POST` request. With this approach, we can make a `POST` request using any strategy that calls `Strategy::page_headers` or `::page_content` (directly or indirectly) and everything else works the same as usual. The only change needed in related strategies was to pass the options through to the `Strategy` methods. For example, if we need to parse a JSON response from a `POST` request, we add a `post_data` or `post_json` hash to the `livecheck` block `url` and use `strategy :json` with a `strategy` block. This leans on existing patterns that we're already familiar with and shouldn't require any notable maintenance burden when adding new strategies, so it seems like a better approach than a `Post` strategy.
2025-02-04 10:30:16 -05:00
end
2023-04-21 01:21:38 +02:00
it "raises an ArgumentError if the argument isn't a valid Symbol" do
expect do
livecheck_f.url(:not_a_valid_symbol)
2023-04-21 01:21:38 +02:00
end.to raise_error ArgumentError
2020-03-16 01:37:49 +05:30
end
livecheck: Add support for POST requests livecheck currently doesn't support `POST` requests but it wasn't entirely clear how best to handle that. I initially approached it as a `Post` strategy but unfortunately that would have required us to handle response body parsing (e.g., JSON, XML, etc.) in some fashion. We could borrow some of the logic from related strategies but we would still be stuck having to update `Post` whenever we add a strategy for a new format. Instead, this implements `POST` support by borrowing ideas from the `using: :post` and `data` `url` options found in formulae. This uses a `post_form` option to handle form data and `post_json` to handle JSON data, encoding the hash argument for each into the appropriate format. The presence of either option means that curl will use a `POST` request. With this approach, we can make a `POST` request using any strategy that calls `Strategy::page_headers` or `::page_content` (directly or indirectly) and everything else works the same as usual. The only change needed in related strategies was to pass the options through to the `Strategy` methods. For example, if we need to parse a JSON response from a `POST` request, we add a `post_data` or `post_json` hash to the `livecheck` block `url` and use `strategy :json` with a `strategy` block. This leans on existing patterns that we're already familiar with and shouldn't require any notable maintenance burden when adding new strategies, so it seems like a better approach than a `Post` strategy.
2025-02-04 10:30:16 -05:00
it "raises an ArgumentError if both `post_form` and `post_json` arguments are provided" do
expect do
livecheck_f.url(:stable, post_form: post_hash, post_json: post_hash)
end.to raise_error ArgumentError
end
2020-03-16 01:37:49 +05:30
end
describe "#arch" do
let(:c_arch) do
Cask::Cask.new("c-arch") do
arch arm: "arm", intel: "intel"
version "0.0.1"
url "https://brew.sh/test-0.0.1.dmg"
name "Test"
desc "Test cask"
homepage "https://brew.sh"
livecheck do
url "https://brew.sh/#{arch}"
end
end
end
[:needs_arm, :needs_intel].each do |needs_arch|
arch_value = needs_arch.to_s.delete_prefix("needs_")
it "delegates `arch` in `livecheck` block to `package_or_resource`", needs_arch do
expect(c_arch.livecheck.url).to eq("https://brew.sh/#{arch_value}")
end
end
end
describe "#os" do
let(:c_os) do
Cask::Cask.new("c-os") do
os macos: "macos", linux: "linux"
version "0.0.1"
url "https://brew.sh/test-0.0.1.dmg"
name "Test"
desc "Test cask"
homepage "https://brew.sh"
livecheck do
url "https://brew.sh/#{os}"
end
end
end
[:needs_macos, :needs_linux].each do |needs_os|
os_value = needs_os.to_s.delete_prefix("needs_")
it "delegates `os` in `livecheck` block to `package_or_resource`", needs_os do
expect(c_os.livecheck.url).to eq("https://brew.sh/#{os_value}")
end
end
end
describe "#version" do
let(:url_with_version) { "https://brew.sh/0.0.1" }
let(:f_version) do
formula do
homepage "https://brew.sh"
url "https://brew.sh/test-0.0.1.tgz"
livecheck do
url "https://brew.sh/#{version}"
end
end
end
let(:c_version) do
Cask::Cask.new("c-version") do
version "0.0.1"
url "https://brew.sh/test-0.0.1.dmg"
name "Test"
desc "Test cask"
homepage "https://brew.sh"
livecheck do
url "https://brew.sh/#{version}"
end
end
end
let(:r_version) do
Resource.new do
url "https://brew.sh/test-0.0.1.tgz"
livecheck do
url "https://brew.sh/#{version}"
end
end
end
it "delegates `version` in `livecheck` block to `package_or_resource`" do
expect(f_version.livecheck.url).to eq(url_with_version)
expect(c_version.livecheck.url).to eq(url_with_version)
expect(r_version.livecheck.url).to eq(url_with_version)
end
end
2020-03-16 01:37:49 +05:30
describe "#to_hash" do
it "returns a Hash of all instance variables" do
expect(livecheck_f.to_hash).to eq(
2020-08-05 11:54:37 -04:00
{
livecheck: Add Options class This adds a `Livecheck::Options` class, which is intended to house various configuration options that are set in `livecheck` blocks, conditionally set by livecheck at runtime, etc. The general idea is that when we add features involving configurations options (e.g., for livecheck, strategies, curl, etc.), we can make changes to `Options` without needing to modify parameters for strategy `find_versions` methods, `Strategy` methods like `page_headers` and `page_content`, etc. This is something that I've been trying to improve over the years and `Options` should help to reduce maintenance overhead in this area while also strengthening type signatures. `Options` replaces the existing `homebrew_curl` option (which related strategies pass to `Strategy` methods and on to `curl_args`) and the new `url_options` (which contains `post_form` or `post_json` values that are used to make `POST` requests). I recently added `url_options` as a temporary way of enabling `POST` support without `Options` but this restores the original `Options`-based implementation. Along the way, I added a `homebrew_curl` parameter to the `url` DSL method, allowing us to set an explicit value in `livecheck` blocks. This is something that we've needed in some cases but I also intend to replace implicit/inferred `homebrew_curl` usage with explicit values in `livecheck` blocks once this is available for use. My intention is to eventually remove the implicit behavior and only rely on explicit values. That will align with how `homebrew_curl` options work for other URLs and makes the behavior clear just from looking at the `livecheck` block. Lastly, this removes the `unused` rest parameter from `find_versions` methods. I originally added `unused` as a way of handling parameters that some `find_versions` methods have but others don't (e.g., `cask` in `ExtractPlist`), as this allowed us to pass various arguments to `find_versions` methods without worrying about whether a particular parameter is available. This isn't an ideal solution and I originally wanted to handle this situation by only passing expected arguments to `find_versions` methods but there was a technical issue standing in the way. I recently found an answer to the issue, so this also replaces the existing `ExtractPlist` special case with generic logic that checks the parameters for a strategy's `find_versions` method and only passes expected arguments. Replacing the aforementioned `find_versions` parameters with `Options` ensures that the remaining parameters are fairly consistent across strategies and any differences are handled by the aforementioned logic. Outside of `ExtractPlist`, the only other difference is that some `find_versions` methods have a `provided_content` parameter but that's currently only used by tests (though it's intended for caching support in the future). I will be renaming that parameter to `content` in an upcoming PR and expanding it to the other strategies, which should make them all consistent outside of `ExtractPlist`.
2025-02-11 18:04:38 -05:00
"options" => Homebrew::Livecheck::Options.new.to_hash,
"cask" => nil,
"formula" => nil,
"regex" => nil,
"skip" => false,
"skip_msg" => nil,
"strategy" => nil,
"throttle" => nil,
"url" => nil,
2020-08-05 11:54:37 -04:00
},
)
2020-03-16 01:37:49 +05:30
end
end
end