brew/Library/Homebrew/test/livecheck_spec.rb
Sam Ford b4757af656
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-07 08:53:47 -05:00

186 lines
4.5 KiB
Ruby

# frozen_string_literal: true
require "formula"
require "livecheck"
RSpec.describe Livecheck do
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"
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) }
let(:post_hash) do
{
"empty" => "",
"boolean" => "true",
"number" => "1",
"string" => "a + b = c",
}
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)
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
describe "#regex" do
it "returns nil if not set" do
expect(livecheck_f.regex).to be_nil
end
it "returns the Regexp if set" do
livecheck_f.regex(/foo/)
expect(livecheck_f.regex).to eq(/foo/)
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
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")
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
end
end
describe "#strategy" do
it "returns nil if not set" do
expect(livecheck_f.strategy).to be_nil
end
it "returns the Symbol if set" do
livecheck_f.strategy(:page_match)
expect(livecheck_f.strategy).to eq(:page_match)
end
end
describe "#throttle" do
it "returns nil if not set" do
expect(livecheck_f.throttle).to be_nil
end
it "returns the Integer if set" do
livecheck_f.throttle(10)
expect(livecheck_f.throttle).to eq(10)
end
end
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)
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
it "sets `url_options` when provided" do
post_args = { post_form: post_hash }
livecheck_f.url(url_string, **post_args)
expect(livecheck_f.url_options).to eq(post_args)
end
it "raises an ArgumentError if the argument isn't a valid Symbol" do
expect do
livecheck_f.url(:not_a_valid_symbol)
end.to raise_error ArgumentError
end
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
end
describe "#to_hash" do
it "returns a Hash of all instance variables" do
expect(livecheck_f.to_hash).to eq(
{
"cask" => nil,
"formula" => nil,
"regex" => nil,
"skip" => false,
"skip_msg" => nil,
"strategy" => nil,
"throttle" => nil,
"url" => nil,
"url_options" => nil,
},
)
end
end
end