data_output_agent_spec.rb 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. # encoding: utf-8
  2. require 'spec_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.user = users(:bob)
  9. _agent.sources << agents(:bob_website_agent)
  10. _agent.save!
  11. _agent
  12. end
  13. describe "#working?" do
  14. it "checks if events have been received within expected receive period" do
  15. expect(agent).not_to be_working
  16. Agents::DataOutputAgent.async_receive agent.id, [events(:bob_website_agent_event).id]
  17. expect(agent.reload).to be_working
  18. two_days_from_now = 2.days.from_now
  19. stub(Time).now { two_days_from_now }
  20. expect(agent.reload).not_to be_working
  21. end
  22. end
  23. describe "validation" do
  24. before do
  25. expect(agent).to be_valid
  26. end
  27. it "should validate presence and length of secrets" do
  28. agent.options[:secrets] = ""
  29. expect(agent).not_to be_valid
  30. agent.options[:secrets] = "foo"
  31. expect(agent).not_to be_valid
  32. agent.options[:secrets] = "foo/bar"
  33. expect(agent).not_to be_valid
  34. agent.options[:secrets] = "foo.xml"
  35. expect(agent).not_to be_valid
  36. agent.options[:secrets] = false
  37. expect(agent).not_to be_valid
  38. agent.options[:secrets] = []
  39. expect(agent).not_to be_valid
  40. agent.options[:secrets] = ["foo.xml"]
  41. expect(agent).not_to be_valid
  42. agent.options[:secrets] = ["hello", true]
  43. expect(agent).not_to be_valid
  44. agent.options[:secrets] = ["hello"]
  45. expect(agent).to be_valid
  46. agent.options[:secrets] = ["hello", "world"]
  47. expect(agent).to be_valid
  48. end
  49. it "should validate presence of expected_receive_period_in_days" do
  50. agent.options[:expected_receive_period_in_days] = ""
  51. expect(agent).not_to be_valid
  52. agent.options[:expected_receive_period_in_days] = 0
  53. expect(agent).not_to be_valid
  54. agent.options[:expected_receive_period_in_days] = -1
  55. expect(agent).not_to be_valid
  56. end
  57. it "should validate presence of template and template.item" do
  58. agent.options[:template] = ""
  59. expect(agent).not_to be_valid
  60. agent.options[:template] = {}
  61. expect(agent).not_to be_valid
  62. agent.options[:template] = { 'item' => 'foo' }
  63. expect(agent).not_to be_valid
  64. agent.options[:template] = { 'item' => { 'title' => 'hi' } }
  65. expect(agent).to be_valid
  66. end
  67. end
  68. describe "#receive_web_request" do
  69. before do
  70. current_time = Time.now
  71. stub(Time).now { current_time }
  72. agents(:bob_website_agent).events.destroy_all
  73. end
  74. it "requires a valid secret" do
  75. content, status, content_type = agent.receive_web_request({ 'secret' => 'fake' }, 'get', 'text/xml')
  76. expect(status).to eq(401)
  77. expect(content).to eq("Not Authorized")
  78. content, status, content_type = agent.receive_web_request({ 'secret' => 'fake' }, 'get', 'application/json')
  79. expect(status).to eq(401)
  80. expect(content).to eq({ :error => "Not Authorized" })
  81. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'application/json')
  82. expect(status).to eq(200)
  83. end
  84. describe "outputting events as RSS and JSON" do
  85. let!(:event1) do
  86. agents(:bob_website_agent).create_event :payload => {
  87. "site_title" => "XKCD",
  88. "url" => "http://imgs.xkcd.com/comics/evolving.png",
  89. "title" => "Evolving",
  90. "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."
  91. }
  92. end
  93. let!(:event2) do
  94. agents(:bob_website_agent).create_event :payload => {
  95. "site_title" => "XKCD",
  96. "url" => "http://imgs.xkcd.com/comics/evolving2.png",
  97. "title" => "Evolving again",
  98. "date" => '',
  99. "hovertext" => "Something else"
  100. }
  101. end
  102. let!(:event3) do
  103. agents(:bob_website_agent).create_event :payload => {
  104. "site_title" => "XKCD",
  105. "url" => "http://imgs.xkcd.com/comics/evolving0.png",
  106. "title" => "Evolving yet again with a past date",
  107. "date" => '2014/05/05',
  108. "hovertext" => "Something else"
  109. }
  110. end
  111. it "can output RSS" do
  112. stub(agent).feed_link { "https://yoursite.com" }
  113. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
  114. expect(status).to eq(200)
  115. expect(content_type).to eq('text/xml')
  116. expect(content.gsub(/\s+/, '')).to eq Utils.unindent(<<-XML).gsub(/\s+/, '')
  117. <?xml version="1.0" encoding="UTF-8" ?>
  118. <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  119. <channel>
  120. <atom:link href="https://yoursite.com/users/#{agent.user.id}/web_requests/#{agent.id}/secret1.xml" rel="self" type="application/rss+xml"/>
  121. <atom:icon>https://yoursite.com/favicon.ico</atom:icon>
  122. <title>XKCD comics as a feed</title>
  123. <description>This is a feed of recent XKCD comics, generated by Huginn</description>
  124. <link>https://yoursite.com</link>
  125. <lastBuildDate>#{Time.now.rfc2822}</lastBuildDate>
  126. <pubDate>#{Time.now.rfc2822}</pubDate>
  127. <ttl>60</ttl>
  128. <item>
  129. <title>Evolving yet again with a past date</title>
  130. <description>Secret hovertext: Something else</description>
  131. <link>http://imgs.xkcd.com/comics/evolving0.png</link>
  132. <pubDate>#{Time.zone.parse(event3.payload['date']).rfc2822}</pubDate>
  133. <guid isPermaLink="false">#{event3.id}</guid>
  134. </item>
  135. <item>
  136. <title>Evolving again</title>
  137. <description>Secret hovertext: Something else</description>
  138. <link>http://imgs.xkcd.com/comics/evolving2.png</link>
  139. <pubDate>#{event2.created_at.rfc2822}</pubDate>
  140. <guid isPermaLink="false">#{event2.id}</guid>
  141. </item>
  142. <item>
  143. <title>Evolving</title>
  144. <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>
  145. <link>http://imgs.xkcd.com/comics/evolving.png</link>
  146. <pubDate>#{event1.created_at.rfc2822}</pubDate>
  147. <guid isPermaLink="false">#{event1.id}</guid>
  148. </item>
  149. </channel>
  150. </rss>
  151. XML
  152. end
  153. it "can output JSON" do
  154. agent.options['template']['item']['foo'] = "hi"
  155. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret2' }, 'get', 'application/json')
  156. expect(status).to eq(200)
  157. expect(content).to eq({
  158. 'title' => 'XKCD comics as a feed',
  159. 'description' => 'This is a feed of recent XKCD comics, generated by Huginn',
  160. 'pubDate' => Time.now,
  161. 'items' => [
  162. {
  163. 'title' => 'Evolving yet again with a past date',
  164. 'description' => 'Secret hovertext: Something else',
  165. 'link' => 'http://imgs.xkcd.com/comics/evolving0.png',
  166. 'guid' => {"contents" => event3.id, "isPermaLink" => "false"},
  167. 'pubDate' => Time.zone.parse(event3.payload['date']).rfc2822,
  168. 'foo' => 'hi'
  169. },
  170. {
  171. 'title' => 'Evolving again',
  172. 'description' => 'Secret hovertext: Something else',
  173. 'link' => 'http://imgs.xkcd.com/comics/evolving2.png',
  174. 'guid' => {"contents" => event2.id, "isPermaLink" => "false"},
  175. 'pubDate' => event2.created_at.rfc2822,
  176. 'foo' => 'hi'
  177. },
  178. {
  179. 'title' => 'Evolving',
  180. '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.',
  181. 'link' => 'http://imgs.xkcd.com/comics/evolving.png',
  182. 'guid' => {"contents" => event1.id, "isPermaLink" => "false"},
  183. 'pubDate' => event1.created_at.rfc2822,
  184. 'foo' => 'hi'
  185. }
  186. ]
  187. })
  188. end
  189. describe 'ordering' do
  190. before do
  191. agent.options['events_order'] = ['{{title}}']
  192. end
  193. it 'can reorder the events_to_show last events based on a Liquid expression' do
  194. asc_content, _status, _content_type = agent.receive_web_request({ 'secret' => 'secret2' }, 'get', 'application/json')
  195. expect(asc_content['items'].map {|i| i["title"] }).to eq(["Evolving", "Evolving again", "Evolving yet again with a past date"])
  196. agent.options['events_order'] = [['{{title}}', 'string', true]]
  197. desc_content, _status, _content_type = agent.receive_web_request({ 'secret' => 'secret2' }, 'get', 'application/json')
  198. expect(desc_content['items']).to eq(asc_content['items'].reverse)
  199. end
  200. end
  201. describe "interpolating \"events\"" do
  202. before do
  203. agent.options['template']['title'] = "XKCD comics as a feed{% if events.first.site_title %} ({{events.first.site_title}}){% endif %}"
  204. agent.save!
  205. end
  206. it "can output RSS" do
  207. stub(agent).feed_link { "https://yoursite.com" }
  208. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
  209. expect(status).to eq(200)
  210. expect(content_type).to eq('text/xml')
  211. expect(Nokogiri(content).at('/rss/channel/title/text()').text).to eq('XKCD comics as a feed (XKCD)')
  212. end
  213. it "can output JSON" do
  214. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret2' }, 'get', 'application/json')
  215. expect(status).to eq(200)
  216. expect(content['title']).to eq('XKCD comics as a feed (XKCD)')
  217. end
  218. end
  219. describe "with a specified icon" do
  220. before do
  221. agent.options['template']['icon'] = 'https://somesite.com/icon.png'
  222. agent.save!
  223. end
  224. it "can output RSS" do
  225. stub(agent).feed_link { "https://yoursite.com" }
  226. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
  227. expect(status).to eq(200)
  228. expect(content_type).to eq('text/xml')
  229. expect(Nokogiri(content).at('/rss/channel/atom:icon/text()').text).to eq('https://somesite.com/icon.png')
  230. end
  231. end
  232. end
  233. describe "outputting nesting" do
  234. before do
  235. agent.options['template']['item']['enclosure'] = {
  236. "_attributes" => {
  237. "type" => "audio/mpeg",
  238. "url" => "{{media_url}}"
  239. }
  240. }
  241. agent.options['template']['item']['foo'] = {
  242. "_attributes" => {
  243. "attr" => "attr-value-{{foo}}"
  244. },
  245. "_contents" => "Foo: {{foo}}"
  246. }
  247. agent.options['template']['item']['nested'] = {
  248. "_attributes" => {
  249. "key" => "value"
  250. },
  251. "_contents" => {
  252. "title" => "some title"
  253. }
  254. }
  255. agent.options['template']['item']['simpleNested'] = {
  256. "title" => "some title",
  257. "complex" => {
  258. "_attributes" => {
  259. "key" => "value"
  260. },
  261. "_contents" => {
  262. "first" => {
  263. "_attributes" => {
  264. "a" => "b"
  265. },
  266. "_contents" => {
  267. "second" => "value"
  268. }
  269. }
  270. }
  271. }
  272. }
  273. agent.save!
  274. end
  275. let!(:event) do
  276. agents(:bob_website_agent).create_event :payload => {
  277. "url" => "http://imgs.xkcd.com/comics/evolving.png",
  278. "title" => "Evolving",
  279. "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.",
  280. "media_url" => "http://google.com/audio.mpeg",
  281. "foo" => 1
  282. }
  283. end
  284. it "can output JSON" do
  285. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret2' }, 'get', 'application/json')
  286. expect(status).to eq(200)
  287. expect(content['items'].first).to eq(
  288. {
  289. 'title' => 'Evolving',
  290. '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.',
  291. 'link' => 'http://imgs.xkcd.com/comics/evolving.png',
  292. 'guid' => {"contents" => event.id, "isPermaLink" => "false"},
  293. 'pubDate' => event.created_at.rfc2822,
  294. 'enclosure' => {
  295. "type" => "audio/mpeg",
  296. "url" => "http://google.com/audio.mpeg"
  297. },
  298. 'foo' => {
  299. 'attr' => 'attr-value-1',
  300. 'contents' => 'Foo: 1'
  301. },
  302. 'nested' => {
  303. "key" => "value",
  304. "title" => "some title"
  305. },
  306. 'simpleNested' => {
  307. "title" => "some title",
  308. "complex" => {
  309. "key"=>"value",
  310. "first" => {
  311. "a" => "b",
  312. "second"=>"value"
  313. }
  314. }
  315. }
  316. }
  317. )
  318. end
  319. it "can output RSS" do
  320. stub(agent).feed_link { "https://yoursite.com" }
  321. content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
  322. expect(status).to eq(200)
  323. expect(content_type).to eq('text/xml')
  324. expect(content.gsub(/\s+/, '')).to eq Utils.unindent(<<-XML).gsub(/\s+/, '')
  325. <?xml version="1.0" encoding="UTF-8" ?>
  326. <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  327. <channel>
  328. <atom:link href="https://yoursite.com/users/#{agent.user.id}/web_requests/#{agent.id}/secret1.xml" rel="self" type="application/rss+xml"/>
  329. <atom:icon>https://yoursite.com/favicon.ico</atom:icon>
  330. <title>XKCD comics as a feed</title>
  331. <description>This is a feed of recent XKCD comics, generated by Huginn</description>
  332. <link>https://yoursite.com</link>
  333. <lastBuildDate>#{Time.now.rfc2822}</lastBuildDate>
  334. <pubDate>#{Time.now.rfc2822}</pubDate>
  335. <ttl>60</ttl>
  336. <item>
  337. <title>Evolving</title>
  338. <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>
  339. <link>http://imgs.xkcd.com/comics/evolving.png</link>
  340. <pubDate>#{event.created_at.rfc2822}</pubDate>
  341. <enclosure type="audio/mpeg" url="http://google.com/audio.mpeg" />
  342. <foo attr="attr-value-1">Foo: 1</foo>
  343. <nested key="value"><title>some title</title></nested>
  344. <simpleNested>
  345. <title>some title</title>
  346. <complex key="value">
  347. <first a="b">
  348. <second>value</second>
  349. </first>
  350. </complex>
  351. </simpleNested>
  352. <guid isPermaLink="false">#{event.id}</guid>
  353. </item>
  354. </channel>
  355. </rss>
  356. XML
  357. end
  358. end
  359. end
  360. end