feedjira 3.2.6 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.github/copilot-instructions.md +176 -0
  3. data/.github/workflows/ruby.yml +1 -1
  4. data/.gitignore +1 -0
  5. data/.rubocop.yml +4 -2
  6. data/.rubocop_todo.yml +354 -5
  7. data/CHANGELOG.md +10 -0
  8. data/feedjira.gemspec +1 -1
  9. data/lib/feedjira/feed_entry_utilities.rb +6 -3
  10. data/lib/feedjira/feed_utilities.rb +2 -2
  11. data/lib/feedjira/parser/json_feed_item.rb +2 -2
  12. data/lib/feedjira/preprocessor.rb +1 -1
  13. data/lib/feedjira/util/parse_time.rb +52 -0
  14. data/lib/feedjira/util.rb +7 -0
  15. data/lib/feedjira/version.rb +1 -1
  16. data/lib/feedjira.rb +2 -1
  17. data/spec/feedjira/atom_entry_utilities_spec.rb +50 -0
  18. data/spec/feedjira/feed_utilities_entry_spec.rb +75 -3
  19. data/spec/feedjira/feed_utilities_spec.rb +19 -0
  20. data/spec/feedjira/parser/atom_entry_spec.rb +2 -2
  21. data/spec/feedjira/parser/atom_feed_burner_entry_spec.rb +1 -1
  22. data/spec/feedjira/parser/atom_google_alerts_entry_spec.rb +19 -4
  23. data/spec/feedjira/parser/atom_youtube_entry_spec.rb +2 -2
  24. data/spec/feedjira/parser/google_docs_atom_spec.rb +36 -0
  25. data/spec/feedjira/parser/i_tunes_rss_category_spec.rb +40 -0
  26. data/spec/feedjira/parser/i_tunes_rss_item_spec.rb +1 -1
  27. data/spec/feedjira/parser/json_feed_item_spec.rb +37 -1
  28. data/spec/feedjira/parser/podlove_chapter_spec.rb +15 -7
  29. data/spec/feedjira/parser/rss_entry_spec.rb +37 -1
  30. data/spec/feedjira/parser/rss_feed_burner_entry_spec.rb +1 -1
  31. data/spec/feedjira/{core_ext/time_spec.rb → util/parse_time_spec.rb} +11 -11
  32. data/spec/feedjira_spec.rb +10 -10
  33. data/spec/support/coverage.rb +1 -1
  34. metadata +8 -7
  35. data/lib/feedjira/core_ext/date.rb +0 -19
  36. data/lib/feedjira/core_ext/string.rb +0 -11
  37. data/lib/feedjira/core_ext/time.rb +0 -38
  38. data/lib/feedjira/core_ext.rb +0 -5
@@ -30,13 +30,13 @@ module Feedjira
30
30
  def parse_published(date_published)
31
31
  return nil unless date_published
32
32
 
33
- Time.parse_safely(date_published)
33
+ Feedjira::Util::ParseTime.call(date_published)
34
34
  end
35
35
 
36
36
  def parse_updated(date_modified)
37
37
  return nil unless date_modified
38
38
 
39
- Time.parse_safely(date_modified)
39
+ Feedjira::Util::ParseTime.call(date_modified)
40
40
  end
41
41
 
42
42
  # Convenience method to return the included content type.
@@ -15,7 +15,7 @@ module Feedjira
15
15
 
16
16
  def process_content
17
17
  content_nodes.each do |node|
18
- node.content = raw_html(node) unless node.cdata?
18
+ node.content = raw_html(node)
19
19
  end
20
20
  end
