s3_agent_spec.rb 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. require 'rails_helper'
  2. describe Agents::S3Agent do
  3. before(:each) do
  4. @valid_params = {
  5. 'mode' => 'read',
  6. 'access_key_id' => '32343242',
  7. 'access_key_secret' => '1231312',
  8. 'watch' => 'false',
  9. 'bucket' => 'testbucket',
  10. 'region' => 'us-east-1',
  11. 'filename' => 'test.txt',
  12. 'data' => '{{ data }}'
  13. }
  14. @checker = Agents::S3Agent.new(:name => "somename", :options => @valid_params)
  15. @checker.user = users(:jane)
  16. @checker.save!
  17. end
  18. describe "#validate_options" do
  19. it "requires the bucket to be set" do
  20. @checker.options['bucket'] = ''
  21. expect(@checker).not_to be_valid
  22. end
  23. it "requires watch to be present" do
  24. @checker.options['watch'] = ''
  25. expect(@checker).not_to be_valid
  26. end
  27. it "requires watch to be either 'true' or 'false'" do
  28. @checker.options['watch'] = 'true'
  29. expect(@checker).to be_valid
  30. @checker.options['watch'] = 'false'
  31. expect(@checker).to be_valid
  32. @checker.options['watch'] = 'test'
  33. expect(@checker).not_to be_valid
  34. end
  35. it "requires region to be present" do
  36. @checker.options['region'] = ''
  37. expect(@checker).not_to be_valid
  38. end
  39. it "requires mode to be set to 'read' or 'write'" do
  40. @checker.options['mode'] = 'write'
  41. expect(@checker).to be_valid
  42. @checker.options['mode'] = ''
  43. expect(@checker).not_to be_valid
  44. end
  45. it "requires 'filename' in 'write' mode" do
  46. @checker.options['mode'] = 'write'
  47. @checker.options['filename'] = ''
  48. expect(@checker).not_to be_valid
  49. end
  50. it "requires 'data' in 'write' mode" do
  51. @checker.options['mode'] = 'write'
  52. @checker.options['data'] = ''
  53. expect(@checker).not_to be_valid
  54. end
  55. end
  56. describe "#validating" do
  57. it "validates the key" do
  58. mock(@checker).client { raise Aws::S3::Errors::SignatureDoesNotMatch.new('', '') }
  59. expect(@checker.validate_access_key_id).to be_falsy
  60. end
  61. it "validates the secret" do
  62. mock(@checker).buckets { true }
  63. expect(@checker.validate_access_key_secret).to be_truthy
  64. end
  65. end
  66. it "completes the buckets" do
  67. mock(@checker).buckets { [OpenStruct.new(name: 'test'), OpenStruct.new(name: 'test2')]}
  68. expect(@checker.complete_bucket).to eq([{text: 'test', id: 'test'}, {text: 'test2', id: 'test2'}])
  69. end
  70. context "#working" do
  71. it "is working with no recent errors" do
  72. @checker.last_check_at = Time.now
  73. expect(@checker).to be_working
  74. end
  75. end
  76. context "#check" do
  77. context "not watching" do
  78. it "emits an event for every file" do
  79. mock(@checker).get_bucket_contents { {"test"=>"231232", "test2"=>"4564545"} }
  80. expect { @checker.check }.to change(Event, :count).by(2)
  81. expect(Event.last.payload).to eq({"file_pointer" => {"file"=>"test2", "agent_id"=> @checker.id}})
  82. end
  83. end
  84. context "watching" do
  85. before(:each) do
  86. @checker.options['watch'] = 'true'
  87. end
  88. it "does not emit any events on the first run" do
  89. contents = {"test"=>"231232", "test2"=>"4564545"}
  90. mock(@checker).get_bucket_contents { contents }
  91. expect { @checker.check }.not_to change(Event, :count)
  92. expect(@checker.memory).to eq('seen_contents' => contents)
  93. end
  94. context "detecting changes" do
  95. before(:each) do
  96. contents = {"test"=>"231232", "test2"=>"4564545"}
  97. mock(@checker).get_bucket_contents { contents }
  98. expect { @checker.check }.not_to change(Event, :count)
  99. @checker.last_check_at = Time.now
  100. end
  101. it "emits events for removed files" do
  102. contents = {"test"=>"231232"}
  103. mock(@checker).get_bucket_contents { contents }
  104. expect { @checker.check }.to change(Event, :count).by(1)
  105. expect(Event.last.payload).to eq({"file_pointer" => {"file" => "test2", "agent_id"=> @checker.id}, "event_type" => "removed"})
  106. end
  107. it "emits events for modified files" do
  108. contents = {"test"=>"231232", "test2"=>"changed"}
  109. mock(@checker).get_bucket_contents { contents }
  110. expect { @checker.check }.to change(Event, :count).by(1)
  111. expect(Event.last.payload).to eq({"file_pointer" => {"file" => "test2", "agent_id"=> @checker.id}, "event_type" => "modified"})
  112. end
  113. it "emits events for added files" do
  114. contents = {"test"=>"231232", "test2"=>"4564545", "test3" => "31231231"}
  115. mock(@checker).get_bucket_contents { contents }
  116. expect { @checker.check }.to change(Event, :count).by(1)
  117. expect(Event.last.payload).to eq({"file_pointer" => {"file" => "test3", "agent_id"=> @checker.id}, "event_type" => "added"})
  118. end
  119. end
  120. context "error handling" do
  121. it "handles AccessDenied exceptions" do
  122. mock(@checker).get_bucket_contents { raise Aws::S3::Errors::AccessDenied.new('', '') }
  123. expect { @checker.check }.to change(AgentLog, :count).by(1)
  124. expect(AgentLog.last.message).to eq("Could not access 'testbucket' Aws::S3::Errors::AccessDenied ")
  125. end
  126. it "handles generic S3 exceptions" do
  127. mock(@checker).get_bucket_contents { raise Aws::S3::Errors::PermanentRedirect.new('', 'error') }
  128. expect { @checker.check }.to change(AgentLog, :count).by(1)
  129. expect(AgentLog.last.message).to eq("Aws::S3::Errors::PermanentRedirect: error")
  130. end
  131. end
  132. end
  133. end
  134. it "get_io returns a StringIO object" do
  135. stringio =StringIO.new
  136. mock_response = mock()
  137. mock(mock_response).body { stringio }
  138. mock_client = mock()
  139. mock(mock_client).get_object(bucket: 'testbucket', key: 'testfile') { mock_response }
  140. mock(@checker).client { mock_client }
  141. @checker.get_io('testfile')
  142. end
  143. context "#get_bucket_contents" do
  144. it "returns a hash with the contents of the bucket" do
  145. mock_response = mock()
  146. mock(mock_response).contents { [OpenStruct.new(key: 'test', etag: '231232'), OpenStruct.new(key: 'test2', etag: '4564545')] }
  147. mock_client = mock()
  148. mock(mock_client).list_objects(bucket: 'testbucket') { [mock_response] }
  149. mock(@checker).client { mock_client }
  150. expect(@checker.send(:get_bucket_contents)).to eq({"test"=>"231232", "test2"=>"4564545"})
  151. end
  152. end
  153. context "#client" do
  154. it "initializes the S3 client correctly" do
  155. mock_credential = mock()
  156. mock(Aws::Credentials).new('32343242', '1231312') { mock_credential }
  157. mock(Aws::S3::Client).new(credentials: mock_credential,
  158. region: 'us-east-1')
  159. @checker.send(:client)
  160. end
  161. end
  162. context "#event_description" do
  163. it "should include event_type when watch is set to true" do
  164. @checker.options['watch'] = 'true'
  165. expect(@checker.event_description).to include('event_type')
  166. end
  167. it "should not include event_type when watch is set to false" do
  168. @checker.options['watch'] = 'false'
  169. expect(@checker.event_description).not_to include('event_type')
  170. end
  171. end
  172. context "#receive" do
  173. before(:each) do
  174. @checker.options['mode'] = 'write'
  175. @checker.options['filename'] = 'file.txt'
  176. @checker.options['data'] = '{{ data }}'
  177. end
  178. it "writes the data at data into a file" do
  179. client_mock = mock()
  180. mock(client_mock).put_object(bucket: @checker.options['bucket'], key: @checker.options['filename'], body: 'hello world!')
  181. mock(@checker).client { client_mock }
  182. event = Event.new(payload: {'data' => 'hello world!'})
  183. @checker.receive([event])
  184. end
  185. it "does nothing when mode is set to 'read'" do
  186. @checker.options['mode'] = 'read'
  187. event = Event.new(payload: {'data' => 'hello world!'})
  188. @checker.receive([event])
  189. end
  190. end
  191. end