2020-12-19 01:11:45 -05:00
|
|
|
# typed: false
|
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2021-08-10 11:09:55 -04:00
|
|
|
require "livecheck/strategy"
|
|
|
|
require "bundle_version"
|
2020-12-19 01:11:45 -05:00
|
|
|
|
|
|
|
describe Homebrew::Livecheck::Strategy::Sparkle do
|
|
|
|
subject(:sparkle) { described_class }
|
|
|
|
|
2021-08-10 17:39:11 -04:00
|
|
|
let(:appcast_url) { "https://www.example.com/example/appcast.xml" }
|
|
|
|
let(:non_http_url) { "ftp://brew.sh/" }
|
2020-12-19 01:11:45 -05:00
|
|
|
|
2022-05-31 13:18:40 -04:00
|
|
|
let(:item_hash) {
|
|
|
|
[
|
|
|
|
{
|
|
|
|
title: "Version 1.2.3",
|
|
|
|
pub_date: "Fri, 01 Jan 2021 01:23:45 +0000",
|
|
|
|
url: "https://www.example.com/example/example-1.2.3.tar.gz",
|
|
|
|
short_version: "1.2.3",
|
|
|
|
version: "123",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
title: "Version 1.2.2",
|
|
|
|
pub_date: "Not a parseable date string",
|
|
|
|
url: "https://www.example.com/example/example-1.2.2.tar.gz",
|
|
|
|
short_version: "1.2.2",
|
|
|
|
version: "122",
|
|
|
|
},
|
|
|
|
]
|
2020-12-20 01:56:54 -05:00
|
|
|
}
|
|
|
|
|
2022-05-31 13:18:40 -04:00
|
|
|
let(:xml) {
|
|
|
|
first_item = <<~EOS
|
|
|
|
<item>
|
|
|
|
<title>#{item_hash[0][:title]}</title>
|
|
|
|
<sparkle:minimumSystemVersion>10.10</sparkle:minimumSystemVersion>
|
|
|
|
<sparkle:releaseNotesLink>https://www.example.com/example/#{item_hash[0][:short_version]}.html</sparkle:releaseNotesLink>
|
|
|
|
<pubDate>#{item_hash[0][:pub_date]}</pubDate>
|
|
|
|
<enclosure url="#{item_hash[0][:url]}" sparkle:shortVersionString="#{item_hash[0][:short_version]}" sparkle:version="#{item_hash[0][:version]}" length="12345678" type="application/octet-stream" sparkle:dsaSignature="ABCDEF+GHIJKLMNOPQRSTUVWXYZab/cdefghijklmnopqrst/uvwxyz1234567==" />
|
|
|
|
</item>
|
|
|
|
EOS
|
|
|
|
|
|
|
|
second_item = <<~EOS
|
|
|
|
<item>
|
|
|
|
<title>#{item_hash[1][:title]}</title>
|
|
|
|
<sparkle:minimumSystemVersion>10.10</sparkle:minimumSystemVersion>
|
|
|
|
<sparkle:releaseNotesLink>https://www.example.com/example/#{item_hash[1][:short_version]}.html</sparkle:releaseNotesLink>
|
|
|
|
<pubDate>#{item_hash[1][:pub_date]}</pubDate>
|
|
|
|
<sparkle:version>#{item_hash[1][:version]}</sparkle:version>
|
|
|
|
<sparkle:shortVersionString>#{item_hash[1][:short_version]}</sparkle:shortVersionString>
|
|
|
|
<link>#{item_hash[1][:url]}</link>
|
|
|
|
</item>
|
|
|
|
EOS
|
|
|
|
|
|
|
|
appcast_xml = <<~EOS
|
2020-12-20 01:56:54 -05:00
|
|
|
<?xml version="1.0" encoding="utf-8"?>
|
|
|
|
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle">
|
|
|
|
<channel>
|
|
|
|
<title>Example Changelog</title>
|
2021-08-10 17:39:11 -04:00
|
|
|
<link>#{appcast_url}</link>
|
2020-12-20 01:56:54 -05:00
|
|
|
<description>Most recent changes with links to updates.</description>
|
|
|
|
<language>en</language>
|
2022-05-31 13:18:40 -04:00
|
|
|
#{first_item}
|
|
|
|
#{second_item}
|
2020-12-20 01:56:54 -05:00
|
|
|
</channel>
|
|
|
|
</rss>
|
|
|
|
EOS
|
2022-05-31 13:18:40 -04:00
|
|
|
|
|
|
|
extra_items = <<~EOS
|
|
|
|
#{first_item.sub(%r{<(enclosure[^>]+?)\s*?/>}, '<\1 os="not-osx" />')}
|
|
|
|
#{first_item.sub(/(<sparkle:minimumSystemVersion>)[^<]+?</m, '\1100<')}
|
|
|
|
#{first_item.sub(/(<sparkle:minimumSystemVersion>)[^<]+?</m, '\19000<')}
|
|
|
|
<item>
|
|
|
|
</item>
|
|
|
|
EOS
|
|
|
|
|
|
|
|
appcast_with_omitted_items = appcast_xml.sub("</item>", "</item>\n#{extra_items}")
|
2022-06-02 10:04:34 -04:00
|
|
|
beta_channel_item = appcast_xml.sub(
|
|
|
|
first_item,
|
|
|
|
first_item.sub(
|
|
|
|
"</title",
|
|
|
|
"</title>\n<sparkle:channel>beta</sparkle:channel>",
|
|
|
|
),
|
|
|
|
)
|
2022-05-31 13:18:40 -04:00
|
|
|
no_versions_item =
|
|
|
|
appcast_xml
|
|
|
|
.sub(second_item, "")
|
|
|
|
.gsub(/sparkle:(shortVersionString|version)="[^"]+?"\s*/, "")
|
|
|
|
.sub(
|
|
|
|
"<title>#{item_hash[0][:title]}</title>",
|
|
|
|
"<title>Version</title>",
|
|
|
|
)
|
|
|
|
no_items = appcast_xml.sub(%r{<item>.+</item>}m, "")
|
|
|
|
undefined_namespace_xml = appcast_xml.sub(/\s*xmlns:sparkle="[^"]+?"/, "")
|
|
|
|
|
|
|
|
{
|
|
|
|
appcast: appcast_xml,
|
|
|
|
appcast_with_omitted_items: appcast_with_omitted_items,
|
2022-06-02 10:04:34 -04:00
|
|
|
beta_channel_item: beta_channel_item,
|
2022-05-31 13:18:40 -04:00
|
|
|
no_versions_item: no_versions_item,
|
|
|
|
no_items: no_items,
|
|
|
|
undefined_namespace: undefined_namespace_xml,
|
|
|
|
}
|
2020-12-20 01:56:54 -05:00
|
|
|
}
|
|
|
|
|
2021-11-16 12:31:54 -05:00
|
|
|
let(:title_regex) { /Version\s+v?(\d+(?:\.\d+)+)\s*$/i }
|
|
|
|
|
2022-05-31 13:18:40 -04:00
|
|
|
let(:items) {
|
2022-06-02 10:04:34 -04:00
|
|
|
items = {
|
2022-05-31 13:18:40 -04:00
|
|
|
appcast: [
|
|
|
|
Homebrew::Livecheck::Strategy::Sparkle::Item.new(
|
|
|
|
title: item_hash[0][:title],
|
|
|
|
pub_date: Time.parse(item_hash[0][:pub_date]),
|
|
|
|
url: item_hash[0][:url],
|
|
|
|
bundle_version: Homebrew::BundleVersion.new(item_hash[0][:short_version], item_hash[0][:version]),
|
|
|
|
),
|
|
|
|
Homebrew::Livecheck::Strategy::Sparkle::Item.new(
|
|
|
|
title: item_hash[1][:title],
|
|
|
|
pub_date: Time.new(0),
|
|
|
|
url: item_hash[1][:url],
|
|
|
|
bundle_version: Homebrew::BundleVersion.new(item_hash[1][:short_version], item_hash[1][:version]),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
no_versions_item: [
|
|
|
|
Homebrew::Livecheck::Strategy::Sparkle::Item.new(
|
|
|
|
title: "Version",
|
|
|
|
pub_date: Time.parse(item_hash[0][:pub_date]),
|
|
|
|
url: item_hash[0][:url],
|
|
|
|
bundle_version: nil,
|
|
|
|
),
|
|
|
|
],
|
|
|
|
}
|
2022-06-02 10:04:34 -04:00
|
|
|
|
|
|
|
beta_channel_item = items[:appcast][0].clone
|
|
|
|
beta_channel_item.channel = "beta"
|
|
|
|
items[:beta_channel_item] = [beta_channel_item, items[:appcast][1].clone]
|
|
|
|
|
|
|
|
items
|
2021-08-10 11:09:55 -04:00
|
|
|
}
|
|
|
|
|
2022-05-31 13:18:40 -04:00
|
|
|
let(:versions) { [items[:appcast][0].nice_version] }
|
2021-08-10 11:09:55 -04:00
|
|
|
|
2020-12-19 01:11:45 -05:00
|
|
|
describe "::match?" do
|
2021-08-10 17:39:11 -04:00
|
|
|
it "returns true for an HTTP URL" do
|
|
|
|
expect(sparkle.match?(appcast_url)).to be true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns false for a non-HTTP URL" do
|
|
|
|
expect(sparkle.match?(non_http_url)).to be false
|
2020-12-19 01:11:45 -05:00
|
|
|
end
|
|
|
|
end
|
2020-12-20 01:56:54 -05:00
|
|
|
|
2022-05-31 13:18:40 -04:00
|
|
|
describe "::items_from_content" do
|
|
|
|
let(:items_from_appcast_xml) { sparkle.items_from_content(xml[:appcast]) }
|
|
|
|
let(:first_item) { items_from_appcast_xml[0] }
|
2020-12-20 01:56:54 -05:00
|
|
|
|
|
|
|
it "returns nil if content is blank" do
|
2022-05-31 13:18:40 -04:00
|
|
|
expect(sparkle.items_from_content("")).to eq([])
|
2020-12-20 01:56:54 -05:00
|
|
|
end
|
|
|
|
|
2022-05-31 13:18:40 -04:00
|
|
|
it "returns an array of Items when given XML data" do
|
|
|
|
expect(items_from_appcast_xml).to eq(items[:appcast])
|
|
|
|
expect(first_item.title).to eq(item_hash[0][:title])
|
|
|
|
expect(first_item.pub_date).to eq(Time.parse(item_hash[0][:pub_date]))
|
|
|
|
expect(first_item.url).to eq(item_hash[0][:url])
|
|
|
|
expect(first_item.short_version).to eq(item_hash[0][:short_version])
|
|
|
|
expect(first_item.version).to eq(item_hash[0][:version])
|
|
|
|
|
2022-06-02 10:04:34 -04:00
|
|
|
expect(sparkle.items_from_content(xml[:beta_channel_item])).to eq(items[:beta_channel_item])
|
2022-05-31 13:18:40 -04:00
|
|
|
expect(sparkle.items_from_content(xml[:no_versions_item])).to eq(items[:no_versions_item])
|
2020-12-20 01:56:54 -05:00
|
|
|
end
|
|
|
|
end
|
2021-08-10 11:09:55 -04:00
|
|
|
|
|
|
|
describe "::versions_from_content" do
|
2022-05-31 13:18:40 -04:00
|
|
|
let(:subbed_items) { items[:appcast].map { |item| item.nice_version.sub("1", "0") } }
|
|
|
|
|
2021-08-10 11:09:55 -04:00
|
|
|
it "returns an array of version strings when given content" do
|
2022-05-31 13:18:40 -04:00
|
|
|
expect(sparkle.versions_from_content(xml[:appcast])).to eq(versions)
|
|
|
|
expect(sparkle.versions_from_content(xml[:appcast_with_omitted_items])).to eq(versions)
|
2022-06-02 10:04:34 -04:00
|
|
|
expect(sparkle.versions_from_content(xml[:beta_channel_item])).to eq(versions)
|
2022-05-31 13:18:40 -04:00
|
|
|
expect(sparkle.versions_from_content(xml[:no_versions_item])).to eq([])
|
|
|
|
expect(sparkle.versions_from_content(xml[:undefined_namespace])).to eq(versions)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns an empty array if no items are found" do
|
|
|
|
expect(sparkle.versions_from_content(xml[:no_items])).to eq([])
|
2021-08-10 11:09:55 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "returns an array of version strings when given content and a block" do
|
|
|
|
# Returning a string from block
|
|
|
|
expect(
|
2022-05-31 13:18:40 -04:00
|
|
|
sparkle.versions_from_content(xml[:appcast]) do |item|
|
|
|
|
item.nice_version&.sub("1", "0")
|
2021-08-10 11:09:55 -04:00
|
|
|
end,
|
2022-05-31 13:18:40 -04:00
|
|
|
).to eq([subbed_items[0]])
|
2021-08-10 11:09:55 -04:00
|
|
|
|
2022-05-31 13:18:40 -04:00
|
|
|
# Returning an array of strings from block
|
|
|
|
expect(
|
|
|
|
sparkle.versions_from_content(xml[:appcast]) do |items|
|
|
|
|
items.map { |item| item.nice_version&.sub("1", "0") }
|
|
|
|
end,
|
|
|
|
).to eq(subbed_items)
|
2022-06-02 10:04:34 -04:00
|
|
|
|
|
|
|
expect(
|
|
|
|
sparkle.versions_from_content(xml[:beta_channel_item]) do |items|
|
|
|
|
items.find { |item| item.channel.nil? }&.nice_version
|
|
|
|
end,
|
|
|
|
).to eq([items[:appcast][1].nice_version])
|
2021-08-10 11:09:55 -04:00
|
|
|
end
|
|
|
|
|
2021-11-16 12:31:54 -05:00
|
|
|
it "returns an array of version strings when given content, a regex, and a block" do
|
2022-05-31 13:18:40 -04:00
|
|
|
# Returning a string from the block
|
2021-11-16 12:31:54 -05:00
|
|
|
expect(
|
2022-05-31 13:18:40 -04:00
|
|
|
sparkle.versions_from_content(xml[:appcast], title_regex) do |item, regex|
|
2021-11-16 12:31:54 -05:00
|
|
|
item.title[regex, 1]
|
|
|
|
end,
|
2022-05-31 13:18:40 -04:00
|
|
|
).to eq([items[:appcast][0].short_version])
|
2021-11-16 12:31:54 -05:00
|
|
|
|
|
|
|
expect(
|
2022-05-31 13:18:40 -04:00
|
|
|
sparkle.versions_from_content(xml[:appcast], title_regex) do |items, regex|
|
|
|
|
next if (item = items[0]).blank?
|
|
|
|
|
|
|
|
match = item&.title&.match(regex)
|
|
|
|
next if match.blank?
|
|
|
|
|
|
|
|
"#{match[1]},#{item.version}"
|
|
|
|
end,
|
|
|
|
).to eq(["#{items[:appcast][0].short_version},#{items[:appcast][0].version}"])
|
|
|
|
|
|
|
|
# Returning an array of strings from the block
|
|
|
|
expect(
|
|
|
|
sparkle.versions_from_content(xml[:appcast], title_regex) do |item, regex|
|
2021-11-16 12:31:54 -05:00
|
|
|
[item.title[regex, 1]]
|
|
|
|
end,
|
2022-05-31 13:18:40 -04:00
|
|
|
).to eq([items[:appcast][0].short_version])
|
|
|
|
|
|
|
|
expect(
|
|
|
|
sparkle.versions_from_content(xml[:appcast], &:short_version),
|
|
|
|
).to eq([items[:appcast][0].short_version])
|
|
|
|
|
|
|
|
expect(
|
|
|
|
sparkle.versions_from_content(xml[:appcast], title_regex) do |items, regex|
|
|
|
|
items.map { |item| item.title[regex, 1] }
|
|
|
|
end,
|
|
|
|
).to eq(items[:appcast].map(&:short_version))
|
2021-11-16 12:31:54 -05:00
|
|
|
end
|
|
|
|
|
2021-08-10 11:09:55 -04:00
|
|
|
it "allows a nil return from a block" do
|
2022-05-31 13:18:40 -04:00
|
|
|
expect(
|
|
|
|
sparkle.versions_from_content(xml[:appcast]) do |item|
|
|
|
|
_ = item # To appease `brew style` without modifying arg name
|
|
|
|
next
|
|
|
|
end,
|
|
|
|
).to eq([])
|
2021-08-10 11:09:55 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "errors on an invalid return type from a block" do
|
2022-05-31 13:18:40 -04:00
|
|
|
expect {
|
|
|
|
sparkle.versions_from_content(xml[:appcast]) do |item|
|
|
|
|
_ = item # To appease `brew style` without modifying arg name
|
|
|
|
123
|
|
|
|
end
|
|
|
|
}.to raise_error(TypeError, Homebrew::Livecheck::Strategy::INVALID_BLOCK_RETURN_VALUE_MSG)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "errors if the first block argument uses an unhandled name" do
|
|
|
|
expect { sparkle.versions_from_content(xml[:appcast]) { |something| something } }
|
|
|
|
.to raise_error("First argument of Sparkle `strategy` block must be `item` or `items`")
|
2021-08-10 11:09:55 -04:00
|
|
|
end
|
|
|
|
end
|
2020-12-19 01:11:45 -05:00
|
|
|
end
|