21
21
 
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "time"
4
+ require "date"
5
+
6
+ module Feedjira
7
+ module Util
8
+ # Module for safely parsing time strings
9
+ module ParseTime
10
+ # Parse a time string and convert it to UTC without raising errors.
11
+ # Parses a flattened 14-digit time (YYYYmmddHHMMMSS) as UTC.
12
+ #
13
+ # === Parameters
14
+ # [dt<String or Time>] Time definition to be parsed.
15
+ #
16
+ # === Returns
17
+ # A Time instance in UTC or nil if there were errors while parsing.
18
+ def self.call(datetime)
19
+ if datetime.is_a?(Time)
20
+ datetime.utc
21
+ elsif datetime.respond_to?(:to_datetime)
22
+ datetime.to_time.utc
23
+ else
24
+ parse_string_safely datetime.to_s
25
+ end
26
+ rescue StandardError => e
27
+ Feedjira.logger.debug("Failed to parse time #{datetime}")
28
+ Feedjira.logger.debug(e)
29
+ nil
30
+ end
31
+
32
+ # Parse a string safely, handling special 14-digit format
33
+ #
34
+ # === Parameters
35
+ # [string<String>] String to be parsed as time.
36
+ #
37
+ # === Returns
38
+ # A Time instance in UTC or nil if there were errors while parsing.
39
+ def self.parse_string_safely(string)
40
+ return nil if string.empty?
41
+
42
+ if /\A\d{14}\z/.match?(string)
43
+ Time.parse("#{string}Z", true)
44
+ else
45
+ Time.parse(string).utc
46
+ end
47
+ end
48
+
49
+ private_class_method :parse_string_safely
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Feedjira
4
+ # Utility modules and helper functions
5
+ module Util
6
+ end
7
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Feedjira
4
- VERSION = "3.2.6"
4
+ VERSION = "4.0.0"
5
5
  end
data/lib/feedjira.rb CHANGED
@@ -6,7 +6,8 @@ require "loofah"
6
6
  require "logger"
7
7
  require "json"
8
8
 
9
- require_relative "feedjira/core_ext"
9
+ require_relative "feedjira/util"
10
+ require_relative "feedjira/util/parse_time"
10
11
  require_relative "feedjira/configuration"
11
12
  require_relative "feedjira/feed_entry_utilities"
12
13
  require_relative "feedjira/feed_utilities"
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe Feedjira::AtomEntryUtilities do
6
+ def klass
7
+ Class.new do
8
+ include SAXMachine
9
+ include Feedjira::AtomEntryUtilities
10
+ end
11
+ end
12
+
13
+ describe "#title" do
14
+ it "returns the title when set" do
15
+ entry = klass.new
16
+ entry.title = "My Title"
17
+
18
+ expect(entry.title).to eq "My Title"
19
+ end
20
+
21
+ it "returns a sanitized version of the raw title when present" do
22
+ entry = klass.new
23
+ entry.raw_title = "My <b>Raw</b> \tTitle"
24
+
25
+ expect(entry.title).to eq "My Raw Title"
26
+ end
27
+
28
+ it "returns nil when no raw title is present" do
29
+ entry = klass.new
30
+
31
+ expect(entry.title).to be_nil
32
+ end
33
+ end
34
+
35
+ describe "#url" do
36
+ it "returns the url when set" do
37
+ entry = klass.new
38
+ entry.url = "http://exampoo.com/feed"
39
+
40
+ expect(entry.url).to eq "http://exampoo.com/feed"
41
+ end
42
+
43
+ it "returns the first link when not set" do
44
+ entry = klass.new
45
+ entry.links = ["http://exampoo.com/feed"]
46
+
47
+ expect(entry.url).to eq "http://exampoo.com/feed"
48
+ end
49
+ end
50
+ end
@@ -13,7 +13,7 @@ describe Feedjira::FeedUtilities do
13
13
  it "parses an ISO 8601 formatted datetime into Time" do
14
14
  time = @klass.new.parse_datetime("2008-02-20T8:05:00-010:00")
15
15
  expect(time.class).to eq Time
16
- expect(time).to eq Time.parse_safely("Wed Feb 20 18:05:00 UTC 2008")
16
+ expect(time).to eq Feedjira::Util::ParseTime.call("Wed Feb 20 18:05:00 UTC 2008")
17
17
  end
18
18
 
19
19
  it "parses a ISO 8601 with milliseconds into Time" do
@@ -23,6 +23,78 @@ describe Feedjira::FeedUtilities do
23
23
  end
24
24
  end
25
25
 
