agent_spec.rb 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921
  1. require 'rails_helper'
  2. describe Agent do
  3. it_behaves_like WorkingHelpers
  4. describe ".bulk_check" do
  5. before do
  6. @weather_agent_count = Agents::WeatherAgent.where(:schedule => "midnight", :disabled => false).count
  7. end
  8. it "should run all Agents with the given schedule" do
  9. mock(Agents::WeatherAgent).async_check(anything).times(@weather_agent_count)
  10. Agents::WeatherAgent.bulk_check("midnight")
  11. end
  12. it "should skip disabled Agents" do
  13. agents(:bob_weather_agent).update_attribute :disabled, true
  14. mock(Agents::WeatherAgent).async_check(anything).times(@weather_agent_count - 1)
  15. Agents::WeatherAgent.bulk_check("midnight")
  16. end
  17. end
  18. describe ".run_schedule" do
  19. before do
  20. expect(Agents::WeatherAgent.count).to be > 0
  21. expect(Agents::WebsiteAgent.count).to be > 0
  22. end
  23. it "runs agents with the given schedule" do
  24. weather_agent_ids = [agents(:bob_weather_agent), agents(:jane_weather_agent)].map(&:id)
  25. stub(Agents::WeatherAgent).async_check(anything) {|agent_id| weather_agent_ids.delete(agent_id) }
  26. stub(Agents::WebsiteAgent).async_check(agents(:bob_website_agent).id)
  27. Agent.run_schedule("midnight")
  28. expect(weather_agent_ids).to be_empty
  29. end
  30. it "groups agents by type" do
  31. mock(Agents::WeatherAgent).bulk_check("midnight").once
  32. mock(Agents::WebsiteAgent).bulk_check("midnight").once
  33. Agent.run_schedule("midnight")
  34. end
  35. it "only runs agents with the given schedule" do
  36. do_not_allow(Agents::WebsiteAgent).async_check
  37. Agent.run_schedule("blah")
  38. end
  39. it "will not run the 'never' schedule" do
  40. agents(:bob_weather_agent).update_attribute 'schedule', 'never'
  41. do_not_allow(Agents::WebsiteAgent).async_check
  42. Agent.run_schedule("never")
  43. end
  44. end
  45. describe "credential" do
  46. it "should return the value of the credential when credential is present" do
  47. expect(agents(:bob_weather_agent).credential("aws_secret")).to eq(user_credentials(:bob_aws_secret).credential_value)
  48. end
  49. it "should return nil when credential is not present" do
  50. expect(agents(:bob_weather_agent).credential("non_existing_credential")).to eq(nil)
  51. end
  52. it "should memoize the load" do
  53. mock.any_instance_of(UserCredential).credential_value.twice { "foo" }
  54. expect(agents(:bob_weather_agent).credential("aws_secret")).to eq("foo")
  55. expect(agents(:bob_weather_agent).credential("aws_secret")).to eq("foo")
  56. agents(:bob_weather_agent).reload
  57. expect(agents(:bob_weather_agent).credential("aws_secret")).to eq("foo")
  58. expect(agents(:bob_weather_agent).credential("aws_secret")).to eq("foo")
  59. end
  60. end
  61. describe "changes to type" do
  62. it "validates types" do
  63. source = Agent.new
  64. source.type = "Agents::WeatherAgent"
  65. expect(source).to have(0).errors_on(:type)
  66. source.type = "Agents::WebsiteAgent"
  67. expect(source).to have(0).errors_on(:type)
  68. source.type = "Agents::Fake"
  69. expect(source).to have(1).error_on(:type)
  70. end
  71. it "disallows changes to type once a record has been saved" do
  72. source = agents(:bob_website_agent)
  73. source.type = "Agents::WeatherAgent"
  74. expect(source).to have(1).error_on(:type)
  75. end
  76. it "should know about available types" do
  77. expect(Agent.types).to include(Agents::WeatherAgent, Agents::WebsiteAgent)
  78. end
  79. end
  80. describe "with an example Agent" do
  81. class Agents::SomethingSource < Agent
  82. default_schedule "2pm"
  83. def check
  84. create_event :payload => {}
  85. end
  86. def validate_options
  87. errors.add(:base, "bad is bad") if options[:bad]
  88. end
  89. end
  90. class Agents::CannotBeScheduled < Agent
  91. cannot_be_scheduled!
  92. def receive(events)
  93. events.each do |event|
  94. create_event :payload => { :events_received => 1 }
  95. end
  96. end
  97. end
  98. before do
  99. stub(Agents::SomethingSource).valid_type?("Agents::SomethingSource") { true }
  100. stub(Agents::CannotBeScheduled).valid_type?("Agents::CannotBeScheduled") { true }
  101. end
  102. describe Agents::SomethingSource do
  103. let(:new_instance) do
  104. agent = Agents::SomethingSource.new(:name => "some agent")
  105. agent.user = users(:bob)
  106. agent
  107. end
  108. it_behaves_like LiquidInterpolatable
  109. it_behaves_like HasGuid
  110. end
  111. describe ".short_type" do
  112. it "returns a short name without 'Agents::'" do
  113. expect(Agents::SomethingSource.new.short_type).to eq("SomethingSource")
  114. expect(Agents::CannotBeScheduled.new.short_type).to eq("CannotBeScheduled")
  115. end
  116. end
  117. describe ".default_schedule" do
  118. it "stores the default on the class" do
  119. expect(Agents::SomethingSource.default_schedule).to eq("2pm")
  120. expect(Agents::SomethingSource.new.default_schedule).to eq("2pm")
  121. end
  122. it "sets the default on new instances, allows setting new schedules, and prevents invalid schedules" do
  123. @checker = Agents::SomethingSource.new(:name => "something")
  124. @checker.user = users(:bob)
  125. expect(@checker.schedule).to eq("2pm")
  126. @checker.save!
  127. expect(@checker.reload.schedule).to eq("2pm")
  128. @checker.update_attribute :schedule, "5pm"
  129. expect(@checker.reload.schedule).to eq("5pm")
  130. expect(@checker.reload.schedule).to eq("5pm")
  131. @checker.schedule = "this_is_not_real"
  132. expect(@checker).to have(1).errors_on(:schedule)
  133. end
  134. it "should have an empty schedule if it cannot_be_scheduled" do
  135. @checker = Agents::CannotBeScheduled.new(:name => "something")
  136. @checker.user = users(:bob)
  137. expect(@checker.schedule).to be_nil
  138. expect(@checker).to be_valid
  139. @checker.schedule = "5pm"
  140. @checker.save!
  141. expect(@checker.schedule).to be_nil
  142. @checker.schedule = "5pm"
  143. expect(@checker).to have(0).errors_on(:schedule)
  144. expect(@checker.schedule).to be_nil
  145. end
  146. end
  147. describe "#create_event" do
  148. before do
  149. @checker = Agents::SomethingSource.new(:name => "something")
  150. @checker.user = users(:bob)
  151. @checker.save!
  152. end
  153. it "should use the checker's user" do
  154. @checker.check
  155. expect(Event.last.user).to eq(@checker.user)
  156. end
  157. it "should log an error if the Agent has been marked with 'cannot_create_events!'" do
  158. mock(@checker).can_create_events? { false }
  159. expect {
  160. @checker.check
  161. }.not_to change { Event.count }
  162. expect(@checker.logs.first.message).to match(/cannot create events/i)
  163. end
  164. end
  165. describe ".async_check" do
  166. before do
  167. @checker = Agents::SomethingSource.new(:name => "something")
  168. @checker.user = users(:bob)
  169. @checker.save!
  170. end
  171. it "records last_check_at and calls check on the given Agent" do
  172. mock(@checker).check.once {
  173. @checker.options[:new] = true
  174. }
  175. mock(Agent).find(@checker.id) { @checker }
  176. expect(@checker.last_check_at).to be_nil
  177. Agents::SomethingSource.async_check(@checker.id)
  178. expect(@checker.reload.last_check_at).to be_within(2).of(Time.now)
  179. expect(@checker.reload.options[:new]).to be_truthy # Show that we save options
  180. end
  181. it "should log exceptions" do
  182. mock(@checker).check.once {
  183. raise "foo"
  184. }
  185. mock(Agent).find(@checker.id) { @checker }
  186. expect {
  187. Agents::SomethingSource.async_check(@checker.id)
  188. }.to raise_error(RuntimeError)
  189. log = @checker.logs.first
  190. expect(log.message).to match(/Exception/)
  191. expect(log.level).to eq(4)
  192. end
  193. it "should not run disabled Agents" do
  194. mock(Agent).find(agents(:bob_weather_agent).id) { agents(:bob_weather_agent) }
  195. do_not_allow(agents(:bob_weather_agent)).check
  196. agents(:bob_weather_agent).update_attribute :disabled, true
  197. Agent.async_check(agents(:bob_weather_agent).id)
  198. end
  199. end
  200. describe ".receive!" do
  201. before do
  202. stub_request(:any, /wunderground/).to_return(:body => File.read(Rails.root.join("spec/data_fixtures/weather.json")), :status => 200)
  203. stub.any_instance_of(Agents::WeatherAgent).is_tomorrow?(anything) { true }
  204. end
  205. it "should use available events" do
  206. Agent.async_check(agents(:bob_weather_agent).id)
  207. mock(Agent).async_receive(agents(:bob_rain_notifier_agent).id, anything).times(1)
  208. Agent.receive!
  209. end
  210. it "should not propogate to disabled Agents" do
  211. Agent.async_check(agents(:bob_weather_agent).id)
  212. agents(:bob_rain_notifier_agent).update_attribute :disabled, true
  213. mock(Agent).async_receive(agents(:bob_rain_notifier_agent).id, anything).times(0)
  214. Agent.receive!
  215. end
  216. it "should log exceptions" do
  217. mock.any_instance_of(Agents::TriggerAgent).receive(anything).once {
  218. raise "foo"
  219. }
  220. Agent.async_check(agents(:bob_weather_agent).id)
  221. expect {
  222. Agent.async_receive(agents(:bob_rain_notifier_agent).id, [agents(:bob_weather_agent).events.last.id])
  223. }.to raise_error(RuntimeError)
  224. log = agents(:bob_rain_notifier_agent).logs.first
  225. expect(log.message).to match(/Exception/)
  226. expect(log.level).to eq(4)
  227. end
  228. it "should track when events have been seen and not received them again" do
  229. mock.any_instance_of(Agents::TriggerAgent).receive(anything).once
  230. Agent.async_check(agents(:bob_weather_agent).id)
  231. expect {
  232. Agent.receive!
  233. }.to change { agents(:bob_rain_notifier_agent).reload.last_checked_event_id }
  234. expect {
  235. Agent.receive!
  236. }.not_to change { agents(:bob_rain_notifier_agent).reload.last_checked_event_id }
  237. end
  238. it "should not run consumers that have nothing to do" do
  239. do_not_allow.any_instance_of(Agents::TriggerAgent).receive(anything)
  240. Agent.receive!
  241. end
  242. it "should group events" do
  243. mock.any_instance_of(Agents::TriggerAgent).receive(anything).twice { |events|
  244. expect(events.map(&:user).map(&:username).uniq.length).to eq(1)
  245. }
  246. Agent.async_check(agents(:bob_weather_agent).id)
  247. Agent.async_check(agents(:jane_weather_agent).id)
  248. Agent.receive!
  249. end
  250. it "should call receive for each event when no_bulk_receive! is used" do
  251. mock.any_instance_of(Agents::TriggerAgent).receive(anything).twice
  252. stub(Agents::TriggerAgent).no_bulk_receive? { true }
  253. Agent.async_check(agents(:bob_weather_agent).id)
  254. Agent.async_check(agents(:bob_weather_agent).id)
  255. Agent.receive!
  256. end
  257. it "should ignore events that were created before a particular Link" do
  258. agent2 = Agents::SomethingSource.new(:name => "something")
  259. agent2.user = users(:bob)
  260. agent2.save!
  261. agent2.check
  262. mock.any_instance_of(Agents::TriggerAgent).receive(anything).twice
  263. agents(:bob_weather_agent).check # bob_weather_agent makes an event
  264. expect {
  265. Agent.receive! # event gets propagated
  266. }.to change { agents(:bob_rain_notifier_agent).reload.last_checked_event_id }
  267. # This agent creates a few events before we link to it, but after our last check.
  268. agent2.check
  269. agent2.check
  270. # Now we link to it.
  271. agents(:bob_rain_notifier_agent).sources << agent2
  272. expect(agent2.links_as_source.first.event_id_at_creation).to eq(agent2.events.reorder("events.id desc").first.id)
  273. expect {
  274. Agent.receive! # but we don't receive those events because they're too old
  275. }.not_to change { agents(:bob_rain_notifier_agent).reload.last_checked_event_id }
  276. # Now a new event is created by agent2
  277. agent2.check
  278. expect {
  279. Agent.receive! # and we receive it
  280. }.to change { agents(:bob_rain_notifier_agent).reload.last_checked_event_id }
  281. end
  282. end
  283. describe ".async_receive" do
  284. it "should not run disabled Agents" do
  285. mock(Agent).find(agents(:bob_rain_notifier_agent).id) { agents(:bob_rain_notifier_agent) }
  286. do_not_allow(agents(:bob_rain_notifier_agent)).receive
  287. agents(:bob_rain_notifier_agent).update_attribute :disabled, true
  288. Agent.async_receive(agents(:bob_rain_notifier_agent).id, [1, 2, 3])
  289. end
  290. end
  291. describe "creating a new agent and then calling .receive!" do
  292. it "should not backfill events for a newly created agent" do
  293. Event.delete_all
  294. sender = Agents::SomethingSource.new(:name => "Sending Agent")
  295. sender.user = users(:bob)
  296. sender.save!
  297. sender.create_event :payload => {}
  298. sender.create_event :payload => {}
  299. expect(sender.events.count).to eq(2)
  300. receiver = Agents::CannotBeScheduled.new(:name => "Receiving Agent")
  301. receiver.user = users(:bob)
  302. receiver.sources << sender
  303. receiver.save!
  304. expect(receiver.events.count).to eq(0)
  305. Agent.receive!
  306. expect(receiver.events.count).to eq(0)
  307. sender.create_event :payload => {}
  308. Agent.receive!
  309. expect(receiver.events.count).to eq(1)
  310. end
  311. end
  312. describe "creating agents with propagate_immediately = true" do
  313. it "should schedule subagent events immediately" do
  314. Event.delete_all
  315. sender = Agents::SomethingSource.new(:name => "Sending Agent")
  316. sender.user = users(:bob)
  317. sender.save!
  318. receiver = Agents::CannotBeScheduled.new(
  319. :name => "Receiving Agent",
  320. )
  321. receiver.propagate_immediately = true
  322. receiver.user = users(:bob)
  323. receiver.sources << sender
  324. receiver.save!
  325. sender.create_event :payload => {"message" => "new payload"}
  326. expect(sender.events.count).to eq(1)
  327. expect(receiver.events.count).to eq(1)
  328. #should be true without calling Agent.receive!
  329. end
  330. it "should only schedule receiving agents that are set to propagate_immediately" do
  331. Event.delete_all
  332. sender = Agents::SomethingSource.new(:name => "Sending Agent")
  333. sender.user = users(:bob)
  334. sender.save!
  335. im_receiver = Agents::CannotBeScheduled.new(
  336. :name => "Immediate Receiving Agent",
  337. )
  338. im_receiver.propagate_immediately = true
  339. im_receiver.user = users(:bob)
  340. im_receiver.sources << sender
  341. im_receiver.save!
  342. slow_receiver = Agents::CannotBeScheduled.new(
  343. :name => "Slow Receiving Agent",
  344. )
  345. slow_receiver.user = users(:bob)
  346. slow_receiver.sources << sender
  347. slow_receiver.save!
  348. sender.create_event :payload => {"message" => "new payload"}
  349. expect(sender.events.count).to eq(1)
  350. expect(im_receiver.events.count).to eq(1)
  351. #we should get the quick one
  352. #but not the slow one
  353. expect(slow_receiver.events.count).to eq(0)
  354. Agent.receive!
  355. #now we should have one in both
  356. expect(im_receiver.events.count).to eq(1)
  357. expect(slow_receiver.events.count).to eq(1)
  358. end
  359. end
  360. describe "validations" do
  361. it "calls validate_options" do
  362. agent = Agents::SomethingSource.new(:name => "something")
  363. agent.user = users(:bob)
  364. agent.options[:bad] = true
  365. expect(agent).to have(1).error_on(:base)
  366. agent.options[:bad] = false
  367. expect(agent).to have(0).errors_on(:base)
  368. end
  369. it "makes options symbol-indifferent before validating" do
  370. agent = Agents::SomethingSource.new(:name => "something")
  371. agent.user = users(:bob)
  372. agent.options["bad"] = true
  373. expect(agent).to have(1).error_on(:base)
  374. agent.options["bad"] = false
  375. expect(agent).to have(0).errors_on(:base)
  376. end
  377. it "makes memory symbol-indifferent before validating" do
  378. agent = Agents::SomethingSource.new(:name => "something")
  379. agent.user = users(:bob)
  380. agent.memory["bad"] = 2
  381. agent.save
  382. expect(agent.memory[:bad]).to eq(2)
  383. end
  384. it "should work when assigned a hash or JSON string" do
  385. agent = Agents::SomethingSource.new(:name => "something")
  386. agent.memory = {}
  387. expect(agent.memory).to eq({})
  388. expect(agent.memory["foo"]).to be_nil
  389. agent.memory = ""
  390. expect(agent.memory["foo"]).to be_nil
  391. expect(agent.memory).to eq({})
  392. agent.memory = '{"hi": "there"}'
  393. expect(agent.memory).to eq({ "hi" => "there" })
  394. agent.memory = '{invalid}'
  395. expect(agent.memory).to eq({ "hi" => "there" })
  396. expect(agent).to have(1).errors_on(:memory)
  397. agent.memory = "{}"
  398. expect(agent.memory["foo"]).to be_nil
  399. expect(agent.memory).to eq({})
  400. expect(agent).to have(0).errors_on(:memory)
  401. agent.options = "{}"
  402. expect(agent.options["foo"]).to be_nil
  403. expect(agent.options).to eq({})
  404. expect(agent).to have(0).errors_on(:options)
  405. agent.options = '{"hi": 2}'
  406. expect(agent.options["hi"]).to eq(2)
  407. expect(agent).to have(0).errors_on(:options)
  408. agent.options = '{"hi": wut}'
  409. expect(agent.options["hi"]).to eq(2)
  410. expect(agent).to have(1).errors_on(:options)
  411. expect(agent.errors_on(:options)).to include("was assigned invalid JSON")
  412. agent.options = 5
  413. expect(agent.options["hi"]).to eq(2)
  414. expect(agent).to have(1).errors_on(:options)
  415. expect(agent.errors_on(:options)).to include("cannot be set to an instance of Fixnum")
  416. end
  417. it "should not allow source agents owned by other people" do
  418. agent = Agents::SomethingSource.new(:name => "something")
  419. agent.user = users(:bob)
  420. agent.source_ids = [agents(:bob_weather_agent).id]
  421. expect(agent).to have(0).errors_on(:sources)
  422. agent.source_ids = [agents(:jane_weather_agent).id]
  423. expect(agent).to have(1).errors_on(:sources)
  424. agent.user = users(:jane)
  425. expect(agent).to have(0).errors_on(:sources)
  426. end
  427. it "should not allow controller agents owned by other people" do
  428. agent = Agents::SomethingSource.new(:name => "something")
  429. agent.user = users(:bob)
  430. agent.controller_ids = [agents(:bob_weather_agent).id]
  431. expect(agent).to have(0).errors_on(:controllers)
  432. agent.controller_ids = [agents(:jane_weather_agent).id]
  433. expect(agent).to have(1).errors_on(:controllers)
  434. agent.user = users(:jane)
  435. expect(agent).to have(0).errors_on(:controllers)
  436. end
  437. it "should not allow control target agents owned by other people" do
  438. agent = Agents::CannotBeScheduled.new(:name => "something")
  439. agent.user = users(:bob)
  440. agent.control_target_ids = [agents(:bob_weather_agent).id]
  441. expect(agent).to have(0).errors_on(:control_targets)
  442. agent.control_target_ids = [agents(:jane_weather_agent).id]
  443. expect(agent).to have(1).errors_on(:control_targets)
  444. agent.user = users(:jane)
  445. expect(agent).to have(0).errors_on(:control_targets)
  446. end
  447. it "should not allow scenarios owned by other people" do
  448. agent = Agents::SomethingSource.new(:name => "something")
  449. agent.user = users(:bob)
  450. agent.scenario_ids = [scenarios(:bob_weather).id]
  451. expect(agent).to have(0).errors_on(:scenarios)
  452. agent.scenario_ids = [scenarios(:bob_weather).id, scenarios(:jane_weather).id]
  453. expect(agent).to have(1).errors_on(:scenarios)
  454. agent.scenario_ids = [scenarios(:jane_weather).id]
  455. expect(agent).to have(1).errors_on(:scenarios)
  456. agent.user = users(:jane)
  457. expect(agent).to have(0).errors_on(:scenarios)
  458. end
  459. it "validates keep_events_for" do
  460. agent = Agents::SomethingSource.new(:name => "something")
  461. agent.user = users(:bob)
  462. expect(agent).to be_valid
  463. agent.keep_events_for = nil
  464. expect(agent).to have(1).errors_on(:keep_events_for)
  465. agent.keep_events_for = 1000
  466. expect(agent).to have(1).errors_on(:keep_events_for)
  467. agent.keep_events_for = ""
  468. expect(agent).to have(1).errors_on(:keep_events_for)
  469. agent.keep_events_for = 5.days.to_i
  470. expect(agent).to be_valid
  471. agent.keep_events_for = 0
  472. expect(agent).to be_valid
  473. agent.keep_events_for = 365.days.to_i
  474. expect(agent).to be_valid
  475. # Rails seems to call to_i on the input. This guards against future changes to that behavior.
  476. agent.keep_events_for = "drop table;"
  477. expect(agent.keep_events_for).to eq(0)
  478. end
  479. end
  480. describe "cleaning up now-expired events" do
  481. before do
  482. @time = "2014-01-01 01:00:00 +00:00"
  483. time_travel_to @time do
  484. @agent = Agents::SomethingSource.new(:name => "something")
  485. @agent.keep_events_for = 5.days
  486. @agent.user = users(:bob)
  487. @agent.save!
  488. @event = @agent.create_event :payload => { "hello" => "world" }
  489. expect(@event.expires_at.to_i).to be_within(2).of(5.days.from_now.to_i)
  490. end
  491. end
  492. describe "when keep_events_for has not changed" do
  493. it "does nothing" do
  494. mock(@agent).update_event_expirations!.times(0)
  495. @agent.options[:foo] = "bar1"
  496. @agent.save!
  497. @agent.options[:foo] = "bar1"
  498. @agent.keep_events_for = 5.days
  499. @agent.save!
  500. end
  501. end
  502. describe "when keep_events_for is changed" do
  503. it "updates events' expires_at" do
  504. time_travel_to @time do
  505. expect {
  506. @agent.options[:foo] = "bar1"
  507. @agent.keep_events_for = 3.days
  508. @agent.save!
  509. }.to change { @event.reload.expires_at }
  510. expect(@event.expires_at.to_i).to be_within(2).of(3.days.from_now.to_i)
  511. end
  512. end
  513. it "updates events relative to their created_at" do
  514. @event.update_attribute :created_at, 2.days.ago
  515. expect(@event.reload.created_at.to_i).to be_within(2).of(2.days.ago.to_i)
  516. expect {
  517. @agent.options[:foo] = "bar2"
  518. @agent.keep_events_for = 3.days
  519. @agent.save!
  520. }.to change { @event.reload.expires_at }
  521. expect(@event.expires_at.to_i).to be_within(60 * 61).of(1.days.from_now.to_i) # The larger time is to deal with daylight savings
  522. end
  523. it "nulls out expires_at when keep_events_for is set to 0" do
  524. expect {
  525. @agent.options[:foo] = "bar"
  526. @agent.keep_events_for = 0
  527. @agent.save!
  528. }.to change { @event.reload.expires_at }.to(nil)
  529. end
  530. end
  531. end
  532. describe "Agent.build_clone" do
  533. before do
  534. Event.delete_all
  535. @sender = Agents::SomethingSource.new(
  536. name: 'Agent (2)',
  537. options: { foo: 'bar2' },
  538. schedule: '5pm')
  539. @sender.user = users(:bob)
  540. @sender.save!
  541. @sender.create_event :payload => {}
  542. @sender.create_event :payload => {}
  543. expect(@sender.events.count).to eq(2)
  544. @receiver = Agents::CannotBeScheduled.new(
  545. name: 'Agent',
  546. options: { foo: 'bar3' },
  547. keep_events_for: 3.days,
  548. propagate_immediately: true)
  549. @receiver.user = users(:bob)
  550. @receiver.sources << @sender
  551. @receiver.memory[:test] = 1
  552. @receiver.save!
  553. end
  554. it "should create a clone of a given agent for editing" do
  555. sender_clone = users(:bob).agents.build_clone(@sender)
  556. expect(sender_clone.attributes).to eq(Agent.new.attributes.
  557. update(@sender.slice(:user_id, :type,
  558. :options, :schedule, :keep_events_for, :propagate_immediately)).
  559. update('name' => 'Agent (2) (2)', 'options' => { 'foo' => 'bar2' }))
  560. expect(sender_clone.source_ids).to eq([])
  561. receiver_clone = users(:bob).agents.build_clone(@receiver)
  562. expect(receiver_clone.attributes).to eq(Agent.new.attributes.
  563. update(@receiver.slice(:user_id, :type,
  564. :options, :schedule, :keep_events_for, :propagate_immediately)).
  565. update('name' => 'Agent (3)', 'options' => { 'foo' => 'bar3' }))
  566. expect(receiver_clone.source_ids).to eq([@sender.id])
  567. end
  568. end
  569. end
  570. describe ".trigger_web_request" do
  571. class Agents::WebRequestReceiver < Agent
  572. cannot_be_scheduled!
  573. end
  574. before do
  575. stub(Agents::WebRequestReceiver).valid_type?("Agents::WebRequestReceiver") { true }
  576. end
  577. context "when .receive_web_request is defined" do
  578. before do
  579. @agent = Agents::WebRequestReceiver.new(:name => "something")
  580. @agent.user = users(:bob)
  581. @agent.save!
  582. def @agent.receive_web_request(params, method, format)
  583. memory['last_request'] = [params, method, format]
  584. ['Ok!', 200]
  585. end
  586. end
  587. it "calls the .receive_web_request hook, updates last_web_request_at, and saves" do
  588. @agent.trigger_web_request({ :some_param => "some_value" }, "post", "text/html")
  589. expect(@agent.reload.memory['last_request']).to eq([ { "some_param" => "some_value" }, "post", "text/html" ])
  590. expect(@agent.last_web_request_at.to_i).to be_within(1).of(Time.now.to_i)
  591. end
  592. end
  593. context "when .receive_webhook is defined" do
  594. before do
  595. @agent = Agents::WebRequestReceiver.new(:name => "something")
  596. @agent.user = users(:bob)
  597. @agent.save!
  598. def @agent.receive_webhook(params)
  599. memory['last_webhook_request'] = params
  600. ['Ok!', 200]
  601. end
  602. end
  603. it "outputs a deprecation warning and calls .receive_webhook with the params" do
  604. mock(Rails.logger).warn("DEPRECATED: The .receive_webhook method is deprecated, please switch your Agent to use .receive_web_request.")
  605. @agent.trigger_web_request({ :some_param => "some_value" }, "post", "text/html")
  606. expect(@agent.reload.memory['last_webhook_request']).to eq({ "some_param" => "some_value" })
  607. expect(@agent.last_web_request_at.to_i).to be_within(1).of(Time.now.to_i)
  608. end
  609. end
  610. end
  611. describe "scopes" do
  612. describe "of_type" do
  613. it "should accept classes" do
  614. agents = Agent.of_type(Agents::WebsiteAgent)
  615. expect(agents).to include(agents(:bob_website_agent))
  616. expect(agents).to include(agents(:jane_website_agent))
  617. expect(agents).not_to include(agents(:bob_weather_agent))
  618. end
  619. it "should accept strings" do
  620. agents = Agent.of_type("Agents::WebsiteAgent")
  621. expect(agents).to include(agents(:bob_website_agent))
  622. expect(agents).to include(agents(:jane_website_agent))
  623. expect(agents).not_to include(agents(:bob_weather_agent))
  624. end
  625. it "should accept instances of an Agent" do
  626. agents = Agent.of_type(agents(:bob_website_agent))
  627. expect(agents).to include(agents(:bob_website_agent))
  628. expect(agents).to include(agents(:jane_website_agent))
  629. expect(agents).not_to include(agents(:bob_weather_agent))
  630. end
  631. end
  632. end
  633. describe "#create_event" do
  634. describe "when the agent has keep_events_for set" do
  635. before do
  636. expect(agents(:jane_weather_agent).keep_events_for).to be > 0
  637. end
  638. it "sets expires_at on created events" do
  639. event = agents(:jane_weather_agent).create_event :payload => { 'hi' => 'there' }
  640. expect(event.expires_at.to_i).to be_within(5).of(agents(:jane_weather_agent).keep_events_for.seconds.from_now.to_i)
  641. end
  642. end
  643. describe "when the agent does not have keep_events_for set" do
  644. before do
  645. expect(agents(:jane_website_agent).keep_events_for).to eq(0)
  646. end
  647. it "does not set expires_at on created events" do
  648. event = agents(:jane_website_agent).create_event :payload => { 'hi' => 'there' }
  649. expect(event.expires_at).to be_nil
  650. end
  651. end
  652. end
  653. describe '.last_checked_event_id' do
  654. it "should be updated by setting drop_pending_events to true" do
  655. agent = agents(:bob_rain_notifier_agent)
  656. agent.last_checked_event_id = nil
  657. agent.save!
  658. agent.update!(drop_pending_events: true)
  659. expect(agent.reload.last_checked_event_id).to eq(Event.maximum(:id))
  660. end
  661. it "should not affect a virtual attribute drop_pending_events" do
  662. agent = agents(:bob_rain_notifier_agent)
  663. agent.update!(drop_pending_events: true)
  664. expect(agent.reload.drop_pending_events).to eq(false)
  665. end
  666. end
  667. describe ".drop_pending_events" do
  668. before do
  669. stub_request(:any, /wunderground/).to_return(body: File.read(Rails.root.join("spec/data_fixtures/weather.json")), status: 200)
  670. stub.any_instance_of(Agents::WeatherAgent).is_tomorrow?(anything) { true }
  671. end
  672. it "should drop pending events while the agent was disabled when set to true" do
  673. agent1 = agents(:bob_weather_agent)
  674. agent2 = agents(:bob_rain_notifier_agent)
  675. expect {
  676. expect {
  677. Agent.async_check(agent1.id)
  678. Agent.receive!
  679. }.to change { agent1.events.count }.by(1)
  680. }.to change { agent2.events.count }.by(1)
  681. agent2.disabled = true
  682. agent2.save!
  683. expect {
  684. expect {
  685. Agent.async_check(agent1.id)
  686. Agent.receive!
  687. }.to change { agent1.events.count }.by(1)
  688. }.not_to change { agent2.events.count }
  689. agent2.disabled = false
  690. agent2.drop_pending_events = true
  691. agent2.save!
  692. expect {
  693. Agent.receive!
  694. }.not_to change { agent2.events.count }
  695. end
  696. end
  697. end
  698. describe AgentDrop do
  699. def interpolate(string, agent)
  700. agent.interpolate_string(string, "agent" => agent)
  701. end
  702. before do
  703. @wsa1 = Agents::WebsiteAgent.new(
  704. name: 'XKCD',
  705. options: {
  706. expected_update_period_in_days: 2,
  707. type: 'html',
  708. url: 'http://xkcd.com/',
  709. mode: 'on_change',
  710. extract: {
  711. url: { css: '#comic img', value: '@src' },
  712. title: { css: '#comic img', value: '@alt' },
  713. },
  714. },
  715. schedule: 'every_1h',
  716. keep_events_for: 2.days)
  717. @wsa1.user = users(:bob)
  718. @wsa1.save!
  719. @wsa2 = Agents::WebsiteAgent.new(
  720. name: 'Dilbert',
  721. options: {
  722. expected_update_period_in_days: 2,
  723. type: 'html',
  724. url: 'http://dilbert.com/',
  725. mode: 'on_change',
  726. extract: {
  727. url: { css: '[id^=strip_enlarged_] img', value: '@src' },
  728. title: { css: '.STR_DateStrip', value: './/text()' },
  729. },
  730. },
  731. schedule: 'every_12h',
  732. keep_events_for: 2.days)
  733. @wsa2.user = users(:bob)
  734. @wsa2.save!
  735. @efa = Agents::EventFormattingAgent.new(
  736. name: 'Formatter',
  737. options: {
  738. instructions: {
  739. message: '{{agent.name}}: {{title}} {{url}}',
  740. agent: '{{agent.type}}',
  741. },
  742. mode: 'clean',
  743. matchers: [],
  744. skip_created_at: 'false',
  745. },
  746. keep_events_for: 2.days,
  747. propagate_immediately: true)
  748. @efa.user = users(:bob)
  749. @efa.sources << @wsa1 << @wsa2
  750. @efa.memory[:test] = 1
  751. @efa.save!
  752. end
  753. it 'should be created via Agent#to_liquid' do
  754. expect(@wsa1.to_liquid.class).to be(AgentDrop)
  755. expect(@wsa2.to_liquid.class).to be(AgentDrop)
  756. expect(@efa.to_liquid.class).to be(AgentDrop)
  757. end
  758. it 'should have .type and .name' do
  759. t = '{{agent.type}}: {{agent.name}}'
  760. expect(interpolate(t, @wsa1)).to eq('WebsiteAgent: XKCD')
  761. expect(interpolate(t, @wsa2)).to eq('WebsiteAgent: Dilbert')
  762. expect(interpolate(t, @efa)).to eq('EventFormattingAgent: Formatter')
  763. end
  764. it 'should have .options' do
  765. t = '{{agent.options.url}}'
  766. expect(interpolate(t, @wsa1)).to eq('http://xkcd.com/')
  767. expect(interpolate(t, @wsa2)).to eq('http://dilbert.com/')
  768. expect(interpolate('{{agent.options.instructions.message}}',
  769. @efa)).to eq('{{agent.name}}: {{title}} {{url}}')
  770. end
  771. it 'should have .sources' do
  772. t = '{{agent.sources.size}}: {{agent.sources | map:"name" | join:", "}}'
  773. expect(interpolate(t, @wsa1)).to eq('0: ')
  774. expect(interpolate(t, @wsa2)).to eq('0: ')
  775. expect(interpolate(t, @efa)).to eq('2: XKCD, Dilbert')
  776. end
  777. it 'should have .receivers' do
  778. t = '{{agent.receivers.size}}: {{agent.receivers | map:"name" | join:", "}}'
  779. expect(interpolate(t, @wsa1)).to eq('1: Formatter')
  780. expect(interpolate(t, @wsa2)).to eq('1: Formatter')
  781. expect(interpolate(t, @efa)).to eq('0: ')
  782. end
  783. end