13996: Update tests for cleaner config access
[arvados.git] / services / api / test / unit / job_test.rb
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 require 'test_helper'
6 require 'helpers/git_test_helper'
7 require 'helpers/docker_migration_helper'
8
9 class JobTest < ActiveSupport::TestCase
10   include DockerMigrationHelper
11   include GitTestHelper
12
13   BAD_COLLECTION = "#{'f' * 32}+0"
14
15   setup do
16     set_user_from_auth :active
17   end
18
19   def job_attrs merge_me={}
20     # Default (valid) set of attributes, with given overrides
21     {
22       script: "hash",
23       script_version: "master",
24       repository: "active/foo",
25     }.merge(merge_me)
26   end
27
28   test "Job without Docker image doesn't get locator" do
29     job = Job.new job_attrs
30     assert job.valid?, job.errors.full_messages.to_s
31     assert_nil job.docker_image_locator
32   end
33
34   { 'name' => [:links, :docker_image_collection_tag, :name],
35     'hash' => [:links, :docker_image_collection_hash, :name],
36     'locator' => [:collections, :docker_image, :portable_data_hash],
37   }.each_pair do |spec_type, (fixture_type, fixture_name, fixture_attr)|
38     test "Job initialized with Docker image #{spec_type} gets locator" do
39       image_spec = send(fixture_type, fixture_name).send(fixture_attr)
40       job = Job.new job_attrs(runtime_constraints:
41                               {'docker_image' => image_spec})
42       assert job.valid?, job.errors.full_messages.to_s
43       assert_equal(collections(:docker_image).portable_data_hash, job.docker_image_locator)
44     end
45
46     test "Job modified with Docker image #{spec_type} gets locator" do
47       job = Job.new job_attrs
48       assert job.valid?, job.errors.full_messages.to_s
49       assert_nil job.docker_image_locator
50       image_spec = send(fixture_type, fixture_name).send(fixture_attr)
51       job.runtime_constraints['docker_image'] = image_spec
52       assert job.valid?, job.errors.full_messages.to_s
53       assert_equal(collections(:docker_image).portable_data_hash, job.docker_image_locator)
54     end
55   end
56
57   test "removing a Docker runtime constraint removes the locator" do
58     image_locator = collections(:docker_image).portable_data_hash
59     job = Job.new job_attrs(runtime_constraints:
60                             {'docker_image' => image_locator})
61     assert job.valid?, job.errors.full_messages.to_s
62     assert_equal(image_locator, job.docker_image_locator)
63     job.runtime_constraints = {}
64     assert job.valid?, job.errors.full_messages.to_s + "after clearing runtime constraints"
65     assert_nil job.docker_image_locator
66   end
67
68   test "locate a Docker image with a repository + tag" do
69     image_repo, image_tag =
70       links(:docker_image_collection_tag2).name.split(':', 2)
71     job = Job.new job_attrs(runtime_constraints:
72                             {'docker_image' => image_repo,
73                               'docker_image_tag' => image_tag})
74     assert job.valid?, job.errors.full_messages.to_s
75     assert_equal(collections(:docker_image).portable_data_hash, job.docker_image_locator)
76   end
77
78   test "can't locate a Docker image with a nonexistent tag" do
79     image_repo = links(:docker_image_collection_tag).name
80     image_tag = '__nonexistent tag__'
81     job = Job.new job_attrs(runtime_constraints:
82                             {'docker_image' => image_repo,
83                               'docker_image_tag' => image_tag})
84     assert(job.invalid?, "Job with bad Docker tag valid")
85   end
86
87   [
88     false,
89     true
90   ].each do |use_config|
91     test "Job with no Docker image uses default docker image when configuration is set #{use_config}" do
92       default_docker_image = collections(:docker_image)[:portable_data_hash]
93       Rails.configuration.Containers.JobsAPI.DefaultDockerImage = default_docker_image if use_config
94
95       job = Job.new job_attrs
96       assert job.valid?, job.errors.full_messages.to_s
97
98       if use_config
99         refute_nil job.docker_image_locator
100         assert_equal default_docker_image, job.docker_image_locator
101       else
102         assert_nil job.docker_image_locator
103       end
104     end
105   end
106
107   test "create a job with a disambiguated script_version branch name" do
108     job = Job.
109       new(script: "testscript",
110           script_version: "heads/7387838c69a21827834586cc42b467ff6c63293b",
111           repository: "active/shabranchnames",
112           script_parameters: {})
113     assert(job.save)
114     assert_equal("abec49829bf1758413509b7ffcab32a771b71e81", job.script_version)
115   end
116
117   test "locate a Docker image with a partial hash" do
118     image_hash = links(:docker_image_collection_hash).name[0..24]
119     job = Job.new job_attrs(runtime_constraints:
120                             {'docker_image' => image_hash})
121     assert job.valid?, job.errors.full_messages.to_s + " with partial hash #{image_hash}"
122     assert_equal(collections(:docker_image).portable_data_hash, job.docker_image_locator)
123   end
124
125   { 'name' => 'arvados_test_nonexistent',
126     'hash' => 'f' * 64,
127     'locator' => BAD_COLLECTION,
128   }.each_pair do |spec_type, image_spec|
129     test "Job validation fails with nonexistent Docker image #{spec_type}" do
130       Rails.configuration.RemoteClusters = {}
131       job = Job.new job_attrs(runtime_constraints:
132                               {'docker_image' => image_spec})
133       assert(job.invalid?, "nonexistent Docker image #{spec_type} #{image_spec} was valid")
134     end
135   end
136
137   test "Job validation fails with non-Docker Collection constraint" do
138     job = Job.new job_attrs(runtime_constraints:
139                             {'docker_image' => collections(:foo_file).uuid})
140     assert(job.invalid?, "non-Docker Collection constraint was valid")
141   end
142
143   test "can create Job with Docker image Collection without Docker links" do
144     image_uuid = collections(:unlinked_docker_image).portable_data_hash
145     job = Job.new job_attrs(runtime_constraints: {"docker_image" => image_uuid})
146     assert(job.valid?, "Job created with unlinked Docker image was invalid")
147     assert_equal(image_uuid, job.docker_image_locator)
148   end
149
150   def check_attrs_unset(job, attrs)
151     assert_empty(attrs.each_key.map { |key| job.send(key) }.compact,
152                  "job has values for #{attrs.keys}")
153   end
154
155   def check_creation_prohibited(attrs)
156     begin
157       job = Job.new(job_attrs(attrs))
158     rescue ActiveModel::MassAssignmentSecurity::Error
159       # Test passes - expected attribute protection
160     else
161       check_attrs_unset(job, attrs)
162     end
163   end
164
165   def check_modification_prohibited(attrs)
166     job = Job.new(job_attrs)
167     attrs.each_pair do |key, value|
168       assert_raises(NoMethodError) { job.send("{key}=".to_sym, value) }
169     end
170     check_attrs_unset(job, attrs)
171   end
172
173   test "can't create Job with Docker image locator" do
174     check_creation_prohibited(docker_image_locator: BAD_COLLECTION)
175   end
176
177   test "can't assign Docker image locator to Job" do
178     check_modification_prohibited(docker_image_locator: BAD_COLLECTION)
179   end
180
181   [
182    {script_parameters: ""},
183    {script_parameters: []},
184    {script_parameters: {["foo"] => ["bar"]}},
185    {runtime_constraints: ""},
186    {runtime_constraints: []},
187    {tasks_summary: ""},
188    {tasks_summary: []},
189   ].each do |invalid_attrs|
190     test "validation failures set error messages: #{invalid_attrs.to_json}" do
191       # Ensure valid_attrs doesn't produce errors -- otherwise we will
192       # not know whether errors reported below are actually caused by
193       # invalid_attrs.
194       Job.new(job_attrs).save!
195
196       err = assert_raises(ArgumentError) do
197         Job.new(job_attrs(invalid_attrs)).save!
198       end
199       assert_match /parameters|constraints|summary/, err.message
200     end
201   end
202
203   test "invalid script_version" do
204     invalid = {
205       script_version: "no/branch/could/ever/possibly/have/this/name",
206     }
207     err = assert_raises(ActiveRecord::RecordInvalid) do
208       Job.new(job_attrs(invalid)).save!
209     end
210     assert_match /Script version .* does not resolve to a commit/, err.message
211   end
212
213   [
214     # Each test case is of the following format
215     # Array of parameters where each parameter is of the format:
216     #  attr name to be changed, attr value, and array of expectations (where each expectation is an array)
217     [['running', false, [['state', 'Queued']]]],
218     [['state', 'Running', [['started_at', 'not_nil']]]],
219     [['is_locked_by_uuid', 'use_current_user_uuid', [['state', 'Queued']]], ['state', 'Running', [['running', true], ['started_at', 'not_nil'], ['success', 'nil']]]],
220     [['running', false, [['state', 'Queued']]], ['state', 'Complete', [['success', true]]]],
221     [['running', true, [['state', 'Running']]], ['cancelled_at', Time.now, [['state', 'Cancelled']]]],
222     [['running', true, [['state', 'Running']]], ['state', 'Cancelled', [['cancelled_at', 'not_nil']]]],
223     [['running', true, [['state', 'Running']]], ['success', true, [['state', 'Complete']]]],
224     [['running', true, [['state', 'Running']]], ['success', false, [['state', 'Failed']]]],
225     [['running', true, [['state', 'Running']]], ['state', 'Complete', [['success', true],['finished_at', 'not_nil']]]],
226     [['running', true, [['state', 'Running']]], ['state', 'Failed', [['success', false],['finished_at', 'not_nil']]]],
227     [['cancelled_at', Time.now, [['state', 'Cancelled']]], ['success', false, [['state', 'Cancelled'],['finished_at', 'nil'], ['cancelled_at', 'not_nil']]]],
228     [['cancelled_at', Time.now, [['state', 'Cancelled'],['running', false]]], ['success', true, [['state', 'Cancelled'],['running', false],['finished_at', 'nil'],['cancelled_at', 'not_nil']]]],
229     # potential migration cases
230     [['state', nil, [['state', 'Queued']]]],
231     [['state', nil, [['state', 'Queued']]], ['cancelled_at', Time.now, [['state', 'Cancelled']]]],
232     [['running', true, [['state', 'Running']]], ['state', nil, [['state', 'Running']]]],
233   ].each do |parameters|
234     test "verify job status #{parameters}" do
235       job = Job.create! job_attrs
236       assert_equal 'Queued', job.state, "job.state"
237
238       parameters.each do |parameter|
239         expectations = parameter[2]
240         if 'use_current_user_uuid' == parameter[1]
241           parameter[1] = Thread.current[:user].uuid
242         end
243
244         if expectations.instance_of? Array
245           job[parameter[0]] = parameter[1]
246           assert_equal true, job.save, job.errors.full_messages.to_s
247           expectations.each do |expectation|
248             if expectation[1] == 'not_nil'
249               assert_not_nil job[expectation[0]], expectation[0]
250             elsif expectation[1] == 'nil'
251               assert_nil job[expectation[0]], expectation[0]
252             else
253               assert_equal expectation[1], job[expectation[0]], expectation[0]
254             end
255           end
256         else
257           raise 'I do not know how to handle this expectation'
258         end
259       end
260     end
261   end
262
263   test "Test job state changes" do
264     all = ["Queued", "Running", "Complete", "Failed", "Cancelled"]
265     valid = {"Queued" => all, "Running" => ["Complete", "Failed", "Cancelled"]}
266     all.each do |start|
267       all.each do |finish|
268         if start != finish
269           job = Job.create! job_attrs(state: start)
270           assert_equal start, job.state
271           job.state = finish
272           job.save
273           job.reload
274           if valid[start] and valid[start].include? finish
275             assert_equal finish, job.state
276           else
277             assert_equal start, job.state
278           end
279         end
280       end
281     end
282   end
283
284   test "Test job locking" do
285     set_user_from_auth :active_trustedclient
286     job = Job.create! job_attrs
287
288     assert_equal "Queued", job.state
289
290     # Should be able to lock successfully
291     job.lock current_user.uuid
292     assert_equal "Running", job.state
293
294     assert_raises ArvadosModel::AlreadyLockedError do
295       # Can't lock it again
296       job.lock current_user.uuid
297     end
298     job.reload
299     assert_equal "Running", job.state
300
301     set_user_from_auth :project_viewer
302     assert_raises ArvadosModel::AlreadyLockedError do
303       # Can't lock it as a different user either
304       job.lock current_user.uuid
305     end
306     job.reload
307     assert_equal "Running", job.state
308
309     assert_raises ArvadosModel::PermissionDeniedError do
310       # Can't update fields as a different user
311       job.update_attributes(state: "Failed")
312     end
313     job.reload
314     assert_equal "Running", job.state
315
316
317     set_user_from_auth :active_trustedclient
318
319     # Can update fields as the locked_by user
320     job.update_attributes(state: "Failed")
321     assert_equal "Failed", job.state
322   end
323
324   test "admin user can cancel a running job despite lock" do
325     set_user_from_auth :active_trustedclient
326     job = Job.create! job_attrs
327     job.lock current_user.uuid
328     assert_equal Job::Running, job.state
329
330     set_user_from_auth :spectator
331     assert_raises do
332       job.update_attributes!(state: Job::Cancelled)
333     end
334
335     set_user_from_auth :admin
336     job.reload
337     assert_equal Job::Running, job.state
338     job.update_attributes!(state: Job::Cancelled)
339     assert_equal Job::Cancelled, job.state
340   end
341
342   test "verify job queue position" do
343     job1 = Job.create! job_attrs
344     assert_equal 'Queued', job1.state, "Incorrect job state for newly created job1"
345
346     job2 = Job.create! job_attrs
347     assert_equal 'Queued', job2.state, "Incorrect job state for newly created job2"
348
349     assert_not_nil job1.queue_position, "Expected non-nil queue position for job1"
350     assert_not_nil job2.queue_position, "Expected non-nil queue position for job2"
351   end
352
353   SDK_MASTER = "ca68b24e51992e790f29df5cc4bc54ce1da4a1c2"
354   SDK_TAGGED = "00634b2b8a492d6f121e3cf1d6587b821136a9a7"
355
356   def sdk_constraint(version)
357     {runtime_constraints: {
358         "arvados_sdk_version" => version,
359         "docker_image" => links(:docker_image_collection_tag).name,
360       }}
361   end
362
363   def check_job_sdk_version(expected)
364     job = yield
365     if expected.nil?
366       refute(job.valid?, "job valid with bad Arvados SDK version")
367     else
368       assert(job.valid?, "job not valid with good Arvados SDK version")
369       assert_equal(expected, job.arvados_sdk_version)
370     end
371   end
372
373   { "master" => SDK_MASTER,
374     "commit2" => SDK_TAGGED,
375     SDK_TAGGED[0, 8] => SDK_TAGGED,
376     "__nonexistent__" => nil,
377   }.each_pair do |search, commit_hash|
378     test "creating job with SDK version '#{search}'" do
379       check_job_sdk_version(commit_hash) do
380         Job.new(job_attrs(sdk_constraint(search)))
381       end
382     end
383
384     test "updating job from no SDK to version '#{search}'" do
385       job = Job.create!(job_attrs)
386       assert_nil job.arvados_sdk_version
387       check_job_sdk_version(commit_hash) do
388         job.runtime_constraints = sdk_constraint(search)[:runtime_constraints]
389         job
390       end
391     end
392
393     test "updating job from SDK version 'master' to '#{search}'" do
394       job = Job.create!(job_attrs(sdk_constraint("master")))
395       assert_equal(SDK_MASTER, job.arvados_sdk_version)
396       check_job_sdk_version(commit_hash) do
397         job.runtime_constraints = sdk_constraint(search)[:runtime_constraints]
398         job
399       end
400     end
401   end
402
403   test "clear the SDK version" do
404     job = Job.create!(job_attrs(sdk_constraint("master")))
405     assert_equal(SDK_MASTER, job.arvados_sdk_version)
406     job.runtime_constraints = {}
407     assert(job.valid?, "job invalid after clearing SDK version")
408     assert_nil(job.arvados_sdk_version)
409   end
410
411   test "job with SDK constraint, without Docker image is invalid" do
412     sdk_attrs = sdk_constraint("master")
413     sdk_attrs[:runtime_constraints].delete("docker_image")
414     job = Job.create(job_attrs(sdk_attrs))
415     refute(job.valid?, "Job valid with SDK version, without Docker image")
416     sdk_errors = job.errors.messages[:arvados_sdk_version] || []
417     refute_empty(sdk_errors.grep(/\bDocker\b/),
418                  "no Job SDK errors mention that Docker is required")
419   end
420
421   test "invalid to clear Docker image constraint when SDK constraint exists" do
422     job = Job.create!(job_attrs(sdk_constraint("master")))
423     job.runtime_constraints.delete("docker_image")
424     refute(job.valid?,
425            "Job with SDK constraint valid after clearing Docker image")
426   end
427
428   test "use migrated docker image if requesting old-format image by tag" do
429     Rails.configuration.Containers.SupportedDockerImageFormats = ['v2']
430     add_docker19_migration_link
431     job = Job.create!(
432       job_attrs(
433         script: 'foo',
434         runtime_constraints: {
435           'docker_image' => links(:docker_image_collection_tag).name}))
436     assert(job.valid?)
437     assert_equal(job.docker_image_locator, collections(:docker_image_1_12).portable_data_hash)
438   end
439
440   test "use migrated docker image if requesting old-format image by pdh" do
441     Rails.configuration.Containers.SupportedDockerImageFormats = ['v2']
442     add_docker19_migration_link
443     job = Job.create!(
444       job_attrs(
445         script: 'foo',
446         runtime_constraints: {
447           'docker_image' => collections(:docker_image).portable_data_hash}))
448     assert(job.valid?)
449     assert_equal(job.docker_image_locator, collections(:docker_image_1_12).portable_data_hash)
450   end
451
452   [[:docker_image, :docker_image, :docker_image_1_12],
453    [:docker_image_1_12, :docker_image, :docker_image_1_12],
454    [:docker_image, :docker_image_1_12, :docker_image_1_12],
455    [:docker_image_1_12, :docker_image_1_12, :docker_image_1_12],
456   ].each do |existing_image, request_image, expect_image|
457     test "if a #{existing_image} job exists, #{request_image} yields #{expect_image} after migration" do
458       Rails.configuration.Containers.SupportedDockerImageFormats = ['v1']
459
460       if existing_image == :docker_image
461         oldjob = Job.create!(
462           job_attrs(
463             script: 'foobar1',
464             runtime_constraints: {
465               'docker_image' => collections(existing_image).portable_data_hash}))
466         oldjob.reload
467         assert_equal(oldjob.docker_image_locator,
468                      collections(existing_image).portable_data_hash)
469       elsif existing_image == :docker_image_1_12
470         assert_raises(ActiveRecord::RecordInvalid,
471                       "Should not resolve v2 image when only v1 is supported") do
472         oldjob = Job.create!(
473           job_attrs(
474             script: 'foobar1',
475             runtime_constraints: {
476               'docker_image' => collections(existing_image).portable_data_hash}))
477         end
478       end
479
480       Rails.configuration.Containers.SupportedDockerImageFormats = ['v2']
481       add_docker19_migration_link
482
483       # Check that both v1 and v2 images get resolved to v2.
484       newjob = Job.create!(
485         job_attrs(
486           script: 'foobar1',
487           runtime_constraints: {
488             'docker_image' => collections(request_image).portable_data_hash}))
489       newjob.reload
490       assert_equal(newjob.docker_image_locator,
491                    collections(expect_image).portable_data_hash)
492     end
493   end
494
495   test "can't create job with SDK version assigned directly" do
496     check_creation_prohibited(arvados_sdk_version: SDK_MASTER)
497   end
498
499   test "can't modify job to assign SDK version directly" do
500     check_modification_prohibited(arvados_sdk_version: SDK_MASTER)
501   end
502
503   test "job validation fails when collection uuid found in script_parameters" do
504     bad_params = {
505       script_parameters: {
506         'input' => {
507           'param1' => 'the collection uuid zzzzz-4zz18-012345678901234'
508         }
509       }
510     }
511     assert_raises(ActiveRecord::RecordInvalid,
512                   "created job with a collection uuid in script_parameters") do
513       Job.create!(job_attrs(bad_params))
514     end
515   end
516
517   test "job validation succeeds when no collection uuid in script_parameters" do
518     good_params = {
519       script_parameters: {
520         'arg1' => 'foo',
521         'arg2' => [ 'bar', 'baz' ],
522         'arg3' => {
523           'a' => 1,
524           'b' => [2, 3, 4],
525         }
526       }
527     }
528     job = Job.create!(job_attrs(good_params))
529     assert job.valid?
530   end
531
532   test 'update job uuid tag in internal.git when version changes' do
533     authorize_with :active
534     j = jobs :queued
535     j.update_attributes repository: 'active/foo', script_version: 'b1'
536     assert_equal('1de84a854e2b440dc53bf42f8548afa4c17da332',
537                  internal_tag(j.uuid))
538     j.update_attributes repository: 'active/foo', script_version: 'master'
539     assert_equal('077ba2ad3ea24a929091a9e6ce545c93199b8e57',
540                  internal_tag(j.uuid))
541   end
542
543   test 'script_parameters_digest is independent of key order' do
544     j1 = Job.new(job_attrs(script_parameters: {'a' => 'a', 'ddee' => {'d' => 'd', 'e' => 'e'}}))
545     j2 = Job.new(job_attrs(script_parameters: {'ddee' => {'e' => 'e', 'd' => 'd'}, 'a' => 'a'}))
546     assert j1.valid?
547     assert j2.valid?
548     assert_equal(j1.script_parameters_digest, j2.script_parameters_digest)
549   end
550
551   test 'job fixtures have correct script_parameters_digest' do
552     Job.all.each do |j|
553       d = j.script_parameters_digest
554       assert_equal(j.update_script_parameters_digest, d,
555                    "wrong script_parameters_digest for #{j.uuid}")
556     end
557   end
558
559   test 'deep_sort_hash on array of hashes' do
560     a = {'z' => [[{'a' => 'a', 'b' => 'b'}]]}
561     b = {'z' => [[{'b' => 'b', 'a' => 'a'}]]}
562     assert_equal Job.deep_sort_hash(a).to_json, Job.deep_sort_hash(b).to_json
563   end
564
565   test 'find_reusable without logging' do
566     Rails.logger.expects(:info).never
567     try_find_reusable
568   end
569
570   test 'find_reusable with logging' do
571     Rails.configuration.Containers.LogReuseDecisions = true
572     Rails.logger.expects(:info).at_least(3)
573     try_find_reusable
574   end
575
576   def try_find_reusable
577     foobar = jobs(:foobar)
578     example_attrs = {
579       script_version: foobar.script_version,
580       script: foobar.script,
581       script_parameters: foobar.script_parameters,
582       repository: foobar.repository,
583     }
584
585     # Two matching jobs exist with identical outputs. The older one
586     # should be reused.
587     j = Job.find_reusable(example_attrs, {}, [], [users(:active)])
588     assert j
589     assert_equal foobar.uuid, j.uuid
590
591     # Two matching jobs exist with different outputs. Neither should
592     # be reused.
593     Job.where(uuid: jobs(:job_with_latest_version).uuid).
594       update_all(output: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+1')
595     assert_nil Job.find_reusable(example_attrs, {}, [], [users(:active)])
596
597     # ...unless config says to reuse the earlier job in such cases.
598     Rails.configuration.Containers.JobsAPI.ReuseJobIfOutputsDiffer = true
599     j = Job.find_reusable(example_attrs, {}, [], [users(:active)])
600     assert_equal foobar.uuid, j.uuid
601   end
602
603   [
604     true,
605     false,
606   ].each do |cascade|
607     test "cancel job with cascade #{cascade}" do
608       job = Job.find_by_uuid jobs(:running_job_with_components_at_level_1).uuid
609       job.cancel cascade: cascade
610       assert_equal Job::Cancelled, job.state
611
612       descendents = ['zzzzz-8i9sb-jobcomponentsl2',
613                      'zzzzz-d1hrv-picomponentsl02',
614                      'zzzzz-8i9sb-job1atlevel3noc',
615                      'zzzzz-8i9sb-job2atlevel3noc']
616
617       jobs = Job.where(uuid: descendents)
618       jobs.each do |j|
619         assert_equal ('Cancelled' == j.state), cascade
620       end
621
622       pipelines = PipelineInstance.where(uuid: descendents)
623       pipelines.each do |pi|
624         assert_equal ('Paused' == pi.state), cascade
625       end
626     end
627   end
628
629   test 'cancelling a completed job raises error' do
630     job = Job.find_by_uuid jobs(:job_with_latest_version).uuid
631     assert job
632     assert_equal 'Complete', job.state
633
634     assert_raises(ArvadosModel::InvalidStateTransitionError) do
635       job.cancel
636     end
637   end
638
639   test 'cancelling a job with circular relationship with another does not result in an infinite loop' do
640     job = Job.find_by_uuid jobs(:running_job_2_with_circular_component_relationship).uuid
641
642     job.cancel cascade: true
643
644     assert_equal Job::Cancelled, job.state
645
646     child = Job.find_by_uuid job.components.collect{|_, uuid| uuid}[0]
647     assert_equal Job::Cancelled, child.state
648   end
649
650   test 'enable legacy api configuration option = true' do
651     Rails.configuration.Containers.JobsAPI.Enable = "true"
652     check_enable_legacy_jobs_api
653     assert_equal [], Rails.configuration.API.DisabledAPIs
654   end
655
656   test 'enable legacy api configuration option = false' do
657     Rails.configuration.Containers.JobsAPI.Enable = "false"
658     check_enable_legacy_jobs_api
659     assert_equal Disable_jobs_api_method_list, Rails.configuration.API.DisabledAPIs
660   end
661
662   test 'enable legacy api configuration option = auto, has jobs' do
663     Rails.configuration.Containers.JobsAPI.Enable = "auto"
664     assert Job.count > 0
665     check_enable_legacy_jobs_api
666     assert_equal [], Rails.configuration.API.DisabledAPIs
667   end
668
669   test 'enable legacy api configuration option = auto, no jobs' do
670     Rails.configuration.Containers.JobsAPI.Enable = "auto"
671     act_as_system_user do
672       Job.destroy_all
673     end
674     assert_equal 0, Job.count
675     assert_equal [], Rails.configuration.API.DisabledAPIs
676     check_enable_legacy_jobs_api
677     assert_equal Disable_jobs_api_method_list, Rails.configuration.API.DisabledAPIs
678   end
679 end