rss_agent_spec.rb 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. require 'rails_helper'
  2. describe Agents::RssAgent do
  3. before do
  4. @valid_options = {
  5. 'expected_update_period_in_days' => "2",
  6. 'url' => "https://github.com/cantino/huginn/commits/master.atom",
  7. }
  8. stub_request(:any, /github.com/).to_return(:body => File.read(Rails.root.join("spec/data_fixtures/github_rss.atom")), :status => 200)
  9. stub_request(:any, /bad.github.com/).to_return(body: File.read(Rails.root.join("spec/data_fixtures/github_rss.atom")).gsub(/<link [^>]+\/>/, '<link/>'), status: 200)
  10. stub_request(:any, /SlickdealsnetFP/).to_return(:body => File.read(Rails.root.join("spec/data_fixtures/slickdeals.atom")), :status => 200)
  11. stub_request(:any, /onethingwell.org/).to_return(body: File.read(Rails.root.join("spec/data_fixtures/onethingwell.rss")), status: 200)
  12. stub_request(:any, /bad.onethingwell.org/).to_return(body: File.read(Rails.root.join("spec/data_fixtures/onethingwell.rss")).gsub(/(?<=<link>)[^<]*/, ''), status: 200)
  13. stub_request(:any, /iso-8859-1/).to_return(body: File.binread(Rails.root.join("spec/data_fixtures/iso-8859-1.rss")), headers: { 'Content-Type' => 'application/rss+xml; charset=ISO-8859-1' }, status: 200)
  14. stub_request(:any, /podcast/).to_return(body: File.read(Rails.root.join("spec/data_fixtures/podcast.rss")), status: 200)
  15. stub_request(:any, /youtube/).to_return(body: File.read(Rails.root.join("spec/data_fixtures/youtube.xml")), status: 200)
  16. end
  17. let(:agent) do
  18. _agent = Agents::RssAgent.new(:name => "rss feed", :options => @valid_options)
  19. _agent.user = users(:bob)
  20. _agent.save!
  21. _agent
  22. end
  23. it_behaves_like WebRequestConcern
  24. describe "validations" do
  25. it "should validate the presence of url" do
  26. agent.options['url'] = "http://google.com"
  27. expect(agent).to be_valid
  28. agent.options['url'] = ["http://google.com", "http://yahoo.com"]
  29. expect(agent).to be_valid
  30. agent.options['url'] = ""
  31. expect(agent).not_to be_valid
  32. agent.options['url'] = nil
  33. expect(agent).not_to be_valid
  34. end
  35. it "should validate the presence and numericality of expected_update_period_in_days" do
  36. agent.options['expected_update_period_in_days'] = "5"
  37. expect(agent).to be_valid
  38. agent.options['expected_update_period_in_days'] = "wut?"
  39. expect(agent).not_to be_valid
  40. agent.options['expected_update_period_in_days'] = 0
  41. expect(agent).not_to be_valid
  42. agent.options['expected_update_period_in_days'] = nil
  43. expect(agent).not_to be_valid
  44. agent.options['expected_update_period_in_days'] = ""
  45. expect(agent).not_to be_valid
  46. end
  47. end
  48. describe "emitting RSS events" do
  49. it "should emit items as events for an Atom feed" do
  50. agent.options['include_feed_info'] = true
  51. agent.options['include_sort_info'] = true
  52. expect {
  53. agent.check
  54. }.to change { agent.events.count }.by(20)
  55. first, *, last = agent.events.last(20)
  56. [first, last].each do |event|
  57. expect(event.payload['feed']).to include({
  58. "type" => "atom",
  59. "title" => "Recent Commits to huginn:master",
  60. "url" => "https://github.com/cantino/huginn/commits/master",
  61. "links" => [
  62. {
  63. "type" => "text/html",
  64. "rel" => "alternate",
  65. "href" => "https://github.com/cantino/huginn/commits/master",
  66. },
  67. {
  68. "type" => "application/atom+xml",
  69. "rel" => "self",
  70. "href" => "https://github.com/cantino/huginn/commits/master.atom",
  71. },
  72. ],
  73. })
  74. end
  75. expect(first.payload['url']).to eq("https://github.com/cantino/huginn/commit/d0a844662846cf3c83b94c637c1803f03db5a5b0")
  76. expect(first.payload['urls']).to eq(["https://github.com/cantino/huginn/commit/d0a844662846cf3c83b94c637c1803f03db5a5b0"])
  77. expect(first.payload['links']).to eq([
  78. {
  79. "href" => "https://github.com/cantino/huginn/commit/d0a844662846cf3c83b94c637c1803f03db5a5b0",
  80. "rel" => "alternate",
  81. "type" => "text/html",
  82. }
  83. ])
  84. expect(first.payload['authors']).to eq(["cantino (https://github.com/cantino)"])
  85. expect(first.payload['date_published']).to be_nil
  86. expect(first.payload['last_updated']).to eq("2014-07-16T22:26:22-07:00")
  87. expect(first.payload['sort_info']).to eq({ 'position' => 20, 'count' => 20 })
  88. expect(last.payload['url']).to eq("https://github.com/cantino/huginn/commit/d465158f77dcd9078697e6167b50abbfdfa8b1af")
  89. expect(last.payload['urls']).to eq(["https://github.com/cantino/huginn/commit/d465158f77dcd9078697e6167b50abbfdfa8b1af"])
  90. expect(last.payload['links']).to eq([
  91. {
  92. "href" => "https://github.com/cantino/huginn/commit/d465158f77dcd9078697e6167b50abbfdfa8b1af",
  93. "rel" => "alternate",
  94. "type" => "text/html",
  95. }
  96. ])
  97. expect(last.payload['authors']).to eq(["CloCkWeRX (https://github.com/CloCkWeRX)"])
  98. expect(last.payload['date_published']).to be_nil
  99. expect(last.payload['last_updated']).to eq("2014-07-01T16:37:47+09:30")
  100. expect(last.payload['sort_info']).to eq({ 'position' => 1, 'count' => 20 })
  101. end
  102. it "should emit items as events in the order specified in the events_order option" do
  103. expect {
  104. agent.options['events_order'] = ['{{title | replace_regex: "^[[:space:]]+", "" }}']
  105. agent.options['include_sort_info'] = true
  106. agent.check
  107. }.to change { agent.events.count }.by(20)
  108. first, *, last = agent.events.last(20)
  109. expect(first.payload['title'].strip).to eq('upgrade rails and gems')
  110. expect(first.payload['url']).to eq("https://github.com/cantino/huginn/commit/87a7abda23a82305d7050ac0bb400ce36c863d01")
  111. expect(first.payload['urls']).to eq(["https://github.com/cantino/huginn/commit/87a7abda23a82305d7050ac0bb400ce36c863d01"])
  112. expect(first.payload['sort_info']).to eq({ 'position' => 20, 'count' => 20 })
  113. expect(last.payload['title'].strip).to eq('Dashed line in a diagram indicates propagate_immediately being false.')
  114. expect(last.payload['url']).to eq("https://github.com/cantino/huginn/commit/0e80f5341587aace2c023b06eb9265b776ac4535")
  115. expect(last.payload['urls']).to eq(["https://github.com/cantino/huginn/commit/0e80f5341587aace2c023b06eb9265b776ac4535"])
  116. expect(last.payload['sort_info']).to eq({ 'position' => 1, 'count' => 20 })
  117. end
  118. it "should emit items as events for a FeedBurner RSS 2.0 feed" do
  119. agent.options['url'] = "http://feeds.feedburner.com/SlickdealsnetFP?format=atom" # This is actually RSS 2.0 w/ Atom extension
  120. agent.options['include_feed_info'] = true
  121. agent.save!
  122. expect {
  123. agent.check
  124. }.to change { agent.events.count }.by(79)
  125. first, *, last = agent.events.last(79)
  126. expect(first.payload['feed']).to include({
  127. "type" => "rss",
  128. "title" => "SlickDeals.net",
  129. "description" => "Slick online shopping deals.",
  130. "url" => "http://slickdeals.net/",
  131. })
  132. # Feedjira extracts feedburner:origLink
  133. expect(first.payload['url']).to eq("http://slickdeals.net/permadeal/130160/green-man-gaming---pc-games-tomb-raider-game-of-the-year-6-hitman-absolution-elite-edition")
  134. expect(last.payload['feed']).to include({
  135. "type" => "rss",
  136. "title" => "SlickDeals.net",
  137. "description" => "Slick online shopping deals.",
  138. "url" => "http://slickdeals.net/",
  139. })
  140. expect(last.payload['url']).to eq("http://slickdeals.net/permadeal/129980/amazon---rearth-ringke-fusion-bumper-hybrid-case-for-iphone-6")
  141. end
  142. it "should track ids and not re-emit the same item when seen again" do
  143. agent.check
  144. expect(agent.memory['seen_ids']).to eq(agent.events.map {|e| e.payload['id'] })
  145. newest_id = agent.memory['seen_ids'][0]
  146. expect(agent.events.first.payload['id']).to eq(newest_id)
  147. agent.memory['seen_ids'] = agent.memory['seen_ids'][1..-1] # forget the newest id
  148. expect {
  149. agent.check
  150. }.to change { agent.events.count }.by(1)
  151. expect(agent.events.first.payload['id']).to eq(newest_id)
  152. expect(agent.memory['seen_ids'][0]).to eq(newest_id)
  153. end
  154. it "should truncate the seen_ids in memory at 500 items per default" do
  155. agent.memory['seen_ids'] = ['x'] * 490
  156. agent.check
  157. expect(agent.memory['seen_ids'].length).to eq(500)
  158. end
  159. it "should truncate the seen_ids in memory at amount of items configured in options" do
  160. agent.options['remembered_id_count'] = "600"
  161. agent.memory['seen_ids'] = ['x'] * 590
  162. agent.check
  163. expect(agent.memory['seen_ids'].length).to eq(600)
  164. end
  165. it "should truncate the seen_ids after configuring a lower limit of items when check is executed" do
  166. agent.memory['seen_ids'] = ['x'] * 600
  167. agent.options['remembered_id_count'] = "400"
  168. expect(agent.memory['seen_ids'].length).to eq(600)
  169. agent.check
  170. expect(agent.memory['seen_ids'].length).to eq(400)
  171. end
  172. it "should truncate the seen_ids at default after removing custom limit" do
  173. agent.options['remembered_id_count'] = "600"
  174. agent.memory['seen_ids'] = ['x'] * 590
  175. agent.check
  176. expect(agent.memory['seen_ids'].length).to eq(600)
  177. agent.options.delete('remembered_id_count')
  178. agent.memory['seen_ids'] = ['x'] * 590
  179. agent.check
  180. expect(agent.memory['seen_ids'].length).to eq(500)
  181. end
  182. it "should support an array of URLs" do
  183. agent.options['url'] = ["https://github.com/cantino/huginn/commits/master.atom", "http://feeds.feedburner.com/SlickdealsnetFP?format=atom"]
  184. agent.save!
  185. expect {
  186. agent.check
  187. }.to change { agent.events.count }.by(20 + 79)
  188. end
  189. it "should fetch one event per run" do
  190. agent.options['url'] = ["https://github.com/cantino/huginn/commits/master.atom"]
  191. agent.options['max_events_per_run'] = 1
  192. agent.check
  193. expect(agent.events.count).to eq(1)
  194. end
  195. it "should fetch all events per run" do
  196. agent.options['url'] = ["https://github.com/cantino/huginn/commits/master.atom"]
  197. # <= 0 should ignore option and get all
  198. agent.options['max_events_per_run'] = 0
  199. agent.check
  200. expect(agent.events.count).to eq(20)
  201. agent.options['max_events_per_run'] = -1
  202. expect {
  203. agent.check
  204. }.to_not change { agent.events.count }
  205. end
  206. end
  207. context "when no ids are available" do
  208. before do
  209. @valid_options['url'] = 'http://feeds.feedburner.com/SlickdealsnetFP?format=atom'
  210. end
  211. it "calculates content MD5 sums" do
  212. expect {
  213. agent.check
  214. }.to change { agent.events.count }.by(79)
  215. expect(agent.memory['seen_ids']).to eq(agent.events.map {|e| Digest::MD5.hexdigest(e.payload['content']) })
  216. end
  217. end
  218. context "parsing feeds" do
  219. before do
  220. @valid_options['url'] = 'http://onethingwell.org/rss'
  221. end
  222. it "captures timestamps normalized in the ISO 8601 format" do
  223. agent.check
  224. first, *, third = agent.events.take(3)
  225. expect(first.payload['date_published']).to eq('2015-08-20T17:00:10+01:00')
  226. expect(third.payload['date_published']).to eq('2015-08-20T13:00:07+01:00')
  227. end
  228. it "captures multiple categories" do
  229. agent.check
  230. first, *, third = agent.events.take(3)
  231. expect(first.payload['categories']).to eq(["csv", "crossplatform", "utilities"])
  232. expect(third.payload['categories']).to eq(["web"])
  233. end
  234. it "sanitizes HTML content" do
  235. agent.options['clean'] = true
  236. agent.check
  237. event = agent.events.last
  238. expect(event.payload['content']).to eq('<a href="http://showgoers.tv/">Showgoers</a>: <blockquote> <p>Showgoers is a Chrome browser extension to synchronize your Netflix player with someone else so that you can co-watch the same movie on different computers with no hassle. Syncing up your player is as easy as sharing a URL.</p> </blockquote>')
  239. expect(event.payload['description']).to eq('<a href="http://showgoers.tv/">Showgoers</a>: <blockquote> <p>Showgoers is a Chrome browser extension to synchronize your Netflix player with someone else so that you can co-watch the same movie on different computers with no hassle. Syncing up your player is as easy as sharing a URL.</p> </blockquote>')
  240. end
  241. it "captures an enclosure" do
  242. agent.check
  243. event = agent.events.fourth
  244. expect(event.payload['enclosure']).to eq({ "url" => "http://c.1tw.org/images/2015/itsy.png", "type" => "image/png", "length" => "48249" })
  245. expect(event.payload['image']).to eq("http://c.1tw.org/images/2015/itsy.png")
  246. end
  247. it "ignores an empty author" do
  248. agent.check
  249. event = agent.events.first
  250. expect(event.payload['authors']).to eq([])
  251. end
  252. context 'with an empty link in RSS' do
  253. before do
  254. @valid_options['url'] = 'http://bad.onethingwell.org/rss'
  255. end
  256. it "does not leak :no_buffer" do
  257. agent.check
  258. event = agent.events.first
  259. expect(event.payload['links']).to eq([])
  260. end
  261. end
  262. context 'with an empty link in RSS' do
  263. before do
  264. @valid_options['url'] = "https://bad.github.com/cantino/huginn/commits/master.atom"
  265. end
  266. it "does not leak :no_buffer" do
  267. agent.check
  268. event = agent.events.first
  269. expect(event.payload['links']).to eq([])
  270. end
  271. end
  272. context 'with the encoding declared in both headers and the content' do
  273. before do
  274. @valid_options['url'] = 'http://example.org/iso-8859-1.rss'
  275. end
  276. it "decodes the content properly" do
  277. agent.check
  278. event = agent.events.first
  279. expect(event.payload['title']).to eq('Mëkanïk Zaïn')
  280. end
  281. it "decodes the content properly with force_encoding specified" do
  282. @valid_options['force_encoding'] = 'iso-8859-1'
  283. agent.check
  284. event = agent.events.first
  285. expect(event.payload['title']).to eq('Mëkanïk Zaïn')
  286. end
  287. end
  288. context 'with podcast elements' do
  289. before do
  290. @valid_options['url'] = 'http://example.com/podcast.rss'
  291. @valid_options['include_feed_info'] = true
  292. end
  293. let :feed_info do
  294. {
  295. "id" => nil,
  296. "type" => "rss",
  297. "url" => "http://www.example.com/podcasts/everything/index.html",
  298. "links" => [ { "href" => "http://www.example.com/podcasts/everything/index.html" } ],
  299. "title" => "All About Everything",
  300. "description" => "All About Everything is a show about everything. Each week we dive into any subject known to man and talk about it as much as we can. Look for our podcast in the Podcasts app or in the iTunes Store",
  301. "copyright" => "℗ & © 2014 John Doe & Family",
  302. "generator" => nil,
  303. "icon" => nil,
  304. "authors" => [
  305. "John Doe"
  306. ],
  307. "date_published" => nil,
  308. "last_updated" => nil,
  309. "itunes_categories" => [
  310. "Technology", "Gadgets",
  311. "TV & Film",
  312. "Arts", "Food"
  313. ],
  314. "itunes_complete" => "yes",
  315. "itunes_explicit" => "no",
  316. "itunes_image" => "http://example.com/podcasts/everything/AllAboutEverything.jpg",
  317. "itunes_owners" => ["John Doe <john.doe@example.com>"],
  318. "itunes_subtitle" => "A show about everything",
  319. "itunes_summary" => "All About Everything is a show about everything. Each week we dive into any subject known to man and talk about it as much as we can. Look for our podcast in the Podcasts app or in the iTunes Store",
  320. "language" => "en-us"
  321. }
  322. end
  323. it "is parsed correctly" do
  324. expect {
  325. agent.check
  326. }.to change { agent.events.count }.by(4)
  327. expect(agent.events.map(&:payload)).to match([
  328. {
  329. "feed" => feed_info,
  330. "id" => "http://example.com/podcasts/archive/aae20140601.mp3",
  331. "url" => "http://example.com/podcasts/archive/aae20140601.mp3",
  332. "urls" => ["http://example.com/podcasts/archive/aae20140601.mp3"],
  333. "links" => [],
  334. "title" => "Red,Whine, & Blue",
  335. "description" => nil,
  336. "content" => nil,
  337. "image" => nil,
  338. "enclosure" => {
  339. "url" => "http://example.com/podcasts/everything/AllAboutEverythingEpisode4.mp3",
  340. "type" => "audio/mpeg",
  341. "length" => "498537"
  342. },
  343. "authors" => ["<Various>"],
  344. "categories" => [],
  345. "date_published" => "2016-03-11T01:15:00+00:00",
  346. "last_updated" => "2016-03-11T01:15:00+00:00",
  347. "itunes_duration" => "03:59",
  348. "itunes_explicit" => "no",
  349. "itunes_image" => "http://example.com/podcasts/everything/AllAboutEverything/Episode4.jpg",
  350. "itunes_subtitle" => "Red + Blue != Purple",
  351. "itunes_summary" => "This week we talk about surviving in a Red state if you are a Blue person. Or vice versa."
  352. },
  353. {
  354. "feed" => feed_info,
  355. "id" => "http://example.com/podcasts/archive/aae20140697.m4v",
  356. "url" => "http://example.com/podcasts/archive/aae20140697.m4v",
  357. "urls" => ["http://example.com/podcasts/archive/aae20140697.m4v"],
  358. "links" => [],
  359. "title" => "The Best Chili",
  360. "description" => nil,
  361. "content" => nil,
  362. "image" => nil,
  363. "enclosure" => {
  364. "url" => "http://example.com/podcasts/everything/AllAboutEverythingEpisode2.m4v",
  365. "type" => "video/x-m4v",
  366. "length" => "5650889"
  367. },
  368. "authors" => ["Jane Doe"],
  369. "categories" => [],
  370. "date_published" => "2016-03-10T02:00:00-07:00",
  371. "last_updated" => "2016-03-10T02:00:00-07:00",
  372. "itunes_closed_captioned" => "Yes",
  373. "itunes_duration" => "04:34",
  374. "itunes_explicit" => "no",
  375. "itunes_image" => "http://example.com/podcasts/everything/AllAboutEverything/Episode3.jpg",
  376. "itunes_subtitle" => "Jane and Eric",
  377. "itunes_summary" => "This week we talk about the best Chili in the world. Which chili is better?"
  378. },
  379. {
  380. "feed" => feed_info,
  381. "id" => "http://example.com/podcasts/archive/aae20140608.mp4",
  382. "url" => "http://example.com/podcasts/archive/aae20140608.mp4",
  383. "urls" => ["http://example.com/podcasts/archive/aae20140608.mp4"],
  384. "links" => [],
  385. "title" => "Socket Wrench Shootout",
  386. "description" => nil,
  387. "content" => nil,
  388. "image" => nil,
  389. "enclosure" => {
  390. "url" => "http://example.com/podcasts/everything/AllAboutEverythingEpisode2.mp4",
  391. "type" => "video/mp4",
  392. "length" => "5650889"
  393. },
  394. "authors" => ["Jane Doe"],
  395. "categories" => [],
  396. "date_published" => "2016-03-09T13:00:00-05:00",
  397. "last_updated" => "2016-03-09T13:00:00-05:00",
  398. "itunes_duration" => "04:34",
  399. "itunes_explicit" => "no",
  400. "itunes_image" => "http://example.com/podcasts/everything/AllAboutEverything/Episode2.jpg",
  401. "itunes_subtitle" => "Comparing socket wrenches is fun!",
  402. "itunes_summary" => "This week we talk about metric vs. Old English socket wrenches. Which one is better? Do you really need both? Get all of your answers here."
  403. },
  404. {
  405. "feed" => feed_info,
  406. "id" => "http://example.com/podcasts/archive/aae20140615.m4a",
  407. "url" => "http://example.com/podcasts/archive/aae20140615.m4a",
  408. "urls" => ["http://example.com/podcasts/archive/aae20140615.m4a"],
  409. "links" => [],
  410. "title" => "Shake Shake Shake Your Spices",
  411. "description" => nil,
  412. "content" => nil,
  413. "image" => nil,
  414. "enclosure" => {
  415. "url" => "http://example.com/podcasts/everything/AllAboutEverythingEpisode3.m4a",
  416. "type" => "audio/x-m4a",
  417. "length" => "8727310"
  418. },
  419. "authors" => ["John Doe"],
  420. "categories" => [],
  421. "date_published" => "2016-03-08T12:00:00+00:00",
  422. "last_updated" => "2016-03-08T12:00:00+00:00",
  423. "itunes_duration" => "07:04",
  424. "itunes_explicit" => "no",
  425. "itunes_image" => "http://example.com/podcasts/everything/AllAboutEverything/Episode1.jpg",
  426. "itunes_subtitle" => "A short primer on table spices",
  427. "itunes_summary" => "This week we talk about <a href=\"https://itunes/apple.com/us/book/antique-trader-salt-pepper/id429691295?mt=11\">salt and pepper shakers</a>, comparing and contrasting pour rates, construction materials, and overall aesthetics. Come and join the party!"
  428. }
  429. ])
  430. end
  431. end
  432. context 'of YouTube' do
  433. before do
  434. @valid_options['url'] = 'http://example.com/youtube.xml'
  435. @valid_options['include_feed_info'] = true
  436. end
  437. it "is parsed correctly" do
  438. expect {
  439. agent.check
  440. }.to change { agent.events.count }.by(15)
  441. expect(agent.events.first.payload).to match({
  442. "feed" => {
  443. "id" => "yt:channel:UCoTLdfNePDQzvdEgIToLIUg",
  444. "type" => "atom",
  445. "url" => "https://www.youtube.com/channel/UCoTLdfNePDQzvdEgIToLIUg",
  446. "links" => [
  447. { "href" => "http://www.youtube.com/feeds/videos.xml?channel_id=UCoTLdfNePDQzvdEgIToLIUg", "rel" => "self" },
  448. { "href" => "https://www.youtube.com/channel/UCoTLdfNePDQzvdEgIToLIUg", "rel" => "alternate" }
  449. ],
  450. "title" => "SecDSM",
  451. "description" => nil,
  452. "copyright" => nil,
  453. "generator" => nil,
  454. "icon" => nil,
  455. "authors" => ["SecDSM (https://www.youtube.com/channel/UCoTLdfNePDQzvdEgIToLIUg)"],
  456. "date_published" => "2016-07-28T18:46:21+00:00",
  457. "last_updated" => "2016-07-28T18:46:21+00:00"
  458. },
  459. "id" => "yt:video:OCs1E0vP7Oc",
  460. "authors" => ["SecDSM (https://www.youtube.com/channel/UCoTLdfNePDQzvdEgIToLIUg)"],
  461. "categories" => [],
  462. "content" => nil,
  463. "date_published" => "2017-06-15T02:36:17+00:00",
  464. "description" => nil,
  465. "enclosure" => nil,
  466. "image" => nil,
  467. "last_updated" => "2017-06-15T02:36:17+00:00",
  468. "links" => [
  469. { "href"=>"https://www.youtube.com/watch?v=OCs1E0vP7Oc", "rel"=>"alternate" }
  470. ],
  471. "title" => "SecDSM 2017 March - Talk 01",
  472. "url" => "https://www.youtube.com/watch?v=OCs1E0vP7Oc",
  473. "urls" => ["https://www.youtube.com/watch?v=OCs1E0vP7Oc"]
  474. })
  475. end
  476. end
  477. end
  478. describe 'logging errors with the feed url' do
  479. it 'includes the feed URL when an exception is raised' do
  480. expect(Feedjira).to receive(:parse).with(anything) { raise StandardError.new("Some error!") }
  481. expect {
  482. agent.check
  483. }.not_to raise_error
  484. expect(agent.logs.last.message).to match(%r[Failed to fetch https://github.com])
  485. end
  486. end
  487. end