26
+ describe "updated= method" do
27
+ it "sets updated when no existing updated value and parsed date is valid" do
28
+ instance = @klass.new
29
+ instance.updated = "2023-01-01T10:00:00Z"
30
+ expect(instance["updated"]).to eq Time.parse("2023-01-01T10:00:00Z").utc
31
+ end
32
+
33
+ it "updates to newer date when existing updated value is older" do
34
+ instance = @klass.new
35
+ instance.updated = "2023-01-01T10:00:00Z"
36
+ instance.updated = "2023-01-02T10:00:00Z"
37
+ expect(instance["updated"]).to eq Time.parse("2023-01-02T10:00:00Z").utc
38
+ end
39
+
40
+ it "keeps existing updated value when new date is older" do
41
+ instance = @klass.new
42
+ instance.updated = "2023-01-02T10:00:00Z"
43
+ instance.updated = "2023-01-01T10:00:00Z"
44
+ expect(instance["updated"]).to eq Time.parse("2023-01-02T10:00:00Z").utc
45
+ end
46
+
47
+ it "does not set updated when date parsing fails" do
48
+ instance = @klass.new
49
+ instance.updated = "invalid-date"
50
+ expect(instance["updated"]).to be_nil
51
+ end
52
+
53
+ it "does not change existing updated when new date is invalid" do
54
+ instance = @klass.new
55
+ instance.updated = "2023-01-01T10:00:00Z"
56
+ original_updated = instance["updated"]
57
+ instance.updated = "invalid-date"
58
+ expect(instance["updated"]).to eq original_updated
59
+ end
60
+ end
61
+
62
+ describe "published= method" do
63
+ it "sets published when no existing published value and parsed date is valid" do
64
+ instance = @klass.new
65
+ instance.published = "2023-01-01T10:00:00Z"
66
+ expect(instance["published"]).to eq Time.parse("2023-01-01T10:00:00Z").utc
67
+ end
68
+
69
+ it "updates to older date when existing published value is newer" do
70
+ instance = @klass.new
71
+ instance.published = "2023-01-02T10:00:00Z"
72
+ instance.published = "2023-01-01T10:00:00Z"
73
+ expect(instance["published"]).to eq Time.parse("2023-01-01T10:00:00Z").utc
74
+ end
75
+
76
+ it "keeps existing published value when new date is newer" do
77
+ instance = @klass.new
78
+ instance.published = "2023-01-01T10:00:00Z"
79
+ instance.published = "2023-01-02T10:00:00Z"
80
+ expect(instance["published"]).to eq Time.parse("2023-01-01T10:00:00Z").utc
81
+ end
82
+
83
+ it "does not set published when date parsing fails" do
84
+ instance = @klass.new
85
+ instance.published = "invalid-date"
86
+ expect(instance["published"]).to be_nil
87
+ end
88
+
89
+ it "does not change existing published when new date is invalid" do
90
+ instance = @klass.new
91
+ instance.published = "2023-01-01T10:00:00Z"
92
+ original_published = instance["published"]
93
+ instance.published = "invalid-date"
94
+ expect(instance["published"]).to eq original_published
95
+ end
96
+ end
97
+
26
98
  describe "sanitizing" do
27
99
  before do
28
100
  @feed = Feedjira.parse(sample_atom_feed)
@@ -37,7 +109,7 @@ describe Feedjira::FeedUtilities do
37
109
  new_title = "<script>this is not safe</script>#{@entry.title}"
38
110
  @entry.title = new_title
39
111
  scrubbed_title = Loofah.scrub_fragment(new_title, :prune).to_s
40
- expect(@entry.title.sanitize).to eq scrubbed_title
112
+ expect(Loofah.scrub_fragment(@entry.title, :prune).to_s).to eq scrubbed_title
41
113
  end
42
114
 
43
115
  it "sanitizes content in place" do
@@ -46,7 +118,7 @@ describe Feedjira::FeedUtilities do
46
118
 
47
119
  scrubbed_content = Loofah.scrub_fragment(new_content, :prune).to_s
48
120
 
49
- expect(@entry.content.sanitize!).to eq scrubbed_content
121
+ @entry.sanitize!
50
122
  expect(@entry.content).to eq scrubbed_content
51
123
  end
52
124
 
@@ -7,6 +7,8 @@ describe Feedjira::FeedUtilities do
7
7
  @klass = Class.new do
8
8
  include SAXMachine
9
9
  include Feedjira::FeedUtilities
10
+
11
+ elements :item, as: :entries, class: Feedjira::Parser::RSSEntry
10
12
  end
11
13
  end
12
14
 
@@ -28,6 +30,23 @@ describe Feedjira::FeedUtilities do
28
30
  end
29
31
  end
30
32
 
