java_script_agent_spec.rb 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. require 'rails_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 "checks for a valid 'language', but allows nil" do
  23. expect(@agent).to be_valid
  24. @agent.options['language'] = ''
  25. expect(@agent).to be_valid
  26. @agent.options.delete('language')
  27. expect(@agent).to be_valid
  28. @agent.options['language'] = 'foo'
  29. expect(@agent).not_to be_valid
  30. %w[javascript JavaScript coffeescript CoffeeScript].each do |valid_language|
  31. @agent.options['language'] = valid_language
  32. expect(@agent).to be_valid
  33. end
  34. end
  35. it "accepts a credential, but it must exist" do
  36. expect(@agent).to be_valid
  37. @agent.options['code'] = 'credential:foo'
  38. expect(@agent).not_to be_valid
  39. users(:jane).user_credentials.create! :credential_name => "foo", :credential_value => "bar"
  40. expect(@agent.reload).to be_valid
  41. end
  42. end
  43. describe "#working?" do
  44. describe "when expected_update_period_in_days is set" do
  45. it "returns false when more than expected_update_period_in_days have passed since the last event creation" do
  46. @agent.options['expected_update_period_in_days'] = 1
  47. @agent.save!
  48. expect(@agent).not_to be_working
  49. @agent.check
  50. expect(@agent.reload).to be_working
  51. three_days_from_now = 3.days.from_now
  52. stub(Time).now { three_days_from_now }
  53. expect(@agent).not_to be_working
  54. end
  55. end
  56. describe "when expected_receive_period_in_days is set" do
  57. it "returns false when more than expected_receive_period_in_days have passed since the last event was received" do
  58. @agent.options['expected_receive_period_in_days'] = 1
  59. @agent.save!
  60. expect(@agent).not_to be_working
  61. Agents::JavaScriptAgent.async_receive @agent.id, [events(:bob_website_agent_event).id]
  62. expect(@agent.reload).to be_working
  63. two_days_from_now = 2.days.from_now
  64. stub(Time).now { two_days_from_now }
  65. expect(@agent.reload).not_to be_working
  66. end
  67. end
  68. end
  69. describe "executing code" do
  70. it "works by default" do
  71. @agent.options = @agent.default_options
  72. @agent.options['make_event'] = true
  73. @agent.save!
  74. expect {
  75. expect {
  76. @agent.receive([events(:bob_website_agent_event)])
  77. @agent.check
  78. }.not_to change { AgentLog.count }
  79. }.to change { Event.count }.by(2)
  80. end
  81. describe "using credentials as code" do
  82. before do
  83. @agent.user.user_credentials.create :credential_name => 'code-foo', :credential_value => 'Agent.check = function() { this.log("ran it"); };'
  84. @agent.options['code'] = "credential:code-foo\n\n"
  85. @agent.save!
  86. end
  87. it "accepts credentials" do
  88. @agent.check
  89. expect(AgentLog.last.message).to eq("ran it")
  90. end
  91. it "logs an error when the credential goes away" do
  92. @agent.user.user_credentials.delete_all
  93. @agent.reload.check
  94. expect(AgentLog.last.message).to eq("Unable to find credential")
  95. end
  96. end
  97. describe "error handling" do
  98. it "should log an error when V8 has issues" do
  99. @agent.options['code'] = 'syntax error!'
  100. @agent.save!
  101. expect {
  102. expect {
  103. @agent.check
  104. }.not_to raise_error
  105. }.to change { AgentLog.count }.by(1)
  106. expect(AgentLog.last.message).to match(/Unexpected identifier/)
  107. expect(AgentLog.last.level).to eq(4)
  108. end
  109. it "should log an error when JavaScript throws" do
  110. @agent.options['code'] = 'Agent.check = function() { throw "oh no"; };'
  111. @agent.save!
  112. expect {
  113. expect {
  114. @agent.check
  115. }.not_to raise_error
  116. }.to change { AgentLog.count }.by(1)
  117. expect(AgentLog.last.message).to match(/oh no/)
  118. expect(AgentLog.last.level).to eq(4)
  119. end
  120. it "won't store NaNs" do
  121. @agent.options['code'] = 'Agent.check = function() { this.memory("foo", NaN); };'
  122. @agent.save!
  123. @agent.check
  124. expect(@agent.memory['foo']).to eq('NaN') # string
  125. @agent.save!
  126. expect { @agent.reload.memory }.not_to raise_error
  127. end
  128. end
  129. describe "creating events" do
  130. it "creates events with this.createEvent in the JavaScript environment" do
  131. @agent.options['code'] = 'Agent.check = function() { this.createEvent({ message: "This is an event!", stuff: { foo: 5 } }); };'
  132. @agent.save!
  133. expect {
  134. expect {
  135. @agent.check
  136. }.not_to change { AgentLog.count }
  137. }.to change { Event.count }.by(1)
  138. created_event = @agent.events.last
  139. expect(created_event.payload).to eq({ 'message' => "This is an event!", 'stuff' => { 'foo' => 5 } })
  140. end
  141. end
  142. describe "logging" do
  143. it "can output AgentLogs with this.log and this.error in the JavaScript environment" do
  144. @agent.options['code'] = 'Agent.check = function() { this.log("woah"); this.error("WOAH!"); };'
  145. @agent.save!
  146. expect {
  147. expect {
  148. @agent.check
  149. }.not_to raise_error
  150. }.to change { AgentLog.count }.by(2)
  151. log1, log2 = AgentLog.last(2)
  152. expect(log1.message).to eq("woah")
  153. expect(log1.level).to eq(3)
  154. expect(log2.message).to eq("WOAH!")
  155. expect(log2.level).to eq(4)
  156. end
  157. end
  158. describe "escaping and unescaping HTML" do
  159. it "can escape and unescape html with this.escapeHtml and this.unescapeHtml in the javascript environment" do
  160. @agent.options['code'] = 'Agent.check = function() { this.createEvent({ escaped: this.escapeHtml(\'test \"escaping\" <characters>\'), unescaped: this.unescapeHtml(\'test &quot;unescaping&quot; &lt;characters&gt;\')}); };'
  161. @agent.save!
  162. expect {
  163. expect {
  164. @agent.check
  165. }.not_to change { AgentLog.count }
  166. }.to change { Event.count}.by(1)
  167. created_event = @agent.events.last
  168. expect(created_event.payload).to eq({ 'escaped' => 'test &quot;escaping&quot; &lt;characters&gt;', 'unescaped' => 'test "unescaping" <characters>'})
  169. end
  170. end
  171. describe "getting incoming events" do
  172. it "can access incoming events in the JavaScript enviroment via this.incomingEvents" do
  173. event = Event.new
  174. event.agent = agents(:bob_rain_notifier_agent)
  175. event.payload = { :data => "Something you should know about" }
  176. event.save!
  177. event.reload
  178. @agent.options['code'] = <<-JS
  179. Agent.receive = function() {
  180. var events = this.incomingEvents();
  181. for(var i = 0; i < events.length; i++) {
  182. this.createEvent({ 'message': 'I got an event!', 'event_was': events[i].payload });
  183. }
  184. }
  185. JS
  186. @agent.save!
  187. expect {
  188. expect {
  189. @agent.receive([events(:bob_website_agent_event), event])
  190. }.not_to change { AgentLog.count }
  191. }.to change { Event.count }.by(2)
  192. created_event = @agent.events.first
  193. expect(created_event.payload).to eq({ 'message' => "I got an event!", 'event_was' => { 'data' => "Something you should know about" } })
  194. end
  195. end
  196. describe "getting and setting memory, getting options" do
  197. it "can access options via this.options and work with memory via this.memory" do
  198. @agent.options['code'] = <<-JS
  199. Agent.check = function() {
  200. if (this.options('make_event')) {
  201. var callCount = this.memory('callCount') || 0;
  202. this.memory('callCount', callCount + 1);
  203. }
  204. };
  205. JS
  206. @agent.save!
  207. expect {
  208. expect {
  209. @agent.check
  210. expect(@agent.memory['callCount']).not_to be_present
  211. @agent.options['make_event'] = true
  212. @agent.check
  213. expect(@agent.memory['callCount']).to eq(1)
  214. @agent.check
  215. expect(@agent.memory['callCount']).to eq(2)
  216. @agent.memory['callCount'] = 20
  217. @agent.check
  218. expect(@agent.memory['callCount']).to eq(21)
  219. }.not_to change { AgentLog.count }
  220. }.not_to change { Event.count }
  221. end
  222. end
  223. describe "using CoffeeScript" do
  224. it "will accept a 'language' of 'CoffeeScript'" do
  225. @agent.options['code'] = 'Agent.check = -> this.log("hello from coffeescript")'
  226. @agent.options['language'] = 'CoffeeScript'
  227. @agent.save!
  228. expect {
  229. @agent.check
  230. }.not_to raise_error
  231. expect(AgentLog.last.message).to eq("hello from coffeescript")
  232. end
  233. end
  234. describe "user credentials" do
  235. it "can access an existing credential" do
  236. @agent.send(:set_credential, 'test', 'hello')
  237. @agent.options['code'] = 'Agent.check = function() { this.log(this.credential("test")); };'
  238. @agent.save!
  239. @agent.check
  240. expect(AgentLog.last.message).to eq("hello")
  241. end
  242. it "will create a new credential" do
  243. @agent.options['code'] = 'Agent.check = function() { this.credential("test","1234"); };'
  244. @agent.save!
  245. expect {
  246. @agent.check
  247. }.to change(UserCredential, :count).by(1)
  248. end
  249. it "updates an existing credential" do
  250. @agent.send(:set_credential, 'test', 1234)
  251. @agent.options['code'] = 'Agent.check = function() { this.credential("test","12345"); };'
  252. @agent.save!
  253. expect {
  254. @agent.check
  255. }.to change(UserCredential, :count).by(0)
  256. expect(@agent.user.user_credentials.last.credential_value).to eq('12345')
  257. end
  258. end
  259. end
  260. end