data_output_agent_spec.rb 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. # encoding: utf-8
  2. require 'rails_helper'
  3. describe Agents::DataOutputAgent do
  4. let(:agent) do
  5. _agent = Agents::DataOutputAgent.new(:name => 'My Data Output Agent')
  6. _agent.options = _agent.default_options.merge('secrets' => ['secret1', 'secret2'], 'events_to_show' => 3)
  7. _agent.options['template']['item']['pubDate'] = "{{date}}"
  8. _agent.options['template']['item']['category'] = "{{ category | as_object }}"
  9. _agent.user = users(:bob)
  10. _agent.sources << agents(:bob_website_agent)
  11. _agent.save!
  12. _agent
  13. end
  14. describe "#working?" do
  15. it "checks if events have been received within expected receive period" do
  16. expect(agent).not_to be_working
  17. Agents::DataOutputAgent.async_receive agent.id, [events(:bob_website_agent_event).id]
  18. expect(agent.reload).to be_working
  19. two_days_from_now = 2.days.from_now
  20. allow(Time).to receive(:now) { two_days_from_now }
  21. expect(agent.reload).not_to be_working
  22. end
  23. end
  24. describe "validation" do
  25. before do
  26. expect(agent).to be_valid
  27. end
  28. it "should validate presence and length of secrets" do
  29. agent.options[:secrets] = ""
  30. expect(agent).not_to be_valid
  31. agent.options[:secrets] = "foo"
  32. expect(agent).not_to be_valid
  33. agent.options[:secrets] = "foo/bar"
  34. expect(agent).not_to be_valid
  35. agent.options[:secrets] = "foo.xml"
  36. expect(agent).not_to be_valid
  37. agent.options[:secrets] = false
  38. expect(agent).not_to be_valid
  39. agent.options[:secrets] = []
  40. expect(agent).not_to be_valid
  41. agent.options[:secrets] = ["foo.xml"]
  42. expect(agent).not_to be_valid
  43. agent.options[:secrets] = ["hello", true]
  44. expect(agent).not_to be_valid
  45. agent.options[:secrets] = ["hello"]
  46. expect(agent).to be_valid
  47. agent.options[:secrets] = ["hello", "world"]
  48. expect(agent).to be_valid
  49. end
  50. it "should validate presence of expected_receive_period_in_days" do
  51. agent.options[:expected_receive_period_in_days] = ""
  52. expect(agent).not_to be_valid
  53. agent.options[:expected_receive_period_in_days] = 0
  54. expect(agent).not_to be_valid
  55. agent.options[:expected_receive_period_in_days] = -1
  56. expect(agent).not_to be_valid
  57. end
  58. it "should validate presence of template and template.item" do
  59. agent.options[:template] = ""
  60. expect(agent).not_to be_valid
  61. agent.options[:template] = {}
  62. expect(agent).not_to be_valid
  63. agent.options[:template] = { 'item' => 'foo' }
  64. expect(agent).not_to be_valid
  65. agent.options[:template] = { 'item' => { 'title' => 'hi' } }
  66. expect(agent).to be_valid
  67. end
  68. end
  69. describe "#receive" do
  70. it "should push to hubs when push_hubs is given" do
  71. agent.options[:push_hubs] = %w[http://push.example.com]
  72. agent.options[:template] = { 'link' => 'http://huginn.example.org' }
  73. alist = nil
  74. stub_request(:post, 'http://push.example.com/')
  75. .with(headers: { 'Content-Type' => %r{\Aapplication/x-www-form-urlencoded\s*(?:;|\z)} })
  76. .to_return { |request|
  77. alist = URI.decode_www_form(request.body).sort
  78. { status: 200, body: 'ok' }
  79. }
  80. agent.receive(events(:bob_website_agent_event))
  81. expect(alist).to eq [
  82. ["hub.mode", "publish"],
  83. ["hub.url", agent.feed_url(secret: agent.options[:secrets].first, format: :xml)]
  84. ]
  85. end
  86. end
  87. describe "#receive_web_request" do
  88. before do
  89. current_time = Time.now
  90. allow(Time).to receive(:now) { current_time }
  91. agents(:bob_website_agent).events.destroy_all
  92. end
  93. it "requires a valid secret" do
  94. content, status, content_type = agent.receive_web_request({ 'secret' => 'fake' }, 'get', 'text/xml')
  95. expect(status).to eq(401)
  96. expect(content).to eq("Not Authorized")
  97. content, status, content_type = agent.receive_web_request({ 'secret' => 'fake' }, 'get', 'application/json')
  98. expect(status).to eq(401)
  99. expect(content).to eq({ :error => "Not Authorized" })
  100. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'application/json')
  101. expect(status).to eq(200)
  102. end
  103. describe "outputting events as RSS and JSON" do
  104. let!(:event1) do
  105. agents(:bob_website_agent).create_event :payload => {
  106. "site_title" => "XKCD",
  107. "url" => "http://imgs.xkcd.com/comics/evolving.png",
  108. "title" => "Evolving",
  109. "hovertext" => "Biologists play reverse Pokemon, trying to avoid putting any one team member on the front lines long enough for the experience to cause evolution.",
  110. "category" => []
  111. }
  112. end
  113. let!(:event2) do
  114. agents(:bob_website_agent).create_event :payload => {
  115. "site_title" => "XKCD",
  116. "url" => "http://imgs.xkcd.com/comics/evolving2.png",
  117. "title" => "Evolving again",
  118. "date" => '',
  119. "hovertext" => "Something else",
  120. "category" => ["Category 1", "Category 2"]
  121. }
  122. end
  123. let!(:event3) do
  124. agents(:bob_website_agent).create_event :payload => {
  125. "site_title" => "XKCD",
  126. "url" => "http://imgs.xkcd.com/comics/evolving0.png",
  127. "title" => "Evolving yet again with a past date",
  128. "date" => '2014/05/05',
  129. "hovertext" => "A small text",
  130. "category" => ["Some category"]
  131. }
  132. end
  133. it "can output RSS" do
  134. allow(agent).to receive(:feed_link) { "https://yoursite.com" }
  135. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
  136. expect(status).to eq(200)
  137. expect(content_type).to eq('application/rss+xml')
  138. expect(content.gsub(/\s+/, '')).to eq Utils.unindent(<<-XML).gsub(/\s+/, '')
  139. <?xml version="1.0" encoding="UTF-8" ?>
  140. <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
  141. <channel>
  142. <atom:link href="https://yoursite.com/users/#{agent.user.id}/web_requests/#{agent.id}/secret1.xml" rel="self" type="application/rss+xml"/>
  143. <atom:icon>https://yoursite.com/favicon.ico</atom:icon>
  144. <title>XKCD comics as a feed</title>
  145. <description>This is a feed of recent XKCD comics, generated by Huginn</description>
  146. <link>https://yoursite.com</link>
  147. <lastBuildDate>#{Time.now.rfc2822}</lastBuildDate>
  148. <pubDate>#{Time.now.rfc2822}</pubDate>
  149. <ttl>60</ttl>
  150. <item>
  151. <title>Evolving yet again with a past date</title>
  152. <description>Secret hovertext: A small text</description>
  153. <link>http://imgs.xkcd.com/comics/evolving0.png</link>
  154. <pubDate>#{Time.zone.parse(event3.payload['date']).rfc2822}</pubDate>
  155. <category>Some category</category>
  156. <guid isPermaLink="false">#{event3.id}</guid>
  157. </item>
  158. <item>
  159. <title>Evolving again</title>
  160. <description>Secret hovertext: Something else</description>
  161. <link>http://imgs.xkcd.com/comics/evolving2.png</link>
  162. <pubDate>#{event2.created_at.rfc2822}</pubDate>
  163. <category>Category 1</category>
  164. <category>Category 2</category>
  165. <guid isPermaLink="false">#{event2.id}</guid>
  166. </item>
  167. <item>
  168. <title>Evolving</title>
  169. <description>Secret hovertext: Biologists play reverse Pokemon, trying to avoid putting any one team member on the front lines long enough for the experience to cause evolution.</description>
  170. <link>http://imgs.xkcd.com/comics/evolving.png</link>
  171. <pubDate>#{event1.created_at.rfc2822}</pubDate>
  172. <guid isPermaLink="false">#{event1.id}</guid>
  173. </item>
  174. </channel>
  175. </rss>
  176. XML
  177. end
  178. describe "with custom rss_content_type given" do
  179. before do
  180. agent.options['rss_content_type'] = 'text/xml'
  181. agent.save!
  182. end
  183. it "can output RSS with the Content-Type" do
  184. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
  185. expect(status).to eq(200)
  186. expect(content_type).to eq('text/xml')
  187. end
  188. end
  189. it "can output RSS with hub links when push_hubs is specified" do
  190. allow(agent).to receive(:feed_link) { "https://yoursite.com" }
  191. agent.options[:push_hubs] = %w[https://pubsubhubbub.superfeedr.com/ https://pubsubhubbub.appspot.com/]
  192. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
  193. expect(status).to eq(200)
  194. expect(content_type).to eq('application/rss+xml')
  195. xml = Nokogiri::XML(content)
  196. expect(xml.xpath('/rss/channel/atom:link[@rel="hub"]/@href').map(&:text).sort).to eq agent.options[:push_hubs].sort
  197. end
  198. it "can output JSON" do
  199. agent.options['template']['item']['foo'] = "hi"
  200. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret2' }, 'get', 'application/json')
  201. expect(status).to eq(200)
  202. expect(content).to eq({
  203. 'title' => 'XKCD comics as a feed',
  204. 'description' => 'This is a feed of recent XKCD comics, generated by Huginn',
  205. 'pubDate' => Time.now,
  206. 'items' => [
  207. {
  208. 'title' => 'Evolving yet again with a past date',
  209. 'description' => 'Secret hovertext: A small text',
  210. 'link' => 'http://imgs.xkcd.com/comics/evolving0.png',
  211. 'guid' => {"contents" => event3.id, "isPermaLink" => "false"},
  212. 'pubDate' => Time.zone.parse(event3.payload['date']).rfc2822,
  213. 'category' => ['Some category'],
  214. 'foo' => 'hi'
  215. },
  216. {
  217. 'title' => 'Evolving again',
  218. 'description' => 'Secret hovertext: Something else',
  219. 'link' => 'http://imgs.xkcd.com/comics/evolving2.png',
  220. 'guid' => {"contents" => event2.id, "isPermaLink" => "false"},
  221. 'pubDate' => event2.created_at.rfc2822,
  222. 'category' => ['Category 1', 'Category 2'],
  223. 'foo' => 'hi'
  224. },
  225. {
  226. 'title' => 'Evolving',
  227. 'description' => 'Secret hovertext: Biologists play reverse Pokemon, trying to avoid putting any one team member on the front lines long enough for the experience to cause evolution.',
  228. 'link' => 'http://imgs.xkcd.com/comics/evolving.png',
  229. 'guid' => {"contents" => event1.id, "isPermaLink" => "false"},
  230. 'pubDate' => event1.created_at.rfc2822,
  231. 'category' => [],
  232. 'foo' => 'hi'
  233. }
  234. ]
  235. })
  236. end
  237. describe "with custom response_headers given" do
  238. before do
  239. agent.options['response_headers'] = {"Access-Control-Allow-Origin" => "*", "X-My-Custom-Header" => "hello"}
  240. agent.save!
  241. end
  242. it "can respond with custom headers" do
  243. content, status, content_type, response_headers = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
  244. expect(status).to eq(200)
  245. expect(response_headers).to eq({"Access-Control-Allow-Origin" => "*", "X-My-Custom-Header" => "hello"})
  246. end
  247. end
  248. context 'with more events' do
  249. let!(:event4) do
  250. agents(:bob_website_agent).create_event payload: {
  251. 'site_title' => 'XKCD',
  252. 'url' => 'http://imgs.xkcd.com/comics/comic1.png',
  253. 'title' => 'Comic 1',
  254. 'date' => '',
  255. 'hovertext' => 'Hovertext for Comic 1'
  256. }
  257. end
  258. let!(:event5) do
  259. agents(:bob_website_agent).create_event payload: {
  260. 'site_title' => 'XKCD',
  261. 'url' => 'http://imgs.xkcd.com/comics/comic2.png',
  262. 'title' => 'Comic 2',
  263. 'date' => '',
  264. 'hovertext' => 'Hovertext for Comic 2'
  265. }
  266. end
  267. let!(:event6) do
  268. agents(:bob_website_agent).create_event payload: {
  269. 'site_title' => 'XKCD',
  270. 'url' => 'http://imgs.xkcd.com/comics/comic3.png',
  271. 'title' => 'Comic 3',
  272. 'date' => '',
  273. 'hovertext' => 'Hovertext for Comic 3'
  274. }
  275. end
  276. describe 'limiting' do
  277. it 'can select the last `events_to_show` events' do
  278. agent.options['events_to_show'] = 2
  279. content, _status, _content_type = agent.receive_web_request({ 'secret' => 'secret2' }, 'get', 'application/json')
  280. expect(content['items'].map {|i| i["title"] }).to eq(["Comic 3", "Comic 2"])
  281. end
  282. end
  283. end
  284. describe 'ordering' do
  285. before do
  286. agent.options['events_order'] = ['{{hovertext}}']
  287. agent.options['events_list_order'] = ['{{title}}']
  288. end
  289. it 'can reorder the last `events_to_show` events based on a Liquid expression' do
  290. agent.options['events_to_show'] = 2
  291. asc_content, _status, _content_type = agent.receive_web_request({ 'secret' => 'secret2' }, 'get', 'application/json')
  292. expect(asc_content['items'].map {|i| i["title"] }).to eq(["Evolving", "Evolving again"])
  293. agent.options['events_to_show'] = 40
  294. asc_content, _status, _content_type = agent.receive_web_request({ 'secret' => 'secret2' }, 'get', 'application/json')
  295. expect(asc_content['items'].map {|i| i["title"] }).to eq(["Evolving", "Evolving again", "Evolving yet again with a past date"])
  296. agent.options['events_list_order'] = [['{{title}}', 'string', true]]
  297. desc_content, _status, _content_type = agent.receive_web_request({ 'secret' => 'secret2' }, 'get', 'application/json')
  298. expect(desc_content['items']).to eq(asc_content['items'].reverse)
  299. end
  300. end
  301. describe "interpolating \"events\"" do
  302. before do
  303. agent.options['template']['title'] = "XKCD comics as a feed{% if events.first.site_title %} ({{events.first.site_title}}){% endif %}"
  304. agent.save!
  305. end
  306. it "can output RSS" do
  307. allow(agent).to receive(:feed_link) { "https://yoursite.com" }
  308. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
  309. expect(status).to eq(200)
  310. expect(content_type).to eq('application/rss+xml')
  311. expect(Nokogiri(content).at('/rss/channel/title/text()').text).to eq('XKCD comics as a feed (XKCD)')
  312. end
  313. it "can output JSON" do
  314. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret2' }, 'get', 'application/json')
  315. expect(status).to eq(200)
  316. expect(content['title']).to eq('XKCD comics as a feed (XKCD)')
  317. end
  318. context "with event with \"events\"" do
  319. before do
  320. agent.sources.first.create_event payload: {
  321. 'site_title' => 'XKCD',
  322. 'url' => 'http://imgs.xkcd.com/comics/comicX.png',
  323. 'title' => 'Comic X',
  324. 'date' => '',
  325. 'hovertext' => 'Hovertext for Comic X',
  326. 'events' => 'Events!'
  327. }
  328. agent.options['template']['item']['events_data'] = "{{ events }}"
  329. agent.save!
  330. end
  331. it "can access the value without being overridden" do
  332. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret2' }, 'get', 'application/json')
  333. expect(status).to eq(200)
  334. expect(content['items'].first['events_data']).to eq('Events!')
  335. end
  336. end
  337. end
  338. describe "with a specified icon" do
  339. before do
  340. agent.options['template']['icon'] = 'https://somesite.com/icon.png'
  341. agent.save!
  342. end
  343. it "can output RSS" do
  344. allow(agent).to receive(:feed_link) { "https://yoursite.com" }
  345. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
  346. expect(status).to eq(200)
  347. expect(content_type).to eq('application/rss+xml')
  348. expect(Nokogiri(content).at('/rss/channel/atom:icon/text()').text).to eq('https://somesite.com/icon.png')
  349. end
  350. end
  351. describe "with media namespace not set" do
  352. before do
  353. agent.options['ns_media'] = nil
  354. agent.save!
  355. end
  356. it "can output RSS" do
  357. allow(agent).to receive(:feed_link) { "https://yoursite.com" }
  358. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
  359. expect(status).to eq(200)
  360. expect(content_type).to eq('application/rss+xml')
  361. doc = Nokogiri(content)
  362. namespaces = doc.collect_namespaces
  363. expect(namespaces).not_to include("xmlns:media")
  364. end
  365. end
  366. describe "with media namespace set true" do
  367. before do
  368. agent.options['ns_media'] = 'true'
  369. agent.save!
  370. end
  371. it "can output RSS" do
  372. allow(agent).to receive(:feed_link) { "https://yoursite.com" }
  373. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
  374. expect(status).to eq(200)
  375. expect(content_type).to eq('application/rss+xml')
  376. doc = Nokogiri(content)
  377. namespaces = doc.collect_namespaces
  378. expect(namespaces).to include(
  379. "xmlns:media" => 'http://search.yahoo.com/mrss/'
  380. )
  381. end
  382. end
  383. describe "with media namespace set false" do
  384. before do
  385. agent.options['ns_media'] = 'false'
  386. agent.save!
  387. end
  388. it "can output RSS" do
  389. allow(agent).to receive(:feed_link) { "https://yoursite.com" }
  390. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
  391. expect(status).to eq(200)
  392. expect(content_type).to eq('application/rss+xml')
  393. doc = Nokogiri(content)
  394. namespaces = doc.collect_namespaces
  395. expect(namespaces).not_to include("xmlns:media")
  396. end
  397. end
  398. describe "with itunes namespace not set" do
  399. before do
  400. agent.options['ns_itunes'] = nil
  401. agent.save!
  402. end
  403. it "can output RSS" do
  404. allow(agent).to receive(:feed_link) { "https://yoursite.com" }
  405. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
  406. expect(status).to eq(200)
  407. expect(content_type).to eq('application/rss+xml')
  408. doc = Nokogiri(content)
  409. namespaces = doc.collect_namespaces
  410. expect(namespaces).not_to include("xmlns:itunes")
  411. expect(doc.at("/rss/channel/*[local-name()='itunes:image']")).to be_nil
  412. end
  413. end
  414. describe "with itunes namespace set true" do
  415. before do
  416. agent.options['ns_itunes'] = 'true'
  417. agent.save!
  418. end
  419. it "can output RSS" do
  420. allow(agent).to receive(:feed_link) { "https://yoursite.com" }
  421. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
  422. expect(status).to eq(200)
  423. expect(content_type).to eq('application/rss+xml')
  424. doc = Nokogiri(content)
  425. namespaces = doc.collect_namespaces
  426. expect(namespaces).to include(
  427. "xmlns:itunes" => 'http://www.itunes.com/dtds/podcast-1.0.dtd'
  428. )
  429. expect(doc.at('/rss/channel/itunes:image').attr('href')).to eq('https://yoursite.com/favicon.ico')
  430. end
  431. end
  432. describe "with itunes namespace set false" do
  433. before do
  434. agent.options['ns_itunes'] = 'false'
  435. agent.save!
  436. end
  437. it "can output RSS" do
  438. allow(agent).to receive(:feed_link) { "https://yoursite.com" }
  439. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
  440. expect(status).to eq(200)
  441. expect(content_type).to eq('application/rss+xml')
  442. doc = Nokogiri(content)
  443. namespaces = doc.collect_namespaces
  444. expect(namespaces).not_to include("xmlns:itunes")
  445. end
  446. end
  447. end
  448. describe "outputting nesting" do
  449. before do
  450. agent.options['template']['item']['enclosure'] = {
  451. "_attributes" => {
  452. "type" => "audio/mpeg",
  453. "url" => "{{media_url}}"
  454. }
  455. }
  456. agent.options['template']['item']['foo'] = {
  457. "_attributes" => {
  458. "attr" => "attr-value-{{foo}}"
  459. },
  460. "_contents" => "Foo: {{foo}}"
  461. }
  462. agent.options['template']['item']['nested'] = {
  463. "_attributes" => {
  464. "key" => "value"
  465. },
  466. "_contents" => {
  467. "title" => "some title"
  468. }
  469. }
  470. agent.options['template']['item']['simpleNested'] = {
  471. "title" => "some title",
  472. "complex" => {
  473. "_attributes" => {
  474. "key" => "value"
  475. },
  476. "_contents" => {
  477. "first" => {
  478. "_attributes" => {
  479. "a" => "b"
  480. },
  481. "_contents" => {
  482. "second" => "value"
  483. }
  484. }
  485. }
  486. }
  487. }
  488. agent.save!
  489. end
  490. let!(:event) do
  491. agents(:bob_website_agent).create_event :payload => {
  492. "url" => "http://imgs.xkcd.com/comics/evolving.png",
  493. "title" => "Evolving",
  494. "hovertext" => "Biologists play reverse Pokemon, trying to avoid putting any one team member on the front lines long enough for the experience to cause evolution.",
  495. "media_url" => "http://google.com/audio.mpeg",
  496. "category" => ["Category 1", "Category 2"],
  497. "foo" => 1
  498. }
  499. end
  500. it "can output JSON" do
  501. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret2' }, 'get', 'application/json')
  502. expect(status).to eq(200)
  503. expect(content['items'].first).to eq(
  504. {
  505. 'title' => 'Evolving',
  506. 'description' => 'Secret hovertext: Biologists play reverse Pokemon, trying to avoid putting any one team member on the front lines long enough for the experience to cause evolution.',
  507. 'link' => 'http://imgs.xkcd.com/comics/evolving.png',
  508. 'guid' => {"contents" => event.id, "isPermaLink" => "false"},
  509. 'pubDate' => event.created_at.rfc2822,
  510. 'category' => ['Category 1', 'Category 2'],
  511. 'enclosure' => {
  512. "type" => "audio/mpeg",
  513. "url" => "http://google.com/audio.mpeg"
  514. },
  515. 'foo' => {
  516. 'attr' => 'attr-value-1',
  517. 'contents' => 'Foo: 1'
  518. },
  519. 'nested' => {
  520. "key" => "value",
  521. "title" => "some title"
  522. },
  523. 'simpleNested' => {
  524. "title" => "some title",
  525. "complex" => {
  526. "key"=>"value",
  527. "first" => {
  528. "a" => "b",
  529. "second"=>"value"
  530. }
  531. }
  532. }
  533. }
  534. )
  535. end
  536. it "can output RSS" do
  537. allow(agent).to receive(:feed_link) { "https://yoursite.com" }
  538. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
  539. expect(status).to eq(200)
  540. expect(content_type).to eq('application/rss+xml')
  541. expect(content.gsub(/\s+/, '')).to eq Utils.unindent(<<-XML).gsub(/\s+/, '')
  542. <?xml version="1.0" encoding="UTF-8" ?>
  543. <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" >
  544. <channel>
  545. <atom:link href="https://yoursite.com/users/#{agent.user.id}/web_requests/#{agent.id}/secret1.xml" rel="self" type="application/rss+xml"/>
  546. <atom:icon>https://yoursite.com/favicon.ico</atom:icon>
  547. <title>XKCD comics as a feed</title>
  548. <description>This is a feed of recent XKCD comics, generated by Huginn</description>
  549. <link>https://yoursite.com</link>
  550. <lastBuildDate>#{Time.now.rfc2822}</lastBuildDate>
  551. <pubDate>#{Time.now.rfc2822}</pubDate>
  552. <ttl>60</ttl>
  553. <item>
  554. <title>Evolving</title>
  555. <description>Secret hovertext: Biologists play reverse Pokemon, trying to avoid putting any one team member on the front lines long enough for the experience to cause evolution.</description>
  556. <link>http://imgs.xkcd.com/comics/evolving.png</link>
  557. <pubDate>#{event.created_at.rfc2822}</pubDate>
  558. <category>Category 1</category>
  559. <category>Category 2</category>
  560. <enclosure type="audio/mpeg" url="http://google.com/audio.mpeg" />
  561. <foo attr="attr-value-1">Foo: 1</foo>
  562. <nested key="value"><title>some title</title></nested>
  563. <simpleNested>
  564. <title>some title</title>
  565. <complex key="value">
  566. <first a="b">
  567. <second>value</second>
  568. </first>
  569. </complex>
  570. </simpleNested>
  571. <guid isPermaLink="false">#{event.id}</guid>
  572. </item>
  573. </channel>
  574. </rss>
  575. XML
  576. end
  577. end
  578. end
  579. end