5105: Remove unnecessary self.included/class_eval stuff.
[arvados.git] / apps / workbench / test / test_helper.rb
1 ENV["RAILS_ENV"] = "test" if (ENV["RAILS_ENV"] != "diagnostics" and ENV["RAILS_ENV"] != "performance")
2
3 unless ENV["NO_COVERAGE_TEST"]
4   begin
5     require 'simplecov'
6     require 'simplecov-rcov'
7     class SimpleCov::Formatter::MergedFormatter
8       def format(result)
9         SimpleCov::Formatter::HTMLFormatter.new.format(result)
10         SimpleCov::Formatter::RcovFormatter.new.format(result)
11       end
12     end
13     SimpleCov.formatter = SimpleCov::Formatter::MergedFormatter
14     SimpleCov.start do
15       add_filter '/test/'
16       add_filter 'initializers/secret_token'
17     end
18   rescue Exception => e
19     $stderr.puts "SimpleCov unavailable (#{e}). Proceeding without."
20   end
21 end
22
23 require File.expand_path('../../config/environment', __FILE__)
24 require 'rails/test_help'
25 require 'mocha/mini_test'
26
27 class ActiveSupport::TestCase
28   # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in
29   # alphabetical order.
30   #
31   # Note: You'll currently still have to declare fixtures explicitly
32   # in integration tests -- they do not yet inherit this setting
33   fixtures :all
34   def use_token token_name
35     was = Thread.current[:arvados_api_token]
36     auth = api_fixture('api_client_authorizations')[token_name.to_s]
37     Thread.current[:arvados_api_token] = auth['api_token']
38     if block_given?
39       yield
40       Thread.current[:arvados_api_token] = was
41     end
42   end
43
44   setup do
45     Thread.current[:arvados_api_token] = nil
46     Thread.current[:user] = nil
47     Thread.current[:reader_tokens] = nil
48     # Diagnostics suite doesn't run a server, so there's no cache to clear.
49     Rails.cache.clear unless (Rails.env == "diagnostics")
50     # Restore configuration settings changed during tests
51     self.class.reset_application_config
52   end
53
54   def self.reset_application_config
55     $application_config.each do |k,v|
56       if k.match /^[^.]*$/
57         Rails.configuration.send (k + '='), v
58       end
59     end
60   end
61 end
62
63 module ApiFixtureLoader
64   def self.included(base)
65     base.extend(ClassMethods)
66   end
67
68   module ClassMethods
69     @@api_fixtures = {}
70     def api_fixture(name, *keys)
71       # Returns the data structure from the named API server test fixture.
72       @@api_fixtures[name] ||= \
73       begin
74         path = File.join(ApiServerForTests::ARV_API_SERVER_DIR,
75                          'test', 'fixtures', "#{name}.yml")
76         file = IO.read(path)
77         trim_index = file.index('# Test Helper trims the rest of the file')
78         file = file[0, trim_index] if trim_index
79         YAML.load(file)
80       end
81       keys.inject(@@api_fixtures[name]) { |hash, key| hash[key] }
82     end
83   end
84   def api_fixture(name, *keys)
85     self.class.api_fixture(name, *keys)
86   end
87
88   def find_fixture(object_class, name)
89     object_class.find(api_fixture(object_class.to_s.pluralize.underscore,
90                                   name, "uuid"))
91   end
92 end
93
94 module ApiMockHelpers
95   def stub_api_calls_with_body body, status_code=200
96     resp = mock
97     stubbed_client = ArvadosApiClient.new
98     stubbed_client.instance_eval do
99       resp.responds_like_instance_of HTTP::Message
100       resp.stubs(:content).returns body
101       resp.stubs(:status_code).returns status_code
102       @api_client = HTTPClient.new
103       @api_client.stubs(:post).returns resp
104     end
105     ArvadosApiClient.stubs(:new_or_current).returns(stubbed_client)
106   end
107
108   def stub_api_calls_with_invalid_json
109     stub_api_calls_with_body ']"omg,bogus"['
110   end
111 end
112
113 class ActiveSupport::TestCase
114   include ApiMockHelpers
115 end
116
117 class ActiveSupport::TestCase
118   include ApiFixtureLoader
119   def session_for api_client_auth_name
120     {
121       arvados_api_token: api_fixture('api_client_authorizations')[api_client_auth_name.to_s]['api_token']
122     }
123   end
124   def json_response
125     Oj.load(@response.body)
126   end
127 end
128
129 class ApiServerForTests
130   PYTHON_TESTS_DIR = File.expand_path('../../../../sdk/python/tests', __FILE__)
131   ARV_API_SERVER_DIR = File.expand_path('../../../../services/api', __FILE__)
132   SERVER_PID_PATH = File.expand_path('tmp/pids/test-server.pid', ARV_API_SERVER_DIR)
133   WEBSOCKET_PID_PATH = File.expand_path('tmp/pids/test-server.pid', ARV_API_SERVER_DIR)
134   @main_process_pid = $$
135   @@server_is_running = false
136
137   def check_call *args
138     output = nil
139     Bundler.with_clean_env do
140       output = IO.popen *args do |io|
141         io.read
142       end
143       if not $?.success?
144         raise RuntimeError, "Command failed (#{$?}): #{args.inspect}"
145       end
146     end
147     output
148   end
149
150   def run_test_server
151     env_script = nil
152     Dir.chdir PYTHON_TESTS_DIR do
153       env_script = check_call %w(python ./run_test_server.py start --auth admin)
154     end
155     test_env = {}
156     env_script.each_line do |line|
157       line = line.chomp
158       if 0 == line.index('export ')
159         toks = line.sub('export ', '').split '=', 2
160         $stderr.puts "run_test_server.py: #{toks[0]}=#{toks[1]}"
161         test_env[toks[0]] = toks[1]
162       end
163     end
164     test_env
165   end
166
167   def stop_test_server
168     Dir.chdir PYTHON_TESTS_DIR do
169       # This is a no-op if we're running within run-tests.sh
170       check_call %w(python ./run_test_server.py stop)
171     end
172     @@server_is_running = false
173   end
174
175   def run args=[]
176     return if @@server_is_running
177
178     # Stop server left over from interrupted previous run
179     stop_test_server
180
181     ::MiniTest.after_run do
182       stop_test_server
183     end
184
185     test_env = run_test_server
186     $application_config['arvados_login_base'] = "https://#{test_env['ARVADOS_API_HOST']}/login"
187     $application_config['arvados_v1_base'] = "https://#{test_env['ARVADOS_API_HOST']}/arvados/v1"
188     $application_config['arvados_insecure_host'] = true
189     ActiveSupport::TestCase.reset_application_config
190
191     @@server_is_running = true
192   end
193
194   def run_rake_task task_name, arg_string
195     Dir.chdir ARV_API_SERVER_DIR do
196       check_call ['bundle', 'exec', 'rake', "#{task_name}[#{arg_string}]"]
197     end
198   end
199 end
200
201 class ActionController::TestCase
202   setup do
203     @counter = 0
204   end
205
206   def check_counter action
207     @counter += 1
208     if @counter == 2
209       assert_equal 1, 2, "Multiple actions in controller test"
210     end
211   end
212
213   [:get, :post, :put, :patch, :delete].each do |method|
214     define_method method do |action, *args|
215       check_counter action
216       super action, *args
217     end
218   end
219 end
220
221 # Test classes can call reset_api_fixtures(when_to_reset,flag) to
222 # override the default. Example:
223 #
224 # class MySuite < ActionDispatch::IntegrationTest
225 #   reset_api_fixtures :after_each_test, false
226 #   reset_api_fixtures :after_suite, true
227 #   ...
228 # end
229 #
230 # The default behavior is reset_api_fixtures(:after_each_test,true).
231 #
232 class ActiveSupport::TestCase
233
234   def self.inherited subclass
235     subclass.class_eval do
236       class << self
237         attr_accessor :want_reset_api_fixtures
238       end
239       @want_reset_api_fixtures = {
240         after_each_test: true,
241         after_suite: false,
242         before_suite: false,
243       }
244     end
245     super
246   end
247   # Existing subclasses of ActiveSupport::TestCase (ones that already
248   # existed before we set up the self.inherited hook above) will not
249   # get their own instance variable. They're not real test cases
250   # anyway, so we give them a "don't reset anywhere" stub.
251   def self.want_reset_api_fixtures
252     {}
253   end
254
255   def self.reset_api_fixtures where, t=true
256     if not want_reset_api_fixtures.has_key? where
257       raise ArgumentError, "There is no #{where.inspect} hook"
258     end
259     self.want_reset_api_fixtures[where] = t
260   end
261
262   def self.run *args
263     reset_api_fixtures_now if want_reset_api_fixtures[:before_suite]
264     result = super
265     reset_api_fixtures_now if want_reset_api_fixtures[:after_suite]
266     result
267   end
268
269   def after_teardown
270     if self.class.want_reset_api_fixtures[:after_each_test]
271       self.class.reset_api_fixtures_now
272     end
273     super
274   end
275
276   protected
277   def self.reset_api_fixtures_now
278     # Never try to reset fixtures when we're just using test
279     # infrastructure to run performance/diagnostics suites.
280     return unless Rails.env == 'test'
281
282     auth = api_fixture('api_client_authorizations')['admin_trustedclient']
283     Thread.current[:arvados_api_token] = auth['api_token']
284     ArvadosApiClient.new.api(nil, '../../database/reset', {})
285     Thread.current[:arvados_api_token] = nil
286   end
287 end
288
289 # If it quacks like a duck, it must be a HTTP request object.
290 class RequestDuck
291   def self.host
292     "localhost"
293   end
294
295   def self.port
296     8080
297   end
298
299   def self.protocol
300     "http"
301   end
302 end
303
304 # Example:
305 #
306 # apps/workbench$ RAILS_ENV=test bundle exec irb -Ilib:test
307 # > load 'test/test_helper.rb'
308 # > singletest 'integration/collection_upload_test.rb', 'Upload two empty files'
309 #
310 def singletest test_class_file, test_name
311   load File.join('test', test_class_file)
312   Minitest.run ['-v', '-n', "test_#{test_name.gsub ' ', '_'}"]
313   Object.send(:remove_const,
314               test_class_file.gsub(/.*\/|\.rb$/, '').camelize.to_sym)
315   ::Minitest::Runnable.runnables.reject! { true }
316 end
317
318 if ENV["RAILS_ENV"].eql? 'test'
319   ApiServerForTests.new.run
320   ApiServerForTests.new.run ["--websockets"]
321 end
322
323 # Reset fixtures now (i.e., before any tests run).
324 ActiveSupport::TestCase.reset_api_fixtures_now