33
+ describe "#preprocess" do
34
+ it "returns the xml without changes when not overridden" do
35
+ expect(@klass.preprocess(sample_rss_feed)).to eq sample_rss_feed
36
+ end
37
+ end
38
+
39
+ describe "#sanitize_entries!" do
40
+ it "sanitizes all entries" do
41
+ result = @klass.parse(sample_rss_feed)
42
+
43
+ entry = result.entries.first
44
+
45
+ expect { result.sanitize_entries! }.to change(entry, :content)
46
+ .from(/onclick="javascript/)
47
+ end
48
+ end
49
+
31
50
  describe "when configured to strip whitespace" do
32
51
  context "when strip_whitespace config is true" do
33
52
  it "strips all XML whitespace" do
@@ -39,7 +39,7 @@ describe Feedjira::Parser::AtomEntry do
39
39
  end
40
40
 
41
41
  it "parses the published date" do
42
- published = Time.parse_safely "Fri Jan 16 18:21:00 UTC 2009"
42
+ published = Feedjira::Util::ParseTime.call "Fri Jan 16 18:21:00 UTC 2009"
43
43
  expect(@entry.published).to eq published
44
44
  end
45
45
 
@@ -48,7 +48,7 @@ describe Feedjira::Parser::AtomEntry do
48
48
  end
49
49
 
50
50
  it "parses the updated date" do
51
- updated = Time.parse_safely "Fri Jan 16 18:21:00 UTC 2009"
51
+ updated = Feedjira::Util::ParseTime.call "Fri Jan 16 18:21:00 UTC 2009"
52
52
  expect(@entry.updated).to eq updated
53
53
  end
54
54
 
@@ -46,7 +46,7 @@ describe Feedjira::Parser::AtomFeedBurnerEntry do
46
46
  end
47
47
 
48
48
  it "parses the published date" do
49
- published = Time.parse_safely "Thu Jan 22 15:50:22 UTC 2009"
49
+ published = Feedjira::Util::ParseTime.call "Thu Jan 22 15:50:22 UTC 2009"
50
50
  expect(@entry.published).to eq published
51
51
  end
52
52
 
@@ -14,8 +14,23 @@ describe Feedjira::Parser::AtomGoogleAlertsEntry do
14
14
  expect(@entry.title_type).to eq "html"
15
15
  end
16
16
 
17
- it "parses the url" do
18
- expect(@entry.url).to eq "https://www.aglobalmarketresearch.com/report-offers-prediction-of-automotive-slack-market-by-top-key-players-like-haldex-meritor-bendix-mei-wabco-accuride-stemco-tbk-febi-aydinsan/"
17
+ it "parses the url out of the params when the host is google" do
18
+ url = "https://www.exampoo.com"
19
+ entry = described_class.new(url: "https://www.google.com/url?url=#{url}")
20
+
21
+ expect(entry.url).to eq url
22
+ end
23
+
24
+ it "returns nil when the url is not present" do
25
+ entry = described_class.new
26
+
27
+ expect(entry.url).to be_nil
28
+ end
29
+
30
+ it "returns nil when the host is not google" do
31
+ entry = described_class.new(url: "https://www.exampoo.com")
32
+
33
+ expect(entry.url).to be_nil
19
34
  end
20
35
 
21
36
  it "parses the content" do
@@ -23,12 +38,12 @@ describe Feedjira::Parser::AtomGoogleAlertsEntry do
23
38
  end
24
39
 
25
40
  it "parses the published date" do
26
- published = Time.parse_safely "2019-07-10T11:53:37Z"
41
+ published = Feedjira::Util::ParseTime.call "2019-07-10T11:53:37Z"
27
42
  expect(@entry.published).to eq published
28
43
  end
29
44
 
30
45
  it "parses the updated date" do
31
- updated = Time.parse_safely "2019-07-10T11:53:37Z"
46
+ updated = Feedjira::Util::ParseTime.call "2019-07-10T11:53:37Z"
32
47
  expect(@entry.updated).to eq updated
33
48
  end
34
49
  end
@@ -22,11 +22,11 @@ describe Feedjira::Parser::AtomYoutubeEntry do
22
22
  end
23
23
 
24
24
  it "has the published date" do
25
- expect(@entry.published).to eq Time.parse_safely("2015-05-04T00:01:27+00:00")
25
+ expect(@entry.published).to eq Feedjira::Util::ParseTime.call("2015-05-04T00:01:27+00:00")
26
26
  end
27
27
 
28
28
  it "has the updated date" do
29
- expect(@entry.updated).to eq Time.parse_safely("2015-05-13T17:38:30+00:00")
29
+ expect(@entry.updated).to eq Feedjira::Util::ParseTime.call("2015-05-13T17:38:30+00:00")
30
30
  end
31
31
 
32
32
  it "has the content populated from the media:description element" do
@@ -31,5 +31,41 @@ module Feedjira
31
31
  expect(@feed.entries.first).to be_a GoogleDocsAtomEntry
32
32
  end
33
33
  end
34
+
35
+ describe "#url" do
36
+ it "returns the url when set" do
37
+ feed = GoogleDocsAtom.new
38
+
39
+ feed.url = "http://exampoo.com/feed"
40
+
41
+ expect(feed.url).to eq "http://exampoo.com/feed"
42
+ end
43
+
44
+ it "returns the first link when not set" do
45
+ feed = GoogleDocsAtom.new
46
+
47
+ feed.links = ["http://exampoo.com/feed"]
48
+
49
+ expect(feed.url).to eq "http://exampoo.com/feed"
50
+ end
51
+ end
52
+
53
+ describe "#feed_url" do
54
+ it "returns the feed_url when set" do
55
+ feed = GoogleDocsAtom.new
56
+
57
+ feed.feed_url = "http://exampoo.com/feed"
58
+
59
+ expect(feed.feed_url).to eq "http://exampoo.com/feed"
60
+ end
61
+
62
+ it "returns the first link when not set" do
63
+ feed = GoogleDocsAtom.new
64
+
65
+ feed.links = ["http://exampoo.com/feed"]
66
+
67
+ expect(feed.feed_url).to eq "http://exampoo.com/feed"
68
+ end
69
+ end
34
70
  end
35
71
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ describe Feedjira::Parser::ITunesRSSCategory do
6
+ describe "#each_subcategory" do
7
+ it "returns an enumerator when no block is given" do
8
+ category = described_class.new
9
+ category.text = "Technology"
10
+
11
+ result = category.each_subcategory
12
+ expect(result).to be_an(Enumerator)
13
+ end
14
+
15
+ it "yields category text and subcategories when block is given" do
16
+ parent_category = described_class.new
17
+ parent_category.text = "Technology"
18
+
19
+ subcategory = described_class.new
20
+ subcategory.text = "Gadgets"
21
+
22
+ parent_category.itunes_categories = [subcategory]
23
+
24
+ yielded_categories = []
25
+ parent_category.each_subcategory { |cat| yielded_categories << cat }
26
+
27
+ expect(yielded_categories).to eq %w[Technology Gadgets]
28
+ end
29
+ end
30
+
31
+ describe "#each_path" do
32
+ it "returns an enumerator when no block is given" do
33
+ category = described_class.new
34
+ category.text = "Technology"
35
+
36
+ result = category.each_path
37
+ expect(result).to be_an(Enumerator)
38
+ end
39
+ end
40
+ end
@@ -54,7 +54,7 @@ describe Feedjira::Parser::ITunesRSSItem do
54
54
  end
55
55
 
56
56
  it "parses the published date" do
57
- published = Time.parse_safely "Wed Jun 15 19:00:00 UTC 2005"
57
+ published = Feedjira::Util::ParseTime.call "Wed Jun 15 19:00:00 UTC 2005"
58
58
  expect(@item.published).to eq published
59
59
  end
60
60
 
@@ -3,6 +3,10 @@
3
3
  require "spec_helper"
4
4
 
5
5
  describe Feedjira::Parser::JSONFeedItem do
6
+ def params(**overrides)
7
+ { "id" => "my_id", "url" => "my_url", **overrides }
8
+ end
9
+
6
10
  before do
7
11
  # I don't really like doing it this way because these unit test should only
8
12
  # rely on JSONFeed, but this is actually how it should work. You would
@@ -28,10 +32,42 @@ describe Feedjira::Parser::JSONFeedItem do
28
32
  end
29
33
 
30
34
  it "parses the published date" do
31
- published = Time.parse_safely "2017-06-02T22:05:47-07:00"
35
+ published = Feedjira::Util::ParseTime.call "2017-06-02T22:05:47-07:00"
32
36
  expect(@entry.published).to eq published
33
37
  end
34
38
 
39
+ it "sets the published date to nil when not present" do
40
+ entry = described_class.new(params)
41
+
42
+ expect(entry.published).to be_nil
43
+ end
44
+
45
+ it "sets updated to date_modified when present" do
46
+ updated = "2017-06-02T22:05:47-07:00"
47
+ entry = described_class.new(params("date_modified" => updated))
48
+
49
+ updated = Feedjira::Util::ParseTime.call "2017-06-02T22:05:47-07:00"
50
+ expect(entry.updated).to eq updated
51
+ end
52
+
53
+ it "sets updated to nil when date_modified is not present" do
54
+ entry = described_class.new(params)
55
+
56
+ expect(entry.updated).to be_nil
57
+ end
58
+
59
+ it "sets the author when nested author object is present" do
60
+ entry = described_class.new(params("author" => { "name" => "John Doe" }))
61
+
62
+ expect(entry.author).to eq "John Doe"
63
+ end
64
+
65
+ it "sets the author to nil when nested author object is not present" do
66
+ entry = described_class.new(params)
67
+
68
+ expect(entry.author).to be_nil
69
+ end
70
+
35
71
  it "supports each" do
36
72
  expect(@entry).to respond_to :each
37
73
  end
@@ -16,13 +16,21 @@ describe Feedjira::Parser::PodloveChapter do
16
16
  expect(@item.chapters.last.title).to eq "Abschied"
17
17
  end
18
18
 
19
- it "parses the start time" do
20
- expect(@chapter.start_ntp).to eq "00:00:26.407"
21
- expect(@chapter.start).to eq 26.407
22
- expect(@item.chapters[1].start).to eq 50
23
- expect(@item.chapters[2].start).to eq 59.12
24
- expect(@item.chapters[3].start).to eq 89.201
25
- expect(@item.chapters.last.start).to eq 5700.034
19
+ describe "#start" do
20
+ it "returns the start time" do
21
+ expect(@chapter.start_ntp).to eq "00:00:26.407"
22
+ expect(@chapter.start).to eq 26.407
23
+ expect(@item.chapters[1].start).to eq 50
24
+ expect(@item.chapters[2].start).to eq 59.12
25
+ expect(@item.chapters[3].start).to eq 89.201
26
+ expect(@item.chapters.last.start).to eq 5700.034
27
+ end
28
+
29
+ it "returns nil when start_ntp is not present" do
30
+ chapter = described_class.new
31
+
32
+ expect(chapter.start).to be_nil
33
+ end
26
34
  end
27
35
 
28
36
  it "parses the title" do
@@ -42,7 +42,7 @@ describe Feedjira::Parser::RSSEntry do
42
42
  end
43
43
 
44
44
  it "parses the published date" do
45
- published = Time.parse_safely "Thu Dec 04 17:17:49 UTC 2008"
45
+ published = Feedjira::Util::ParseTime.call "Thu Dec 04 17:17:49 UTC 2008"
46
46
  expect(@entry.published).to eq published
47
47
  end
48
48
 
@@ -122,4 +122,40 @@ describe Feedjira::Parser::RSSEntry do
122
122
  feed = Feedjira.parse(sample_rss_feed_with_comments)
123
123
  expect(feed.entries[0].comments).to eq "https://news.ycombinator.com/item?id=30937433"
124
124
  end
125
+
126
+ it "returns nil when no URL is available from link or guid" do
127
+ xml = <<~XML
128
+ <rss version="2.0">
129
+ <channel>
130
+ <item>
131
+ <title>Entry without URL</title>
132
+ <description>This entry has no link or guid</description>
133
+ </item>
134
+ </channel>
135
+ </rss>
136
+ XML
137
+
138
+ feed = Feedjira.parse(xml)
139
+ entry = feed.entries.first
140
+
141
+ expect(entry.url).to be_nil
142
+ end
143
+
144
+ it "returns nil when guid exists but is not a permalink" do
145
+ xml = <<~XML
146
+ <rss version="2.0">
147
+ <channel>
148
+ <item>
149
+ <title>Entry with non-permalink GUID</title>
150
+ <guid isPermaLink="false">some-guid-123</guid>
151
+ </item>
152
+ </channel>
153
+ </rss>
154
+ XML
155
+
156
+ feed = Feedjira.parse(xml)
157
+ entry = feed.entries.first
158
+
159
+ expect(entry.url).to be_nil
160
+ end
125
161
  end
@@ -43,7 +43,7 @@ describe Feedjira::Parser::RSSFeedBurnerEntry do
43
43
  end
44
44
 
45
45
  it "parses the published date" do
46
- published = Time.parse_safely "Wed Nov 02 17:25:27 UTC 2011"
46
+ published = Feedjira::Util::ParseTime.call "Wed Nov 02 17:25:27 UTC 2011"
47
47
  expect(@entry.published).to eq published
48
48
  end
49
49