250 lines
9.0 KiB
Ruby
Raw Normal View History

2020-12-19 01:11:45 -05:00
# typed: false
# frozen_string_literal: true
Standardize valid strategy block return types Valid `strategy` block return types currently vary between strategies. Some only accept a string whereas others accept a string or array of strings. [`strategy` blocks also accept a `nil` return (to simplify early returns) but this was already standardized across strategies.] While some strategies only identify one version by default (where a string is an appropriate return type), it could be that a strategy block identifies more than one version. In this situation, the strategy would need to be modified to accept (and work with) an array from a `strategy` block. Rather than waiting for this to become a problem, this modifies all strategies to standardize on allowing `strategy` blocks to return a string or array of strings (even if only one of these is currently used in practice). Standardizing valid return types helps to further simplify the mental model for `strategy` blocks and reduce cognitive load. This commit extracts related logic from `#find_versions` into methods like `#versions_from_content`, which is conceptually similar to `PageMatch#page_matches` (renamed to `#versions_from_content` for consistency). This allows us to write tests for the related code without having to make network requests (or stub them) at this point. In general, this also helps to better align the structure of strategies and how the various `#find_versions` methods work with versions. There's still more planned work to be done here but this is a step in the right direction.
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 }
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
Sparkle: Pass all items into strategy block It's sometimes necessary to work with all the items in a Sparkle feed to be able to correctly identify the newest version but livecheck's `Sparkle` strategy only passes the `item` it views as newest into a `strategy` block. This updates the `Sparkle` strategy to optionally pass all items into a `strategy` block, so we can manipulate them (e.g., filtering, sorting). This is enabled by naming the first argument of the strategy block `items` instead of `item`. `Sparkle` `strategy` blocks where the first argument is `item` will continue to work as expected. This necessarily updates `#item_from_content` (now `items_from_content`) to return all items. I've decided to move the sorting out of `#items_from_content`, so it simply returns the items in the order they appear. If there is ever an exceptional situation where we need the original order, this will technically allow for it. The sorting has instead been moved into the `#versions_from_content` method, to maintain the existing behavior. I thought about passing the items into the `strategy` block in their original order but it feels like sorting by default is the better approach for now (partly from the perspective of maintaining existing behavior) and we can always revisit this in the future if a cask ever requires the original order. Lastly, this expands the `Sparkle` tests to increase coverage. The only untested parts are `#find_versions` (which currently requires a network request) and a couple safeguard `raise` calls when there's a `REXML::UndefinedNamespaceException` (which shouldn't be encountered unless something is broken).
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
}
Sparkle: Pass all items into strategy block It's sometimes necessary to work with all the items in a Sparkle feed to be able to correctly identify the newest version but livecheck's `Sparkle` strategy only passes the `item` it views as newest into a `strategy` block. This updates the `Sparkle` strategy to optionally pass all items into a `strategy` block, so we can manipulate them (e.g., filtering, sorting). This is enabled by naming the first argument of the strategy block `items` instead of `item`. `Sparkle` `strategy` blocks where the first argument is `item` will continue to work as expected. This necessarily updates `#item_from_content` (now `items_from_content`) to return all items. I've decided to move the sorting out of `#items_from_content`, so it simply returns the items in the order they appear. If there is ever an exceptional situation where we need the original order, this will technically allow for it. The sorting has instead been moved into the `#versions_from_content` method, to maintain the existing behavior. I thought about passing the items into the `strategy` block in their original order but it feels like sorting by default is the better approach for now (partly from the perspective of maintaining existing behavior) and we can always revisit this in the future if a cask ever requires the original order. Lastly, this expands the `Sparkle` tests to increase coverage. The only untested parts are `#find_versions` (which currently requires a network request) and a couple safeguard `raise` calls when there's a `REXML::UndefinedNamespaceException` (which shouldn't be encountered unless something is broken).
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>
<link>#{appcast_url}</link>
2020-12-20 01:56:54 -05:00
<description>Most recent changes with links to updates.</description>
<language>en</language>
Sparkle: Pass all items into strategy block It's sometimes necessary to work with all the items in a Sparkle feed to be able to correctly identify the newest version but livecheck's `Sparkle` strategy only passes the `item` it views as newest into a `strategy` block. This updates the `Sparkle` strategy to optionally pass all items into a `strategy` block, so we can manipulate them (e.g., filtering, sorting). This is enabled by naming the first argument of the strategy block `items` instead of `item`. `Sparkle` `strategy` blocks where the first argument is `item` will continue to work as expected. This necessarily updates `#item_from_content` (now `items_from_content`) to return all items. I've decided to move the sorting out of `#items_from_content`, so it simply returns the items in the order they appear. If there is ever an exceptional situation where we need the original order, this will technically allow for it. The sorting has instead been moved into the `#versions_from_content` method, to maintain the existing behavior. I thought about passing the items into the `strategy` block in their original order but it feels like sorting by default is the better approach for now (partly from the perspective of maintaining existing behavior) and we can always revisit this in the future if a cask ever requires the original order. Lastly, this expands the `Sparkle` tests to increase coverage. The only untested parts are `#find_versions` (which currently requires a network request) and a couple safeguard `raise` calls when there's a `REXML::UndefinedNamespaceException` (which shouldn't be encountered unless something is broken).
2022-05-31 13:18:40 -04:00
#{first_item}
#{second_item}
2020-12-20 01:56:54 -05:00
</channel>
</rss>
EOS
Sparkle: Pass all items into strategy block It's sometimes necessary to work with all the items in a Sparkle feed to be able to correctly identify the newest version but livecheck's `Sparkle` strategy only passes the `item` it views as newest into a `strategy` block. This updates the `Sparkle` strategy to optionally pass all items into a `strategy` block, so we can manipulate them (e.g., filtering, sorting). This is enabled by naming the first argument of the strategy block `items` instead of `item`. `Sparkle` `strategy` blocks where the first argument is `item` will continue to work as expected. This necessarily updates `#item_from_content` (now `items_from_content`) to return all items. I've decided to move the sorting out of `#items_from_content`, so it simply returns the items in the order they appear. If there is ever an exceptional situation where we need the original order, this will technically allow for it. The sorting has instead been moved into the `#versions_from_content` method, to maintain the existing behavior. I thought about passing the items into the `strategy` block in their original order but it feels like sorting by default is the better approach for now (partly from the perspective of maintaining existing behavior) and we can always revisit this in the future if a cask ever requires the original order. Lastly, this expands the `Sparkle` tests to increase coverage. The only untested parts are `#find_versions` (which currently requires a network request) and a couple safeguard `raise` calls when there's a `REXML::UndefinedNamespaceException` (which shouldn't be encountered unless something is broken).
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}")
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,
no_versions_item: no_versions_item,
no_items: no_items,
undefined_namespace: undefined_namespace_xml,
}
2020-12-20 01:56:54 -05:00
}
let(:title_regex) { /Version\s+v?(\d+(?:\.\d+)+)\s*$/i }
Sparkle: Pass all items into strategy block It's sometimes necessary to work with all the items in a Sparkle feed to be able to correctly identify the newest version but livecheck's `Sparkle` strategy only passes the `item` it views as newest into a `strategy` block. This updates the `Sparkle` strategy to optionally pass all items into a `strategy` block, so we can manipulate them (e.g., filtering, sorting). This is enabled by naming the first argument of the strategy block `items` instead of `item`. `Sparkle` `strategy` blocks where the first argument is `item` will continue to work as expected. This necessarily updates `#item_from_content` (now `items_from_content`) to return all items. I've decided to move the sorting out of `#items_from_content`, so it simply returns the items in the order they appear. If there is ever an exceptional situation where we need the original order, this will technically allow for it. The sorting has instead been moved into the `#versions_from_content` method, to maintain the existing behavior. I thought about passing the items into the `strategy` block in their original order but it feels like sorting by default is the better approach for now (partly from the perspective of maintaining existing behavior) and we can always revisit this in the future if a cask ever requires the original order. Lastly, this expands the `Sparkle` tests to increase coverage. The only untested parts are `#find_versions` (which currently requires a network request) and a couple safeguard `raise` calls when there's a `REXML::UndefinedNamespaceException` (which shouldn't be encountered unless something is broken).
2022-05-31 13:18:40 -04:00
let(:items) {
{
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,
),
],
}
Standardize valid strategy block return types Valid `strategy` block return types currently vary between strategies. Some only accept a string whereas others accept a string or array of strings. [`strategy` blocks also accept a `nil` return (to simplify early returns) but this was already standardized across strategies.] While some strategies only identify one version by default (where a string is an appropriate return type), it could be that a strategy block identifies more than one version. In this situation, the strategy would need to be modified to accept (and work with) an array from a `strategy` block. Rather than waiting for this to become a problem, this modifies all strategies to standardize on allowing `strategy` blocks to return a string or array of strings (even if only one of these is currently used in practice). Standardizing valid return types helps to further simplify the mental model for `strategy` blocks and reduce cognitive load. This commit extracts related logic from `#find_versions` into methods like `#versions_from_content`, which is conceptually similar to `PageMatch#page_matches` (renamed to `#versions_from_content` for consistency). This allows us to write tests for the related code without having to make network requests (or stub them) at this point. In general, this also helps to better align the structure of strategies and how the various `#find_versions` methods work with versions. There's still more planned work to be done here but this is a step in the right direction.
2021-08-10 11:09:55 -04:00
}
Sparkle: Pass all items into strategy block It's sometimes necessary to work with all the items in a Sparkle feed to be able to correctly identify the newest version but livecheck's `Sparkle` strategy only passes the `item` it views as newest into a `strategy` block. This updates the `Sparkle` strategy to optionally pass all items into a `strategy` block, so we can manipulate them (e.g., filtering, sorting). This is enabled by naming the first argument of the strategy block `items` instead of `item`. `Sparkle` `strategy` blocks where the first argument is `item` will continue to work as expected. This necessarily updates `#item_from_content` (now `items_from_content`) to return all items. I've decided to move the sorting out of `#items_from_content`, so it simply returns the items in the order they appear. If there is ever an exceptional situation where we need the original order, this will technically allow for it. The sorting has instead been moved into the `#versions_from_content` method, to maintain the existing behavior. I thought about passing the items into the `strategy` block in their original order but it feels like sorting by default is the better approach for now (partly from the perspective of maintaining existing behavior) and we can always revisit this in the future if a cask ever requires the original order. Lastly, this expands the `Sparkle` tests to increase coverage. The only untested parts are `#find_versions` (which currently requires a network request) and a couple safeguard `raise` calls when there's a `REXML::UndefinedNamespaceException` (which shouldn't be encountered unless something is broken).
2022-05-31 13:18:40 -04:00
let(:versions) { [items[:appcast][0].nice_version] }
Standardize valid strategy block return types Valid `strategy` block return types currently vary between strategies. Some only accept a string whereas others accept a string or array of strings. [`strategy` blocks also accept a `nil` return (to simplify early returns) but this was already standardized across strategies.] While some strategies only identify one version by default (where a string is an appropriate return type), it could be that a strategy block identifies more than one version. In this situation, the strategy would need to be modified to accept (and work with) an array from a `strategy` block. Rather than waiting for this to become a problem, this modifies all strategies to standardize on allowing `strategy` blocks to return a string or array of strings (even if only one of these is currently used in practice). Standardizing valid return types helps to further simplify the mental model for `strategy` blocks and reduce cognitive load. This commit extracts related logic from `#find_versions` into methods like `#versions_from_content`, which is conceptually similar to `PageMatch#page_matches` (renamed to `#versions_from_content` for consistency). This allows us to write tests for the related code without having to make network requests (or stub them) at this point. In general, this also helps to better align the structure of strategies and how the various `#find_versions` methods work with versions. There's still more planned work to be done here but this is a step in the right direction.
2021-08-10 11:09:55 -04:00
2020-12-19 01:11:45 -05:00
describe "::match?" do
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
Sparkle: Pass all items into strategy block It's sometimes necessary to work with all the items in a Sparkle feed to be able to correctly identify the newest version but livecheck's `Sparkle` strategy only passes the `item` it views as newest into a `strategy` block. This updates the `Sparkle` strategy to optionally pass all items into a `strategy` block, so we can manipulate them (e.g., filtering, sorting). This is enabled by naming the first argument of the strategy block `items` instead of `item`. `Sparkle` `strategy` blocks where the first argument is `item` will continue to work as expected. This necessarily updates `#item_from_content` (now `items_from_content`) to return all items. I've decided to move the sorting out of `#items_from_content`, so it simply returns the items in the order they appear. If there is ever an exceptional situation where we need the original order, this will technically allow for it. The sorting has instead been moved into the `#versions_from_content` method, to maintain the existing behavior. I thought about passing the items into the `strategy` block in their original order but it feels like sorting by default is the better approach for now (partly from the perspective of maintaining existing behavior) and we can always revisit this in the future if a cask ever requires the original order. Lastly, this expands the `Sparkle` tests to increase coverage. The only untested parts are `#find_versions` (which currently requires a network request) and a couple safeguard `raise` calls when there's a `REXML::UndefinedNamespaceException` (which shouldn't be encountered unless something is broken).
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
Sparkle: Pass all items into strategy block It's sometimes necessary to work with all the items in a Sparkle feed to be able to correctly identify the newest version but livecheck's `Sparkle` strategy only passes the `item` it views as newest into a `strategy` block. This updates the `Sparkle` strategy to optionally pass all items into a `strategy` block, so we can manipulate them (e.g., filtering, sorting). This is enabled by naming the first argument of the strategy block `items` instead of `item`. `Sparkle` `strategy` blocks where the first argument is `item` will continue to work as expected. This necessarily updates `#item_from_content` (now `items_from_content`) to return all items. I've decided to move the sorting out of `#items_from_content`, so it simply returns the items in the order they appear. If there is ever an exceptional situation where we need the original order, this will technically allow for it. The sorting has instead been moved into the `#versions_from_content` method, to maintain the existing behavior. I thought about passing the items into the `strategy` block in their original order but it feels like sorting by default is the better approach for now (partly from the perspective of maintaining existing behavior) and we can always revisit this in the future if a cask ever requires the original order. Lastly, this expands the `Sparkle` tests to increase coverage. The only untested parts are `#find_versions` (which currently requires a network request) and a couple safeguard `raise` calls when there's a `REXML::UndefinedNamespaceException` (which shouldn't be encountered unless something is broken).
2022-05-31 13:18:40 -04:00
expect(sparkle.items_from_content("")).to eq([])
2020-12-20 01:56:54 -05:00
end
Sparkle: Pass all items into strategy block It's sometimes necessary to work with all the items in a Sparkle feed to be able to correctly identify the newest version but livecheck's `Sparkle` strategy only passes the `item` it views as newest into a `strategy` block. This updates the `Sparkle` strategy to optionally pass all items into a `strategy` block, so we can manipulate them (e.g., filtering, sorting). This is enabled by naming the first argument of the strategy block `items` instead of `item`. `Sparkle` `strategy` blocks where the first argument is `item` will continue to work as expected. This necessarily updates `#item_from_content` (now `items_from_content`) to return all items. I've decided to move the sorting out of `#items_from_content`, so it simply returns the items in the order they appear. If there is ever an exceptional situation where we need the original order, this will technically allow for it. The sorting has instead been moved into the `#versions_from_content` method, to maintain the existing behavior. I thought about passing the items into the `strategy` block in their original order but it feels like sorting by default is the better approach for now (partly from the perspective of maintaining existing behavior) and we can always revisit this in the future if a cask ever requires the original order. Lastly, this expands the `Sparkle` tests to increase coverage. The only untested parts are `#find_versions` (which currently requires a network request) and a couple safeguard `raise` calls when there's a `REXML::UndefinedNamespaceException` (which shouldn't be encountered unless something is broken).
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])
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
Standardize valid strategy block return types Valid `strategy` block return types currently vary between strategies. Some only accept a string whereas others accept a string or array of strings. [`strategy` blocks also accept a `nil` return (to simplify early returns) but this was already standardized across strategies.] While some strategies only identify one version by default (where a string is an appropriate return type), it could be that a strategy block identifies more than one version. In this situation, the strategy would need to be modified to accept (and work with) an array from a `strategy` block. Rather than waiting for this to become a problem, this modifies all strategies to standardize on allowing `strategy` blocks to return a string or array of strings (even if only one of these is currently used in practice). Standardizing valid return types helps to further simplify the mental model for `strategy` blocks and reduce cognitive load. This commit extracts related logic from `#find_versions` into methods like `#versions_from_content`, which is conceptually similar to `PageMatch#page_matches` (renamed to `#versions_from_content` for consistency). This allows us to write tests for the related code without having to make network requests (or stub them) at this point. In general, this also helps to better align the structure of strategies and how the various `#find_versions` methods work with versions. There's still more planned work to be done here but this is a step in the right direction.
2021-08-10 11:09:55 -04:00
describe "::versions_from_content" do
Sparkle: Pass all items into strategy block It's sometimes necessary to work with all the items in a Sparkle feed to be able to correctly identify the newest version but livecheck's `Sparkle` strategy only passes the `item` it views as newest into a `strategy` block. This updates the `Sparkle` strategy to optionally pass all items into a `strategy` block, so we can manipulate them (e.g., filtering, sorting). This is enabled by naming the first argument of the strategy block `items` instead of `item`. `Sparkle` `strategy` blocks where the first argument is `item` will continue to work as expected. This necessarily updates `#item_from_content` (now `items_from_content`) to return all items. I've decided to move the sorting out of `#items_from_content`, so it simply returns the items in the order they appear. If there is ever an exceptional situation where we need the original order, this will technically allow for it. The sorting has instead been moved into the `#versions_from_content` method, to maintain the existing behavior. I thought about passing the items into the `strategy` block in their original order but it feels like sorting by default is the better approach for now (partly from the perspective of maintaining existing behavior) and we can always revisit this in the future if a cask ever requires the original order. Lastly, this expands the `Sparkle` tests to increase coverage. The only untested parts are `#find_versions` (which currently requires a network request) and a couple safeguard `raise` calls when there's a `REXML::UndefinedNamespaceException` (which shouldn't be encountered unless something is broken).
2022-05-31 13:18:40 -04:00
let(:subbed_items) { items[:appcast].map { |item| item.nice_version.sub("1", "0") } }
Standardize valid strategy block return types Valid `strategy` block return types currently vary between strategies. Some only accept a string whereas others accept a string or array of strings. [`strategy` blocks also accept a `nil` return (to simplify early returns) but this was already standardized across strategies.] While some strategies only identify one version by default (where a string is an appropriate return type), it could be that a strategy block identifies more than one version. In this situation, the strategy would need to be modified to accept (and work with) an array from a `strategy` block. Rather than waiting for this to become a problem, this modifies all strategies to standardize on allowing `strategy` blocks to return a string or array of strings (even if only one of these is currently used in practice). Standardizing valid return types helps to further simplify the mental model for `strategy` blocks and reduce cognitive load. This commit extracts related logic from `#find_versions` into methods like `#versions_from_content`, which is conceptually similar to `PageMatch#page_matches` (renamed to `#versions_from_content` for consistency). This allows us to write tests for the related code without having to make network requests (or stub them) at this point. In general, this also helps to better align the structure of strategies and how the various `#find_versions` methods work with versions. There's still more planned work to be done here but this is a step in the right direction.
2021-08-10 11:09:55 -04:00
it "returns an array of version strings when given content" do
Sparkle: Pass all items into strategy block It's sometimes necessary to work with all the items in a Sparkle feed to be able to correctly identify the newest version but livecheck's `Sparkle` strategy only passes the `item` it views as newest into a `strategy` block. This updates the `Sparkle` strategy to optionally pass all items into a `strategy` block, so we can manipulate them (e.g., filtering, sorting). This is enabled by naming the first argument of the strategy block `items` instead of `item`. `Sparkle` `strategy` blocks where the first argument is `item` will continue to work as expected. This necessarily updates `#item_from_content` (now `items_from_content`) to return all items. I've decided to move the sorting out of `#items_from_content`, so it simply returns the items in the order they appear. If there is ever an exceptional situation where we need the original order, this will technically allow for it. The sorting has instead been moved into the `#versions_from_content` method, to maintain the existing behavior. I thought about passing the items into the `strategy` block in their original order but it feels like sorting by default is the better approach for now (partly from the perspective of maintaining existing behavior) and we can always revisit this in the future if a cask ever requires the original order. Lastly, this expands the `Sparkle` tests to increase coverage. The only untested parts are `#find_versions` (which currently requires a network request) and a couple safeguard `raise` calls when there's a `REXML::UndefinedNamespaceException` (which shouldn't be encountered unless something is broken).
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)
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([])
Standardize valid strategy block return types Valid `strategy` block return types currently vary between strategies. Some only accept a string whereas others accept a string or array of strings. [`strategy` blocks also accept a `nil` return (to simplify early returns) but this was already standardized across strategies.] While some strategies only identify one version by default (where a string is an appropriate return type), it could be that a strategy block identifies more than one version. In this situation, the strategy would need to be modified to accept (and work with) an array from a `strategy` block. Rather than waiting for this to become a problem, this modifies all strategies to standardize on allowing `strategy` blocks to return a string or array of strings (even if only one of these is currently used in practice). Standardizing valid return types helps to further simplify the mental model for `strategy` blocks and reduce cognitive load. This commit extracts related logic from `#find_versions` into methods like `#versions_from_content`, which is conceptually similar to `PageMatch#page_matches` (renamed to `#versions_from_content` for consistency). This allows us to write tests for the related code without having to make network requests (or stub them) at this point. In general, this also helps to better align the structure of strategies and how the various `#find_versions` methods work with versions. There's still more planned work to be done here but this is a step in the right direction.
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(
Sparkle: Pass all items into strategy block It's sometimes necessary to work with all the items in a Sparkle feed to be able to correctly identify the newest version but livecheck's `Sparkle` strategy only passes the `item` it views as newest into a `strategy` block. This updates the `Sparkle` strategy to optionally pass all items into a `strategy` block, so we can manipulate them (e.g., filtering, sorting). This is enabled by naming the first argument of the strategy block `items` instead of `item`. `Sparkle` `strategy` blocks where the first argument is `item` will continue to work as expected. This necessarily updates `#item_from_content` (now `items_from_content`) to return all items. I've decided to move the sorting out of `#items_from_content`, so it simply returns the items in the order they appear. If there is ever an exceptional situation where we need the original order, this will technically allow for it. The sorting has instead been moved into the `#versions_from_content` method, to maintain the existing behavior. I thought about passing the items into the `strategy` block in their original order but it feels like sorting by default is the better approach for now (partly from the perspective of maintaining existing behavior) and we can always revisit this in the future if a cask ever requires the original order. Lastly, this expands the `Sparkle` tests to increase coverage. The only untested parts are `#find_versions` (which currently requires a network request) and a couple safeguard `raise` calls when there's a `REXML::UndefinedNamespaceException` (which shouldn't be encountered unless something is broken).
2022-05-31 13:18:40 -04:00
sparkle.versions_from_content(xml[:appcast]) do |item|
item.nice_version&.sub("1", "0")
Standardize valid strategy block return types Valid `strategy` block return types currently vary between strategies. Some only accept a string whereas others accept a string or array of strings. [`strategy` blocks also accept a `nil` return (to simplify early returns) but this was already standardized across strategies.] While some strategies only identify one version by default (where a string is an appropriate return type), it could be that a strategy block identifies more than one version. In this situation, the strategy would need to be modified to accept (and work with) an array from a `strategy` block. Rather than waiting for this to become a problem, this modifies all strategies to standardize on allowing `strategy` blocks to return a string or array of strings (even if only one of these is currently used in practice). Standardizing valid return types helps to further simplify the mental model for `strategy` blocks and reduce cognitive load. This commit extracts related logic from `#find_versions` into methods like `#versions_from_content`, which is conceptually similar to `PageMatch#page_matches` (renamed to `#versions_from_content` for consistency). This allows us to write tests for the related code without having to make network requests (or stub them) at this point. In general, this also helps to better align the structure of strategies and how the various `#find_versions` methods work with versions. There's still more planned work to be done here but this is a step in the right direction.
2021-08-10 11:09:55 -04:00
end,
Sparkle: Pass all items into strategy block It's sometimes necessary to work with all the items in a Sparkle feed to be able to correctly identify the newest version but livecheck's `Sparkle` strategy only passes the `item` it views as newest into a `strategy` block. This updates the `Sparkle` strategy to optionally pass all items into a `strategy` block, so we can manipulate them (e.g., filtering, sorting). This is enabled by naming the first argument of the strategy block `items` instead of `item`. `Sparkle` `strategy` blocks where the first argument is `item` will continue to work as expected. This necessarily updates `#item_from_content` (now `items_from_content`) to return all items. I've decided to move the sorting out of `#items_from_content`, so it simply returns the items in the order they appear. If there is ever an exceptional situation where we need the original order, this will technically allow for it. The sorting has instead been moved into the `#versions_from_content` method, to maintain the existing behavior. I thought about passing the items into the `strategy` block in their original order but it feels like sorting by default is the better approach for now (partly from the perspective of maintaining existing behavior) and we can always revisit this in the future if a cask ever requires the original order. Lastly, this expands the `Sparkle` tests to increase coverage. The only untested parts are `#find_versions` (which currently requires a network request) and a couple safeguard `raise` calls when there's a `REXML::UndefinedNamespaceException` (which shouldn't be encountered unless something is broken).
2022-05-31 13:18:40 -04:00
).to eq([subbed_items[0]])
Standardize valid strategy block return types Valid `strategy` block return types currently vary between strategies. Some only accept a string whereas others accept a string or array of strings. [`strategy` blocks also accept a `nil` return (to simplify early returns) but this was already standardized across strategies.] While some strategies only identify one version by default (where a string is an appropriate return type), it could be that a strategy block identifies more than one version. In this situation, the strategy would need to be modified to accept (and work with) an array from a `strategy` block. Rather than waiting for this to become a problem, this modifies all strategies to standardize on allowing `strategy` blocks to return a string or array of strings (even if only one of these is currently used in practice). Standardizing valid return types helps to further simplify the mental model for `strategy` blocks and reduce cognitive load. This commit extracts related logic from `#find_versions` into methods like `#versions_from_content`, which is conceptually similar to `PageMatch#page_matches` (renamed to `#versions_from_content` for consistency). This allows us to write tests for the related code without having to make network requests (or stub them) at this point. In general, this also helps to better align the structure of strategies and how the various `#find_versions` methods work with versions. There's still more planned work to be done here but this is a step in the right direction.
2021-08-10 11:09:55 -04:00
Sparkle: Pass all items into strategy block It's sometimes necessary to work with all the items in a Sparkle feed to be able to correctly identify the newest version but livecheck's `Sparkle` strategy only passes the `item` it views as newest into a `strategy` block. This updates the `Sparkle` strategy to optionally pass all items into a `strategy` block, so we can manipulate them (e.g., filtering, sorting). This is enabled by naming the first argument of the strategy block `items` instead of `item`. `Sparkle` `strategy` blocks where the first argument is `item` will continue to work as expected. This necessarily updates `#item_from_content` (now `items_from_content`) to return all items. I've decided to move the sorting out of `#items_from_content`, so it simply returns the items in the order they appear. If there is ever an exceptional situation where we need the original order, this will technically allow for it. The sorting has instead been moved into the `#versions_from_content` method, to maintain the existing behavior. I thought about passing the items into the `strategy` block in their original order but it feels like sorting by default is the better approach for now (partly from the perspective of maintaining existing behavior) and we can always revisit this in the future if a cask ever requires the original order. Lastly, this expands the `Sparkle` tests to increase coverage. The only untested parts are `#find_versions` (which currently requires a network request) and a couple safeguard `raise` calls when there's a `REXML::UndefinedNamespaceException` (which shouldn't be encountered unless something is broken).
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)
Standardize valid strategy block return types Valid `strategy` block return types currently vary between strategies. Some only accept a string whereas others accept a string or array of strings. [`strategy` blocks also accept a `nil` return (to simplify early returns) but this was already standardized across strategies.] While some strategies only identify one version by default (where a string is an appropriate return type), it could be that a strategy block identifies more than one version. In this situation, the strategy would need to be modified to accept (and work with) an array from a `strategy` block. Rather than waiting for this to become a problem, this modifies all strategies to standardize on allowing `strategy` blocks to return a string or array of strings (even if only one of these is currently used in practice). Standardizing valid return types helps to further simplify the mental model for `strategy` blocks and reduce cognitive load. This commit extracts related logic from `#find_versions` into methods like `#versions_from_content`, which is conceptually similar to `PageMatch#page_matches` (renamed to `#versions_from_content` for consistency). This allows us to write tests for the related code without having to make network requests (or stub them) at this point. In general, this also helps to better align the structure of strategies and how the various `#find_versions` methods work with versions. There's still more planned work to be done here but this is a step in the right direction.
2021-08-10 11:09:55 -04:00
end
it "returns an array of version strings when given content, a regex, and a block" do
Sparkle: Pass all items into strategy block It's sometimes necessary to work with all the items in a Sparkle feed to be able to correctly identify the newest version but livecheck's `Sparkle` strategy only passes the `item` it views as newest into a `strategy` block. This updates the `Sparkle` strategy to optionally pass all items into a `strategy` block, so we can manipulate them (e.g., filtering, sorting). This is enabled by naming the first argument of the strategy block `items` instead of `item`. `Sparkle` `strategy` blocks where the first argument is `item` will continue to work as expected. This necessarily updates `#item_from_content` (now `items_from_content`) to return all items. I've decided to move the sorting out of `#items_from_content`, so it simply returns the items in the order they appear. If there is ever an exceptional situation where we need the original order, this will technically allow for it. The sorting has instead been moved into the `#versions_from_content` method, to maintain the existing behavior. I thought about passing the items into the `strategy` block in their original order but it feels like sorting by default is the better approach for now (partly from the perspective of maintaining existing behavior) and we can always revisit this in the future if a cask ever requires the original order. Lastly, this expands the `Sparkle` tests to increase coverage. The only untested parts are `#find_versions` (which currently requires a network request) and a couple safeguard `raise` calls when there's a `REXML::UndefinedNamespaceException` (which shouldn't be encountered unless something is broken).
2022-05-31 13:18:40 -04:00
# Returning a string from the block
expect(
Sparkle: Pass all items into strategy block It's sometimes necessary to work with all the items in a Sparkle feed to be able to correctly identify the newest version but livecheck's `Sparkle` strategy only passes the `item` it views as newest into a `strategy` block. This updates the `Sparkle` strategy to optionally pass all items into a `strategy` block, so we can manipulate them (e.g., filtering, sorting). This is enabled by naming the first argument of the strategy block `items` instead of `item`. `Sparkle` `strategy` blocks where the first argument is `item` will continue to work as expected. This necessarily updates `#item_from_content` (now `items_from_content`) to return all items. I've decided to move the sorting out of `#items_from_content`, so it simply returns the items in the order they appear. If there is ever an exceptional situation where we need the original order, this will technically allow for it. The sorting has instead been moved into the `#versions_from_content` method, to maintain the existing behavior. I thought about passing the items into the `strategy` block in their original order but it feels like sorting by default is the better approach for now (partly from the perspective of maintaining existing behavior) and we can always revisit this in the future if a cask ever requires the original order. Lastly, this expands the `Sparkle` tests to increase coverage. The only untested parts are `#find_versions` (which currently requires a network request) and a couple safeguard `raise` calls when there's a `REXML::UndefinedNamespaceException` (which shouldn't be encountered unless something is broken).
2022-05-31 13:18:40 -04:00
sparkle.versions_from_content(xml[:appcast], title_regex) do |item, regex|
item.title[regex, 1]
end,
Sparkle: Pass all items into strategy block It's sometimes necessary to work with all the items in a Sparkle feed to be able to correctly identify the newest version but livecheck's `Sparkle` strategy only passes the `item` it views as newest into a `strategy` block. This updates the `Sparkle` strategy to optionally pass all items into a `strategy` block, so we can manipulate them (e.g., filtering, sorting). This is enabled by naming the first argument of the strategy block `items` instead of `item`. `Sparkle` `strategy` blocks where the first argument is `item` will continue to work as expected. This necessarily updates `#item_from_content` (now `items_from_content`) to return all items. I've decided to move the sorting out of `#items_from_content`, so it simply returns the items in the order they appear. If there is ever an exceptional situation where we need the original order, this will technically allow for it. The sorting has instead been moved into the `#versions_from_content` method, to maintain the existing behavior. I thought about passing the items into the `strategy` block in their original order but it feels like sorting by default is the better approach for now (partly from the perspective of maintaining existing behavior) and we can always revisit this in the future if a cask ever requires the original order. Lastly, this expands the `Sparkle` tests to increase coverage. The only untested parts are `#find_versions` (which currently requires a network request) and a couple safeguard `raise` calls when there's a `REXML::UndefinedNamespaceException` (which shouldn't be encountered unless something is broken).
2022-05-31 13:18:40 -04:00
).to eq([items[:appcast][0].short_version])
expect(
Sparkle: Pass all items into strategy block It's sometimes necessary to work with all the items in a Sparkle feed to be able to correctly identify the newest version but livecheck's `Sparkle` strategy only passes the `item` it views as newest into a `strategy` block. This updates the `Sparkle` strategy to optionally pass all items into a `strategy` block, so we can manipulate them (e.g., filtering, sorting). This is enabled by naming the first argument of the strategy block `items` instead of `item`. `Sparkle` `strategy` blocks where the first argument is `item` will continue to work as expected. This necessarily updates `#item_from_content` (now `items_from_content`) to return all items. I've decided to move the sorting out of `#items_from_content`, so it simply returns the items in the order they appear. If there is ever an exceptional situation where we need the original order, this will technically allow for it. The sorting has instead been moved into the `#versions_from_content` method, to maintain the existing behavior. I thought about passing the items into the `strategy` block in their original order but it feels like sorting by default is the better approach for now (partly from the perspective of maintaining existing behavior) and we can always revisit this in the future if a cask ever requires the original order. Lastly, this expands the `Sparkle` tests to increase coverage. The only untested parts are `#find_versions` (which currently requires a network request) and a couple safeguard `raise` calls when there's a `REXML::UndefinedNamespaceException` (which shouldn't be encountered unless something is broken).
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|
[item.title[regex, 1]]
end,
Sparkle: Pass all items into strategy block It's sometimes necessary to work with all the items in a Sparkle feed to be able to correctly identify the newest version but livecheck's `Sparkle` strategy only passes the `item` it views as newest into a `strategy` block. This updates the `Sparkle` strategy to optionally pass all items into a `strategy` block, so we can manipulate them (e.g., filtering, sorting). This is enabled by naming the first argument of the strategy block `items` instead of `item`. `Sparkle` `strategy` blocks where the first argument is `item` will continue to work as expected. This necessarily updates `#item_from_content` (now `items_from_content`) to return all items. I've decided to move the sorting out of `#items_from_content`, so it simply returns the items in the order they appear. If there is ever an exceptional situation where we need the original order, this will technically allow for it. The sorting has instead been moved into the `#versions_from_content` method, to maintain the existing behavior. I thought about passing the items into the `strategy` block in their original order but it feels like sorting by default is the better approach for now (partly from the perspective of maintaining existing behavior) and we can always revisit this in the future if a cask ever requires the original order. Lastly, this expands the `Sparkle` tests to increase coverage. The only untested parts are `#find_versions` (which currently requires a network request) and a couple safeguard `raise` calls when there's a `REXML::UndefinedNamespaceException` (which shouldn't be encountered unless something is broken).
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))
end
Standardize valid strategy block return types Valid `strategy` block return types currently vary between strategies. Some only accept a string whereas others accept a string or array of strings. [`strategy` blocks also accept a `nil` return (to simplify early returns) but this was already standardized across strategies.] While some strategies only identify one version by default (where a string is an appropriate return type), it could be that a strategy block identifies more than one version. In this situation, the strategy would need to be modified to accept (and work with) an array from a `strategy` block. Rather than waiting for this to become a problem, this modifies all strategies to standardize on allowing `strategy` blocks to return a string or array of strings (even if only one of these is currently used in practice). Standardizing valid return types helps to further simplify the mental model for `strategy` blocks and reduce cognitive load. This commit extracts related logic from `#find_versions` into methods like `#versions_from_content`, which is conceptually similar to `PageMatch#page_matches` (renamed to `#versions_from_content` for consistency). This allows us to write tests for the related code without having to make network requests (or stub them) at this point. In general, this also helps to better align the structure of strategies and how the various `#find_versions` methods work with versions. There's still more planned work to be done here but this is a step in the right direction.
2021-08-10 11:09:55 -04:00
it "allows a nil return from a block" do
Sparkle: Pass all items into strategy block It's sometimes necessary to work with all the items in a Sparkle feed to be able to correctly identify the newest version but livecheck's `Sparkle` strategy only passes the `item` it views as newest into a `strategy` block. This updates the `Sparkle` strategy to optionally pass all items into a `strategy` block, so we can manipulate them (e.g., filtering, sorting). This is enabled by naming the first argument of the strategy block `items` instead of `item`. `Sparkle` `strategy` blocks where the first argument is `item` will continue to work as expected. This necessarily updates `#item_from_content` (now `items_from_content`) to return all items. I've decided to move the sorting out of `#items_from_content`, so it simply returns the items in the order they appear. If there is ever an exceptional situation where we need the original order, this will technically allow for it. The sorting has instead been moved into the `#versions_from_content` method, to maintain the existing behavior. I thought about passing the items into the `strategy` block in their original order but it feels like sorting by default is the better approach for now (partly from the perspective of maintaining existing behavior) and we can always revisit this in the future if a cask ever requires the original order. Lastly, this expands the `Sparkle` tests to increase coverage. The only untested parts are `#find_versions` (which currently requires a network request) and a couple safeguard `raise` calls when there's a `REXML::UndefinedNamespaceException` (which shouldn't be encountered unless something is broken).
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([])
Standardize valid strategy block return types Valid `strategy` block return types currently vary between strategies. Some only accept a string whereas others accept a string or array of strings. [`strategy` blocks also accept a `nil` return (to simplify early returns) but this was already standardized across strategies.] While some strategies only identify one version by default (where a string is an appropriate return type), it could be that a strategy block identifies more than one version. In this situation, the strategy would need to be modified to accept (and work with) an array from a `strategy` block. Rather than waiting for this to become a problem, this modifies all strategies to standardize on allowing `strategy` blocks to return a string or array of strings (even if only one of these is currently used in practice). Standardizing valid return types helps to further simplify the mental model for `strategy` blocks and reduce cognitive load. This commit extracts related logic from `#find_versions` into methods like `#versions_from_content`, which is conceptually similar to `PageMatch#page_matches` (renamed to `#versions_from_content` for consistency). This allows us to write tests for the related code without having to make network requests (or stub them) at this point. In general, this also helps to better align the structure of strategies and how the various `#find_versions` methods work with versions. There's still more planned work to be done here but this is a step in the right direction.
2021-08-10 11:09:55 -04:00
end
it "errors on an invalid return type from a block" do
Sparkle: Pass all items into strategy block It's sometimes necessary to work with all the items in a Sparkle feed to be able to correctly identify the newest version but livecheck's `Sparkle` strategy only passes the `item` it views as newest into a `strategy` block. This updates the `Sparkle` strategy to optionally pass all items into a `strategy` block, so we can manipulate them (e.g., filtering, sorting). This is enabled by naming the first argument of the strategy block `items` instead of `item`. `Sparkle` `strategy` blocks where the first argument is `item` will continue to work as expected. This necessarily updates `#item_from_content` (now `items_from_content`) to return all items. I've decided to move the sorting out of `#items_from_content`, so it simply returns the items in the order they appear. If there is ever an exceptional situation where we need the original order, this will technically allow for it. The sorting has instead been moved into the `#versions_from_content` method, to maintain the existing behavior. I thought about passing the items into the `strategy` block in their original order but it feels like sorting by default is the better approach for now (partly from the perspective of maintaining existing behavior) and we can always revisit this in the future if a cask ever requires the original order. Lastly, this expands the `Sparkle` tests to increase coverage. The only untested parts are `#find_versions` (which currently requires a network request) and a couple safeguard `raise` calls when there's a `REXML::UndefinedNamespaceException` (which shouldn't be encountered unless something is broken).
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`")
Standardize valid strategy block return types Valid `strategy` block return types currently vary between strategies. Some only accept a string whereas others accept a string or array of strings. [`strategy` blocks also accept a `nil` return (to simplify early returns) but this was already standardized across strategies.] While some strategies only identify one version by default (where a string is an appropriate return type), it could be that a strategy block identifies more than one version. In this situation, the strategy would need to be modified to accept (and work with) an array from a `strategy` block. Rather than waiting for this to become a problem, this modifies all strategies to standardize on allowing `strategy` blocks to return a string or array of strings (even if only one of these is currently used in practice). Standardizing valid return types helps to further simplify the mental model for `strategy` blocks and reduce cognitive load. This commit extracts related logic from `#find_versions` into methods like `#versions_from_content`, which is conceptually similar to `PageMatch#page_matches` (renamed to `#versions_from_content` for consistency). This allows us to write tests for the related code without having to make network requests (or stub them) at this point. In general, this also helps to better align the structure of strategies and how the various `#find_versions` methods work with versions. There's still more planned work to be done here but this is a step in the right direction.
2021-08-10 11:09:55 -04:00
end
end
2020-12-19 01:11:45 -05:00
end