java_script_agent_spec.rb 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. require 'spec_helper'
  2. describe Agents::JavaScriptAgent do
  3. before do
  4. @valid_params = {
  5. :name => "somename",
  6. :options => {
  7. :code => "Agent.check = function() { this.createEvent({ 'message': 'hi' }); };",
  8. }
  9. }
  10. @agent = Agents::JavaScriptAgent.new(@valid_params)
  11. @agent.user = users(:jane)
  12. @agent.save!
  13. end
  14. describe "validations" do
  15. it "requires 'code'" do
  16. expect(@agent).to be_valid
  17. @agent.options['code'] = ''
  18. expect(@agent).not_to be_valid
  19. @agent.options.delete('code')
  20. expect(@agent).not_to be_valid
  21. end
  22. it "accepts a credential, but it must exist" do
  23. expect(@agent).to be_valid
  24. @agent.options['code'] = 'credential:foo'
  25. expect(@agent).not_to be_valid
  26. users(:jane).user_credentials.create! :credential_name => "foo", :credential_value => "bar"
  27. expect(@agent.reload).to be_valid
  28. end
  29. end
  30. describe "#working?" do
  31. describe "when expected_update_period_in_days is set" do
  32. it "returns false when more than expected_update_period_in_days have passed since the last event creation" do
  33. @agent.options['expected_update_period_in_days'] = 1
  34. @agent.save!
  35. expect(@agent).not_to be_working
  36. @agent.check
  37. expect(@agent.reload).to be_working
  38. three_days_from_now = 3.days.from_now
  39. stub(Time).now { three_days_from_now }
  40. expect(@agent).not_to be_working
  41. end
  42. end
  43. describe "when expected_receive_period_in_days is set" do
  44. it "returns false when more than expected_receive_period_in_days have passed since the last event was received" do
  45. @agent.options['expected_receive_period_in_days'] = 1
  46. @agent.save!
  47. expect(@agent).not_to be_working
  48. Agents::JavaScriptAgent.async_receive @agent.id, [events(:bob_website_agent_event).id]
  49. expect(@agent.reload).to be_working
  50. two_days_from_now = 2.days.from_now
  51. stub(Time).now { two_days_from_now }
  52. expect(@agent.reload).not_to be_working
  53. end
  54. end
  55. end
  56. describe "executing code" do
  57. it "works by default" do
  58. @agent.options = @agent.default_options
  59. @agent.options['make_event'] = true
  60. @agent.save!
  61. expect {
  62. expect {
  63. @agent.receive([events(:bob_website_agent_event)])
  64. @agent.check
  65. }.not_to change { AgentLog.count }
  66. }.to change { Event.count }.by(2)
  67. end
  68. describe "using credentials as code" do
  69. before do
  70. @agent.user.user_credentials.create :credential_name => 'code-foo', :credential_value => 'Agent.check = function() { this.log("ran it"); };'
  71. @agent.options['code'] = 'credential:code-foo'
  72. @agent.save!
  73. end
  74. it "accepts credentials" do
  75. @agent.check
  76. expect(AgentLog.last.message).to eq("ran it")
  77. end
  78. it "logs an error when the credential goes away" do
  79. @agent.user.user_credentials.delete_all
  80. @agent.reload.check
  81. expect(AgentLog.last.message).to eq("Unable to find credential")
  82. end
  83. end
  84. describe "error handling" do
  85. it "should log an error when V8 has issues" do
  86. @agent.options['code'] = 'syntax error!'
  87. @agent.save!
  88. expect {
  89. expect {
  90. @agent.check
  91. }.not_to raise_error
  92. }.to change { AgentLog.count }.by(1)
  93. expect(AgentLog.last.message).to match(/Unexpected identifier/)
  94. expect(AgentLog.last.level).to eq(4)
  95. end
  96. it "should log an error when JavaScript throws" do
  97. @agent.options['code'] = 'Agent.check = function() { throw "oh no"; };'
  98. @agent.save!
  99. expect {
  100. expect {
  101. @agent.check
  102. }.not_to raise_error
  103. }.to change { AgentLog.count }.by(1)
  104. expect(AgentLog.last.message).to match(/oh no/)
  105. expect(AgentLog.last.level).to eq(4)
  106. end
  107. it "won't store NaNs" do
  108. @agent.options['code'] = 'Agent.check = function() { this.memory("foo", NaN); };'
  109. @agent.save!
  110. @agent.check
  111. expect(@agent.memory['foo']).to eq('NaN') # string
  112. @agent.save!
  113. expect { @agent.reload.memory }.not_to raise_error
  114. end
  115. end
  116. describe "creating events" do
  117. it "creates events with this.createEvent in the JavaScript environment" do
  118. @agent.options['code'] = 'Agent.check = function() { this.createEvent({ message: "This is an event!", stuff: { foo: 5 } }); };'
  119. @agent.save!
  120. expect {
  121. expect {
  122. @agent.check
  123. }.not_to change { AgentLog.count }
  124. }.to change { Event.count }.by(1)
  125. created_event = @agent.events.last
  126. expect(created_event.payload).to eq({ 'message' => "This is an event!", 'stuff' => { 'foo' => 5 } })
  127. end
  128. end
  129. describe "logging" do
  130. it "can output AgentLogs with this.log and this.error in the JavaScript environment" do
  131. @agent.options['code'] = 'Agent.check = function() { this.log("woah"); this.error("WOAH!"); };'
  132. @agent.save!
  133. expect {
  134. expect {
  135. @agent.check
  136. }.not_to raise_error
  137. }.to change { AgentLog.count }.by(2)
  138. log1, log2 = AgentLog.last(2)
  139. expect(log1.message).to eq("woah")
  140. expect(log1.level).to eq(3)
  141. expect(log2.message).to eq("WOAH!")
  142. expect(log2.level).to eq(4)
  143. end
  144. end
  145. describe "getting incoming events" do
  146. it "can access incoming events in the JavaScript enviroment via this.incomingEvents" do
  147. event = Event.new
  148. event.agent = agents(:bob_rain_notifier_agent)
  149. event.payload = { :data => "Something you should know about" }
  150. event.save!
  151. event.reload
  152. @agent.options['code'] = <<-JS
  153. Agent.receive = function() {
  154. var events = this.incomingEvents();
  155. for(var i = 0; i < events.length; i++) {
  156. this.createEvent({ 'message': 'I got an event!', 'event_was': events[i].payload });
  157. }
  158. }
  159. JS
  160. @agent.save!
  161. expect {
  162. expect {
  163. @agent.receive([events(:bob_website_agent_event), event])
  164. }.not_to change { AgentLog.count }
  165. }.to change { Event.count }.by(2)
  166. created_event = @agent.events.first
  167. expect(created_event.payload).to eq({ 'message' => "I got an event!", 'event_was' => { 'data' => "Something you should know about" } })
  168. end
  169. end
  170. describe "getting and setting memory, getting options" do
  171. it "can access options via this.options and work with memory via this.memory" do
  172. @agent.options['code'] = <<-JS
  173. Agent.check = function() {
  174. if (this.options('make_event')) {
  175. var callCount = this.memory('callCount') || 0;
  176. this.memory('callCount', callCount + 1);
  177. }
  178. };
  179. JS
  180. @agent.save!
  181. expect {
  182. expect {
  183. @agent.check
  184. expect(@agent.memory['callCount']).not_to be_present
  185. @agent.options['make_event'] = true
  186. @agent.check
  187. expect(@agent.memory['callCount']).to eq(1)
  188. @agent.check
  189. expect(@agent.memory['callCount']).to eq(2)
  190. @agent.memory['callCount'] = 20
  191. @agent.check
  192. expect(@agent.memory['callCount']).to eq(21)
  193. }.not_to change { AgentLog.count }
  194. }.not_to change { Event.count }
  195. end
  196. end
  197. end
  198. end