13627: Fix tests after rebase
[arvados.git] / sdk / cwl / tests / test_submit.py
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: Apache-2.0
4
5 import copy
6 import cStringIO
7 import functools
8 import hashlib
9 import json
10 import logging
11 import mock
12 import sys
13 import unittest
14
15 import arvados
16 import arvados.collection
17 import arvados_cwl
18 import arvados_cwl.runner
19 import arvados.keep
20
21 from .matcher import JsonDiffMatcher, StripYAMLComments
22 from .mock_discovery import get_rootDesc
23
24 import ruamel.yaml as yaml
25
26 _rootDesc = None
27
28 def stubs(func):
29     @functools.wraps(func)
30     @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
31     @mock.patch("arvados.collection.KeepClient")
32     @mock.patch("arvados.keep.KeepClient")
33     @mock.patch("arvados.events.subscribe")
34     def wrapped(self, events, keep_client1, keep_client2, keepdocker, *args, **kwargs):
35         class Stubs:
36             pass
37         stubs = Stubs()
38         stubs.events = events
39         stubs.keepdocker = keepdocker
40
41         def putstub(p, **kwargs):
42             return "%s+%i" % (hashlib.md5(p).hexdigest(), len(p))
43         keep_client1().put.side_effect = putstub
44         keep_client1.put.side_effect = putstub
45         keep_client2().put.side_effect = putstub
46         keep_client2.put.side_effect = putstub
47
48         stubs.keep_client = keep_client2
49         stubs.keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
50         stubs.fake_user_uuid = "zzzzz-tpzed-zzzzzzzzzzzzzzz"
51
52         stubs.api = mock.MagicMock()
53         stubs.api._rootDesc = get_rootDesc()
54
55         stubs.api.users().current().execute.return_value = {
56             "uuid": stubs.fake_user_uuid,
57         }
58         stubs.api.collections().list().execute.return_value = {"items": []}
59
60         class CollectionExecute(object):
61             def __init__(self, exe):
62                 self.exe = exe
63             def execute(self, num_retries=None):
64                 return self.exe
65
66         def collection_createstub(created_collections, body, ensure_unique_name=None):
67             mt = body["manifest_text"]
68             uuid = "zzzzz-4zz18-zzzzzzzzzzzzzz%d" % len(created_collections)
69             pdh = "%s+%i" % (hashlib.md5(mt).hexdigest(), len(mt))
70             created_collections[uuid] = {
71                 "uuid": uuid,
72                 "portable_data_hash": pdh,
73                 "manifest_text": mt
74             }
75             return CollectionExecute(created_collections[uuid])
76
77         def collection_getstub(created_collections, uuid):
78             for v in created_collections.itervalues():
79                 if uuid in (v["uuid"], v["portable_data_hash"]):
80                     return CollectionExecute(v)
81
82         created_collections = {
83             "99999999999999999999999999999998+99": {
84                 "uuid": "",
85                 "portable_data_hash": "99999999999999999999999999999998+99",
86                 "manifest_text": ". 99999999999999999999999999999998+99 0:0:file1.txt"
87             },
88             "99999999999999999999999999999994+99": {
89                 "uuid": "",
90                 "portable_data_hash": "99999999999999999999999999999994+99",
91                 "manifest_text": ". 99999999999999999999999999999994+99 0:0:expect_arvworkflow.cwl"
92             }
93         }
94         stubs.api.collections().create.side_effect = functools.partial(collection_createstub, created_collections)
95         stubs.api.collections().get.side_effect = functools.partial(collection_getstub, created_collections)
96
97         stubs.expect_job_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
98         stubs.api.jobs().create().execute.return_value = {
99             "uuid": stubs.expect_job_uuid,
100             "state": "Queued",
101         }
102
103         stubs.expect_container_request_uuid = "zzzzz-xvhdp-zzzzzzzzzzzzzzz"
104         stubs.api.container_requests().create().execute.return_value = {
105             "uuid": stubs.expect_container_request_uuid,
106             "container_uuid": "zzzzz-dz642-zzzzzzzzzzzzzzz",
107             "state": "Queued"
108         }
109
110         stubs.expect_pipeline_template_uuid = "zzzzz-d1hrv-zzzzzzzzzzzzzzz"
111         stubs.api.pipeline_templates().create().execute.return_value = {
112             "uuid": stubs.expect_pipeline_template_uuid,
113         }
114         stubs.expect_job_spec = {
115             'runtime_constraints': {
116                 'docker_image': 'arvados/jobs:'+arvados_cwl.__version__,
117                 'min_ram_mb_per_node': 1024
118             },
119             'script_parameters': {
120                 'x': {
121                     'basename': 'blorp.txt',
122                     'location': 'keep:169f39d466a5438ac4a90e779bf750c7+53/blorp.txt',
123                     'class': 'File'
124                 },
125                 'y': {
126                     'basename': '99999999999999999999999999999998+99',
127                     'location': 'keep:99999999999999999999999999999998+99',
128                     'class': 'Directory'
129                 },
130                 'z': {
131                     'basename': 'anonymous',
132                     "listing": [{
133                         "basename": "renamed.txt",
134                         "class": "File",
135                         "location": "keep:99999999999999999999999999999998+99/file1.txt"
136                     }],
137                     'class': 'Directory'
138                 },
139                 'cwl:tool': '3fffdeaa75e018172e1b583425f4ebff+60/workflow.cwl#main'
140             },
141             'repository': 'arvados',
142             'script_version': 'master',
143             'minimum_script_version': '570509ab4d2ef93d870fd2b1f2eab178afb1bad9',
144             'script': 'cwl-runner'
145         }
146         stubs.pipeline_component = stubs.expect_job_spec.copy()
147         stubs.expect_pipeline_instance = {
148             'name': 'submit_wf.cwl',
149             'state': 'RunningOnServer',
150             'owner_uuid': None,
151             "components": {
152                 "cwl-runner": {
153                     'runtime_constraints': {'docker_image': 'arvados/jobs:'+arvados_cwl.__version__, 'min_ram_mb_per_node': 1024},
154                     'script_parameters': {
155                         'y': {"value": {'basename': '99999999999999999999999999999998+99', 'location': 'keep:99999999999999999999999999999998+99', 'class': 'Directory'}},
156                         'x': {"value": {
157                             'basename': 'blorp.txt',
158                             'class': 'File',
159                             'location': 'keep:169f39d466a5438ac4a90e779bf750c7+53/blorp.txt',
160                             "size": 16
161                         }},
162                         'z': {"value": {'basename': 'anonymous', 'class': 'Directory',
163                               'listing': [
164                                   {
165                                       'basename': 'renamed.txt',
166                                       'class': 'File', 'location':
167                                       'keep:99999999999999999999999999999998+99/file1.txt'
168                                   }
169                               ]}},
170                         'cwl:tool': '3fffdeaa75e018172e1b583425f4ebff+60/workflow.cwl#main',
171                         'arv:debug': True,
172                         'arv:enable_reuse': True,
173                         'arv:on_error': 'continue'
174                     },
175                     'repository': 'arvados',
176                     'script_version': 'master',
177                     'minimum_script_version': '570509ab4d2ef93d870fd2b1f2eab178afb1bad9',
178                     'script': 'cwl-runner',
179                     'job': {'state': 'Queued', 'uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz'}
180                 }
181             }
182         }
183         stubs.pipeline_create = copy.deepcopy(stubs.expect_pipeline_instance)
184         stubs.expect_pipeline_uuid = "zzzzz-d1hrv-zzzzzzzzzzzzzzz"
185         stubs.pipeline_create["uuid"] = stubs.expect_pipeline_uuid
186         stubs.pipeline_with_job = copy.deepcopy(stubs.pipeline_create)
187         stubs.pipeline_with_job["components"]["cwl-runner"]["job"] = {
188             "uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz",
189             "state": "Queued"
190         }
191         stubs.api.pipeline_instances().create().execute.return_value = stubs.pipeline_create
192         stubs.api.pipeline_instances().get().execute.return_value = stubs.pipeline_with_job
193
194         with open("tests/wf/submit_wf_packed.cwl") as f:
195             expect_packed_workflow = yaml.round_trip_load(f)
196
197         stubs.expect_container_spec = {
198             'priority': 500,
199             'mounts': {
200                 '/var/spool/cwl': {
201                     'writable': True,
202                     'kind': 'collection'
203                 },
204                 '/var/lib/cwl/workflow.json': {
205                     'content': expect_packed_workflow,
206                     'kind': 'json'
207                 },
208                 'stdout': {
209                     'path': '/var/spool/cwl/cwl.output.json',
210                     'kind': 'file'
211                 },
212                 '/var/lib/cwl/cwl.input.json': {
213                     'kind': 'json',
214                     'content': {
215                         'y': {
216                             'basename': '99999999999999999999999999999998+99',
217                             'location': 'keep:99999999999999999999999999999998+99',
218                             'class': 'Directory'},
219                         'x': {
220                             'basename': u'blorp.txt',
221                             'class': 'File',
222                             'location': u'keep:169f39d466a5438ac4a90e779bf750c7+53/blorp.txt',
223                             "size": 16
224                         },
225                         'z': {'basename': 'anonymous', 'class': 'Directory', 'listing': [
226                             {'basename': 'renamed.txt',
227                              'class': 'File',
228                              'location': 'keep:99999999999999999999999999999998+99/file1.txt'
229                             }
230                         ]}
231                     },
232                     'kind': 'json'
233                 }
234             },
235             'secret_mounts': {},
236             'state': 'Committed',
237             'command': ['arvados-cwl-runner', '--local', '--api=containers',
238                         '--no-log-timestamps', '--disable-validate',
239                         '--eval-timeout=20', '--thread-count=4',
240                         '--enable-reuse', '--debug', '--on-error=continue',
241                         '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json'],
242             'name': 'submit_wf.cwl',
243             'container_image': 'arvados/jobs:'+arvados_cwl.__version__,
244             'output_path': '/var/spool/cwl',
245             'cwd': '/var/spool/cwl',
246             'runtime_constraints': {
247                 'API': True,
248                 'vcpus': 1,
249                 'ram': 1024*1024*1024
250             },
251             'use_existing': True,
252             'properties': {},
253             'secret_mounts': {}
254         }
255
256         stubs.expect_workflow_uuid = "zzzzz-7fd4e-zzzzzzzzzzzzzzz"
257         stubs.api.workflows().create().execute.return_value = {
258             "uuid": stubs.expect_workflow_uuid,
259         }
260         def update_mock(**kwargs):
261             stubs.updated_uuid = kwargs.get('uuid')
262             return mock.DEFAULT
263         stubs.api.workflows().update.side_effect = update_mock
264         stubs.api.workflows().update().execute.side_effect = lambda **kwargs: {
265             "uuid": stubs.updated_uuid,
266         }
267
268         return func(self, stubs, *args, **kwargs)
269     return wrapped
270
271
272 class TestSubmit(unittest.TestCase):
273     @mock.patch("arvados_cwl.runner.arv_docker_get_image")
274     @mock.patch("time.sleep")
275     @stubs
276     def test_submit(self, stubs, tm, arvdock):
277         capture_stdout = cStringIO.StringIO()
278         exited = arvados_cwl.main(
279             ["--submit", "--no-wait", "--api=jobs", "--debug",
280              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
281             capture_stdout, sys.stderr, api_client=stubs.api)
282         self.assertEqual(exited, 0)
283
284         stubs.api.collections().create.assert_has_calls([
285             mock.call(body=JsonDiffMatcher({
286                 'manifest_text':
287                 '. 5bcc9fe8f8d5992e6cf418dc7ce4dbb3+16 0:16:blub.txt\n',
288                 'replication_desired': None,
289                 'name': 'submit_tool.cwl dependencies',
290             }), ensure_unique_name=True),
291             mock.call(body=JsonDiffMatcher({
292                 'manifest_text':
293                 '. 979af1245a12a1fed634d4222473bfdc+16 0:16:blorp.txt\n',
294                 'replication_desired': None,
295                 'name': 'submit_wf.cwl input',
296             }), ensure_unique_name=True),
297             mock.call(body=JsonDiffMatcher({
298                 'manifest_text':
299                 '. 61df2ed9ee3eb7dd9b799e5ca35305fa+1217 0:1217:workflow.cwl\n',
300                 'replication_desired': None,
301                 'name': 'submit_wf.cwl',
302             }), ensure_unique_name=True)        ])
303
304         arvdock.assert_has_calls([
305             mock.call(stubs.api, {"class": "DockerRequirement", "dockerPull": "debian:8"}, True, None),
306             mock.call(stubs.api, {'dockerPull': 'arvados/jobs:'+arvados_cwl.__version__}, True, None)
307         ])
308
309         expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
310         stubs.api.pipeline_instances().create.assert_called_with(
311             body=JsonDiffMatcher(expect_pipeline))
312         self.assertEqual(capture_stdout.getvalue(),
313                          stubs.expect_pipeline_uuid + '\n')
314
315
316     @mock.patch("time.sleep")
317     @stubs
318     def test_submit_no_reuse(self, stubs, tm):
319         capture_stdout = cStringIO.StringIO()
320         exited = arvados_cwl.main(
321             ["--submit", "--no-wait", "--api=jobs", "--debug", "--disable-reuse",
322              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
323             capture_stdout, sys.stderr, api_client=stubs.api)
324         self.assertEqual(exited, 0)
325
326         expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
327         expect_pipeline["components"]["cwl-runner"]["script_parameters"]["arv:enable_reuse"] = {"value": False}
328         expect_pipeline["properties"] = {"run_options": {"enable_job_reuse": False}}
329
330         stubs.api.pipeline_instances().create.assert_called_with(
331             body=JsonDiffMatcher(expect_pipeline))
332         self.assertEqual(capture_stdout.getvalue(),
333                          stubs.expect_pipeline_uuid + '\n')
334
335     @stubs
336     def test_error_when_multiple_storage_classes_specified(self, stubs):
337         storage_classes = "foo,bar"
338         exited = arvados_cwl.main(
339                 ["--debug", "--storage-classes", storage_classes,
340                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
341                 sys.stdin, sys.stderr, api_client=stubs.api)
342         self.assertEqual(exited, 1)
343
344     @mock.patch("time.sleep")
345     @stubs
346     def test_submit_on_error(self, stubs, tm):
347         capture_stdout = cStringIO.StringIO()
348         exited = arvados_cwl.main(
349             ["--submit", "--no-wait", "--api=jobs", "--debug", "--on-error=stop",
350              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
351             capture_stdout, sys.stderr, api_client=stubs.api)
352         self.assertEqual(exited, 0)
353
354         expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
355         expect_pipeline["components"]["cwl-runner"]["script_parameters"]["arv:on_error"] = "stop"
356
357         stubs.api.pipeline_instances().create.assert_called_with(
358             body=JsonDiffMatcher(expect_pipeline))
359         self.assertEqual(capture_stdout.getvalue(),
360                          stubs.expect_pipeline_uuid + '\n')
361
362
363     @mock.patch("time.sleep")
364     @stubs
365     def test_submit_runner_ram(self, stubs, tm):
366         capture_stdout = cStringIO.StringIO()
367         exited = arvados_cwl.main(
368             ["--submit", "--no-wait", "--debug", "--submit-runner-ram=2048",
369              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
370             capture_stdout, sys.stderr, api_client=stubs.api)
371         self.assertEqual(exited, 0)
372
373         expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
374         expect_pipeline["components"]["cwl-runner"]["runtime_constraints"]["min_ram_mb_per_node"] = 2048
375
376         stubs.api.pipeline_instances().create.assert_called_with(
377             body=JsonDiffMatcher(expect_pipeline))
378         self.assertEqual(capture_stdout.getvalue(),
379                          stubs.expect_pipeline_uuid + '\n')
380
381
382     @mock.patch("time.sleep")
383     @stubs
384     def test_submit_invalid_runner_ram(self, stubs, tm):
385         capture_stdout = cStringIO.StringIO()
386         exited = arvados_cwl.main(
387             ["--submit", "--no-wait", "--debug", "--submit-runner-ram=-2048",
388              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
389             capture_stdout, sys.stderr, api_client=stubs.api)
390         self.assertEqual(exited, 1)
391
392     @mock.patch("time.sleep")
393     @stubs
394     def test_submit_output_name(self, stubs, tm):
395         output_name = "test_output_name"
396
397         capture_stdout = cStringIO.StringIO()
398         exited = arvados_cwl.main(
399             ["--submit", "--no-wait", "--debug", "--output-name", output_name,
400              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
401             capture_stdout, sys.stderr, api_client=stubs.api)
402         self.assertEqual(exited, 0)
403
404         expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
405         expect_pipeline["components"]["cwl-runner"]["script_parameters"]["arv:output_name"] = output_name
406
407         stubs.api.pipeline_instances().create.assert_called_with(
408             body=JsonDiffMatcher(expect_pipeline))
409         self.assertEqual(capture_stdout.getvalue(),
410                          stubs.expect_pipeline_uuid + '\n')
411
412
413     @mock.patch("time.sleep")
414     @stubs
415     def test_submit_pipeline_name(self, stubs, tm):
416         capture_stdout = cStringIO.StringIO()
417         exited = arvados_cwl.main(
418             ["--submit", "--no-wait", "--debug", "--name=hello job 123",
419              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
420             capture_stdout, sys.stderr, api_client=stubs.api)
421         self.assertEqual(exited, 0)
422
423         expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
424         expect_pipeline["name"] = "hello job 123"
425
426         stubs.api.pipeline_instances().create.assert_called_with(
427             body=JsonDiffMatcher(expect_pipeline))
428         self.assertEqual(capture_stdout.getvalue(),
429                          stubs.expect_pipeline_uuid + '\n')
430
431     @mock.patch("time.sleep")
432     @stubs
433     def test_submit_output_tags(self, stubs, tm):
434         output_tags = "tag0,tag1,tag2"
435
436         capture_stdout = cStringIO.StringIO()
437         exited = arvados_cwl.main(
438             ["--submit", "--no-wait", "--debug", "--output-tags", output_tags,
439              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
440             capture_stdout, sys.stderr, api_client=stubs.api)
441         self.assertEqual(exited, 0)
442
443         expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
444         expect_pipeline["components"]["cwl-runner"]["script_parameters"]["arv:output_tags"] = output_tags
445
446         stubs.api.pipeline_instances().create.assert_called_with(
447             body=JsonDiffMatcher(expect_pipeline))
448         self.assertEqual(capture_stdout.getvalue(),
449                          stubs.expect_pipeline_uuid + '\n')
450
451     @mock.patch("time.sleep")
452     @stubs
453     def test_submit_with_project_uuid(self, stubs, tm):
454         project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
455
456         exited = arvados_cwl.main(
457             ["--submit", "--no-wait", "--debug",
458              "--project-uuid", project_uuid,
459              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
460             sys.stdout, sys.stderr, api_client=stubs.api)
461         self.assertEqual(exited, 0)
462
463         expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
464         expect_pipeline["owner_uuid"] = project_uuid
465         stubs.api.pipeline_instances().create.assert_called_with(
466             body=JsonDiffMatcher(expect_pipeline))
467
468     @stubs
469     def test_submit_container(self, stubs):
470         capture_stdout = cStringIO.StringIO()
471         try:
472             exited = arvados_cwl.main(
473                 ["--submit", "--no-wait", "--api=containers", "--debug",
474                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
475                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
476             self.assertEqual(exited, 0)
477         except:
478             logging.exception("")
479
480         stubs.api.collections().create.assert_has_calls([
481             mock.call(body=JsonDiffMatcher({
482                 'manifest_text':
483                 '. 5bcc9fe8f8d5992e6cf418dc7ce4dbb3+16 0:16:blub.txt\n',
484                 'replication_desired': None,
485                 'name': 'submit_tool.cwl dependencies',
486             }), ensure_unique_name=True),
487             mock.call(body=JsonDiffMatcher({
488                 'manifest_text':
489                 '. 979af1245a12a1fed634d4222473bfdc+16 0:16:blorp.txt\n',
490                 'replication_desired': None,
491                 'name': 'submit_wf.cwl input',
492             }), ensure_unique_name=True)])
493
494         expect_container = copy.deepcopy(stubs.expect_container_spec)
495         stubs.api.container_requests().create.assert_called_with(
496             body=JsonDiffMatcher(expect_container))
497         self.assertEqual(capture_stdout.getvalue(),
498                          stubs.expect_container_request_uuid + '\n')
499
500     @stubs
501     def test_submit_container_no_reuse(self, stubs):
502         capture_stdout = cStringIO.StringIO()
503         try:
504             exited = arvados_cwl.main(
505                 ["--submit", "--no-wait", "--api=containers", "--debug", "--disable-reuse",
506                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
507                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
508             self.assertEqual(exited, 0)
509         except:
510             logging.exception("")
511
512         expect_container = copy.deepcopy(stubs.expect_container_spec)
513         expect_container["command"] = [
514             'arvados-cwl-runner', '--local', '--api=containers',
515             '--no-log-timestamps', '--disable-validate',
516             '--eval-timeout=20', '--thread-count=4',
517             '--disable-reuse', '--debug', '--on-error=continue',
518             '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
519         expect_container["use_existing"] = False
520
521         stubs.api.container_requests().create.assert_called_with(
522             body=JsonDiffMatcher(expect_container))
523         self.assertEqual(capture_stdout.getvalue(),
524                          stubs.expect_container_request_uuid + '\n')
525
526
527     @stubs
528     def test_submit_container_reuse_disabled_by_workflow(self, stubs):
529         capture_stdout = cStringIO.StringIO()
530
531         exited = arvados_cwl.main(
532             ["--submit", "--no-wait", "--api=containers", "--debug",
533              "tests/wf/submit_wf_no_reuse.cwl", "tests/submit_test_job.json"],
534             capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
535         self.assertEqual(exited, 0)
536
537         expect_container = copy.deepcopy(stubs.expect_container_spec)
538         expect_container["command"] = [
539             'arvados-cwl-runner', '--local', '--api=containers',
540             '--no-log-timestamps', '--disable-validate',
541             '--eval-timeout=20', '--thread-count=4',
542             '--disable-reuse', '--debug', '--on-error=continue',
543             '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
544         expect_container["use_existing"] = False
545         expect_container["name"] = "submit_wf_no_reuse.cwl"
546         expect_container["mounts"]["/var/lib/cwl/workflow.json"]["content"]["$graph"][1]["hints"] = [
547             {
548                 "class": "http://arvados.org/cwl#ReuseRequirement",
549                 "enableReuse": False,
550             },
551         ]
552         expect_container["mounts"]["/var/lib/cwl/workflow.json"]["content"]["$graph"][0]["$namespaces"] = {
553             "arv": "http://arvados.org/cwl#",
554             "cwltool": "http://commonwl.org/cwltool#"
555         }
556
557         stubs.api.container_requests().create.assert_called_with(
558             body=JsonDiffMatcher(expect_container))
559         self.assertEqual(capture_stdout.getvalue(),
560                          stubs.expect_container_request_uuid + '\n')
561
562
563     @stubs
564     def test_submit_container_on_error(self, stubs):
565         capture_stdout = cStringIO.StringIO()
566         try:
567             exited = arvados_cwl.main(
568                 ["--submit", "--no-wait", "--api=containers", "--debug", "--on-error=stop",
569                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
570                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
571             self.assertEqual(exited, 0)
572         except:
573             logging.exception("")
574
575         expect_container = copy.deepcopy(stubs.expect_container_spec)
576         expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
577                                        '--no-log-timestamps', '--disable-validate',
578                                        '--eval-timeout=20', '--thread-count=4',
579                                        '--enable-reuse', '--debug', '--on-error=stop',
580                                        '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
581
582         stubs.api.container_requests().create.assert_called_with(
583             body=JsonDiffMatcher(expect_container))
584         self.assertEqual(capture_stdout.getvalue(),
585                          stubs.expect_container_request_uuid + '\n')
586
587     @stubs
588     def test_submit_container_output_name(self, stubs):
589         output_name = "test_output_name"
590
591         capture_stdout = cStringIO.StringIO()
592         try:
593             exited = arvados_cwl.main(
594                 ["--submit", "--no-wait", "--api=containers", "--debug", "--output-name", output_name,
595                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
596                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
597             self.assertEqual(exited, 0)
598         except:
599             logging.exception("")
600
601         expect_container = copy.deepcopy(stubs.expect_container_spec)
602         expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
603                                        '--no-log-timestamps', '--disable-validate',
604                                        '--eval-timeout=20', '--thread-count=4',
605                                        '--enable-reuse',
606                                        "--output-name="+output_name, '--debug', '--on-error=continue',
607                                        '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
608         expect_container["output_name"] = output_name
609
610         stubs.api.container_requests().create.assert_called_with(
611             body=JsonDiffMatcher(expect_container))
612         self.assertEqual(capture_stdout.getvalue(),
613                          stubs.expect_container_request_uuid + '\n')
614
615     @stubs
616     def test_submit_storage_classes(self, stubs):
617         capture_stdout = cStringIO.StringIO()
618         try:
619             exited = arvados_cwl.main(
620                 ["--debug", "--submit", "--no-wait", "--api=containers", "--storage-classes=foo",
621                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
622                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
623             self.assertEqual(exited, 0)
624         except:
625             logging.exception("")
626
627         expect_container = copy.deepcopy(stubs.expect_container_spec)
628         expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
629                                        '--no-log-timestamps', '--disable-validate',
630                                        '--eval-timeout=20', '--thread-count=4',
631                                        '--enable-reuse', "--debug",
632                                        "--storage-classes=foo", '--on-error=continue',
633                                        '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
634
635         stubs.api.container_requests().create.assert_called_with(
636             body=JsonDiffMatcher(expect_container))
637         self.assertEqual(capture_stdout.getvalue(),
638                          stubs.expect_container_request_uuid + '\n')
639
640     @mock.patch("arvados_cwl.task_queue.TaskQueue")
641     @mock.patch("arvados_cwl.arvworkflow.ArvadosWorkflow.job")
642     @mock.patch("arvados_cwl.ArvCwlRunner.make_output_collection", return_value = (None, None))
643     @stubs
644     def test_storage_classes_correctly_propagate_to_make_output_collection(self, stubs, make_output, job, tq):
645         def set_final_output(job_order, output_callback, runtimeContext):
646             output_callback("zzzzz-4zz18-zzzzzzzzzzzzzzzz", "success")
647             return []
648         job.side_effect = set_final_output
649
650         try:
651             exited = arvados_cwl.main(
652                 ["--debug", "--local", "--storage-classes=foo",
653                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
654                 sys.stdin, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
655             self.assertEqual(exited, 0)
656         except:
657             logging.exception("")
658
659         make_output.assert_called_with(u'Output of submit_wf.cwl', ['foo'], '', 'zzzzz-4zz18-zzzzzzzzzzzzzzzz')
660
661     @mock.patch("arvados_cwl.task_queue.TaskQueue")
662     @mock.patch("arvados_cwl.arvworkflow.ArvadosWorkflow.job")
663     @mock.patch("arvados_cwl.ArvCwlRunner.make_output_collection", return_value = (None, None))
664     @stubs
665     def test_default_storage_classes_correctly_propagate_to_make_output_collection(self, stubs, make_output, job, tq):
666         def set_final_output(job_order, output_callback, runtimeContext):
667             output_callback("zzzzz-4zz18-zzzzzzzzzzzzzzzz", "success")
668             return []
669         job.side_effect = set_final_output
670
671         try:
672             exited = arvados_cwl.main(
673                 ["--debug", "--local",
674                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
675                 sys.stdin, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
676             self.assertEqual(exited, 0)
677         except:
678             logging.exception("")
679
680         make_output.assert_called_with(u'Output of submit_wf.cwl', ['default'], '', 'zzzzz-4zz18-zzzzzzzzzzzzzzzz')
681
682     @stubs
683     def test_submit_container_output_ttl(self, stubs):
684         capture_stdout = cStringIO.StringIO()
685         try:
686             exited = arvados_cwl.main(
687                 ["--submit", "--no-wait", "--api=containers", "--debug", "--intermediate-output-ttl", "3600",
688                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
689                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
690             self.assertEqual(exited, 0)
691         except:
692             logging.exception("")
693
694         expect_container = copy.deepcopy(stubs.expect_container_spec)
695         expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
696                                        '--no-log-timestamps', '--disable-validate',
697                                        '--eval-timeout=20', '--thread-count=4',
698                                        '--enable-reuse', '--debug', '--on-error=continue',
699                                        "--intermediate-output-ttl=3600",
700                                        '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
701
702         stubs.api.container_requests().create.assert_called_with(
703             body=JsonDiffMatcher(expect_container))
704         self.assertEqual(capture_stdout.getvalue(),
705                          stubs.expect_container_request_uuid + '\n')
706
707     @stubs
708     def test_submit_container_trash_intermediate(self, stubs):
709         capture_stdout = cStringIO.StringIO()
710         try:
711             exited = arvados_cwl.main(
712                 ["--submit", "--no-wait", "--api=containers", "--debug", "--trash-intermediate",
713                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
714                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
715             self.assertEqual(exited, 0)
716         except:
717             logging.exception("")
718
719         expect_container = copy.deepcopy(stubs.expect_container_spec)
720         expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
721                                        '--no-log-timestamps', '--disable-validate',
722                                        '--eval-timeout=20', '--thread-count=4',
723                                        '--enable-reuse', '--debug', '--on-error=continue',
724                                        "--trash-intermediate",
725                                        '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
726
727         stubs.api.container_requests().create.assert_called_with(
728             body=JsonDiffMatcher(expect_container))
729         self.assertEqual(capture_stdout.getvalue(),
730                          stubs.expect_container_request_uuid + '\n')
731
732     @stubs
733     def test_submit_container_output_tags(self, stubs):
734         output_tags = "tag0,tag1,tag2"
735
736         capture_stdout = cStringIO.StringIO()
737         try:
738             exited = arvados_cwl.main(
739                 ["--submit", "--no-wait", "--api=containers", "--debug", "--output-tags", output_tags,
740                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
741                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
742             self.assertEqual(exited, 0)
743         except:
744             logging.exception("")
745
746         expect_container = copy.deepcopy(stubs.expect_container_spec)
747         expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
748                                        '--no-log-timestamps', '--disable-validate',
749                                        '--eval-timeout=20', '--thread-count=4',
750                                        '--enable-reuse',
751                                        "--output-tags="+output_tags, '--debug', '--on-error=continue',
752                                        '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
753
754         stubs.api.container_requests().create.assert_called_with(
755             body=JsonDiffMatcher(expect_container))
756         self.assertEqual(capture_stdout.getvalue(),
757                          stubs.expect_container_request_uuid + '\n')
758
759     @stubs
760     def test_submit_container_runner_ram(self, stubs):
761         capture_stdout = cStringIO.StringIO()
762         try:
763             exited = arvados_cwl.main(
764                 ["--submit", "--no-wait", "--api=containers", "--debug", "--submit-runner-ram=2048",
765                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
766                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
767             self.assertEqual(exited, 0)
768         except:
769             logging.exception("")
770
771         expect_container = copy.deepcopy(stubs.expect_container_spec)
772         expect_container["runtime_constraints"]["ram"] = 2048*1024*1024
773
774         stubs.api.container_requests().create.assert_called_with(
775             body=JsonDiffMatcher(expect_container))
776         self.assertEqual(capture_stdout.getvalue(),
777                          stubs.expect_container_request_uuid + '\n')
778
779     @mock.patch("arvados.collection.CollectionReader")
780     @mock.patch("time.sleep")
781     @stubs
782     def test_submit_file_keepref(self, stubs, tm, collectionReader):
783         capture_stdout = cStringIO.StringIO()
784         exited = arvados_cwl.main(
785             ["--submit", "--no-wait", "--api=containers", "--debug",
786              "tests/wf/submit_keepref_wf.cwl"],
787             capture_stdout, sys.stderr, api_client=stubs.api)
788         self.assertEqual(exited, 0)
789
790
791     @mock.patch("arvados.collection.CollectionReader")
792     @mock.patch("time.sleep")
793     @stubs
794     def test_submit_keepref(self, stubs, tm, reader):
795         capture_stdout = cStringIO.StringIO()
796
797         with open("tests/wf/expect_arvworkflow.cwl") as f:
798             reader().open().__enter__().read.return_value = f.read()
799
800         exited = arvados_cwl.main(
801             ["--submit", "--no-wait", "--api=containers", "--debug",
802              "keep:99999999999999999999999999999994+99/expect_arvworkflow.cwl#main", "-x", "XxX"],
803             capture_stdout, sys.stderr, api_client=stubs.api)
804         self.assertEqual(exited, 0)
805
806         expect_container = {
807             'priority': 500,
808             'mounts': {
809                 '/var/spool/cwl': {
810                     'writable': True,
811                     'kind': 'collection'
812                 },
813                 'stdout': {
814                     'path': '/var/spool/cwl/cwl.output.json',
815                     'kind': 'file'
816                 },
817                 '/var/lib/cwl/workflow': {
818                     'portable_data_hash': '99999999999999999999999999999994+99',
819                     'kind': 'collection'
820                 },
821                 '/var/lib/cwl/cwl.input.json': {
822                     'content': {
823                         'x': 'XxX'
824                     },
825                     'kind': 'json'
826                 }
827             }, 'state': 'Committed',
828             'output_path': '/var/spool/cwl',
829             'name': 'expect_arvworkflow.cwl#main',
830             'container_image': 'arvados/jobs:'+arvados_cwl.__version__,
831             'command': ['arvados-cwl-runner', '--local', '--api=containers',
832                         '--no-log-timestamps', '--disable-validate',
833                         '--eval-timeout=20', '--thread-count=4',
834                         '--enable-reuse', '--debug', '--on-error=continue',
835                         '/var/lib/cwl/workflow/expect_arvworkflow.cwl#main', '/var/lib/cwl/cwl.input.json'],
836             'cwd': '/var/spool/cwl',
837             'runtime_constraints': {
838                 'API': True,
839                 'vcpus': 1,
840                 'ram': 1073741824
841             },
842             'use_existing': True,
843             'properties': {},
844             'secret_mounts': {}
845         }
846
847         stubs.api.container_requests().create.assert_called_with(
848             body=JsonDiffMatcher(expect_container))
849         self.assertEqual(capture_stdout.getvalue(),
850                          stubs.expect_container_request_uuid + '\n')
851
852
853     @mock.patch("arvados.collection.CollectionReader")
854     @mock.patch("time.sleep")
855     @stubs
856     def test_submit_jobs_keepref(self, stubs, tm, reader):
857         capture_stdout = cStringIO.StringIO()
858
859         with open("tests/wf/expect_arvworkflow.cwl") as f:
860             reader().open().__enter__().read.return_value = f.read()
861
862         exited = arvados_cwl.main(
863             ["--submit", "--no-wait", "--api=jobs", "--debug",
864              "keep:99999999999999999999999999999994+99/expect_arvworkflow.cwl#main", "-x", "XxX"],
865             capture_stdout, sys.stderr, api_client=stubs.api)
866         self.assertEqual(exited, 0)
867
868         expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
869         expect_pipeline["components"]["cwl-runner"]["script_parameters"]["x"] = "XxX"
870         del expect_pipeline["components"]["cwl-runner"]["script_parameters"]["y"]
871         del expect_pipeline["components"]["cwl-runner"]["script_parameters"]["z"]
872         expect_pipeline["components"]["cwl-runner"]["script_parameters"]["cwl:tool"] = "99999999999999999999999999999994+99/expect_arvworkflow.cwl#main"
873         expect_pipeline["name"] = "expect_arvworkflow.cwl#main"
874         stubs.api.pipeline_instances().create.assert_called_with(
875             body=JsonDiffMatcher(expect_pipeline))
876
877     @mock.patch("time.sleep")
878     @stubs
879     def test_submit_arvworkflow(self, stubs, tm):
880         capture_stdout = cStringIO.StringIO()
881
882         with open("tests/wf/expect_arvworkflow.cwl") as f:
883             stubs.api.workflows().get().execute.return_value = {"definition": f.read(), "name": "a test workflow"}
884
885         exited = arvados_cwl.main(
886             ["--submit", "--no-wait", "--api=containers", "--debug",
887              "962eh-7fd4e-gkbzl62qqtfig37", "-x", "XxX"],
888             capture_stdout, sys.stderr, api_client=stubs.api)
889         self.assertEqual(exited, 0)
890
891         expect_container = {
892             'priority': 500,
893             'mounts': {
894                 '/var/spool/cwl': {
895                     'writable': True,
896                     'kind': 'collection'
897                 },
898                 'stdout': {
899                     'path': '/var/spool/cwl/cwl.output.json',
900                     'kind': 'file'
901                 },
902                 '/var/lib/cwl/workflow.json': {
903                     'kind': 'json',
904                     'content': {
905                         'cwlVersion': 'v1.0',
906                         '$graph': [
907                             {
908                                 'id': '#main',
909                                 'inputs': [
910                                     {'type': 'string', 'id': '#main/x'}
911                                 ],
912                                 'steps': [
913                                     {'in': [{'source': '#main/x', 'id': '#main/step1/x'}],
914                                      'run': '#submit_tool.cwl',
915                                      'id': '#main/step1',
916                                      'out': []}
917                                 ],
918                                 'class': 'Workflow',
919                                 'outputs': []
920                             },
921                             {
922                                 'inputs': [
923                                     {
924                                         'inputBinding': {'position': 1},
925                                         'type': 'string',
926                                         'id': '#submit_tool.cwl/x'}
927                                 ],
928                                 'requirements': [
929                                     {'dockerPull': 'debian:8', 'class': 'DockerRequirement'}
930                                 ],
931                                 'id': '#submit_tool.cwl',
932                                 'outputs': [],
933                                 'baseCommand': 'cat',
934                                 'class': 'CommandLineTool'
935                             }
936                         ]
937                     }
938                 },
939                 '/var/lib/cwl/cwl.input.json': {
940                     'content': {
941                         'x': 'XxX'
942                     },
943                     'kind': 'json'
944                 }
945             }, 'state': 'Committed',
946             'output_path': '/var/spool/cwl',
947             'name': 'a test workflow',
948             'container_image': 'arvados/jobs:'+arvados_cwl.__version__,
949             'command': ['arvados-cwl-runner', '--local', '--api=containers',
950                         '--no-log-timestamps', '--disable-validate',
951                         '--eval-timeout=20', '--thread-count=4',
952                         '--enable-reuse', '--debug', '--on-error=continue',
953                         '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json'],
954             'cwd': '/var/spool/cwl',
955             'runtime_constraints': {
956                 'API': True,
957                 'vcpus': 1,
958                 'ram': 1073741824
959             },
960             'use_existing': True,
961             'properties': {
962                 "template_uuid": "962eh-7fd4e-gkbzl62qqtfig37"
963             },
964             'secret_mounts': {}
965         }
966
967         stubs.api.container_requests().create.assert_called_with(
968             body=JsonDiffMatcher(expect_container))
969         self.assertEqual(capture_stdout.getvalue(),
970                          stubs.expect_container_request_uuid + '\n')
971
972
973     @stubs
974     def test_submit_container_name(self, stubs):
975         capture_stdout = cStringIO.StringIO()
976         try:
977             exited = arvados_cwl.main(
978                 ["--submit", "--no-wait", "--api=containers", "--debug", "--name=hello container 123",
979                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
980                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
981             self.assertEqual(exited, 0)
982         except:
983             logging.exception("")
984
985         expect_container = copy.deepcopy(stubs.expect_container_spec)
986         expect_container["name"] = "hello container 123"
987
988         stubs.api.container_requests().create.assert_called_with(
989             body=JsonDiffMatcher(expect_container))
990         self.assertEqual(capture_stdout.getvalue(),
991                          stubs.expect_container_request_uuid + '\n')
992
993
994     @stubs
995     def test_submit_container_project(self, stubs):
996         project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
997         capture_stdout = cStringIO.StringIO()
998         try:
999             exited = arvados_cwl.main(
1000                 ["--submit", "--no-wait", "--api=containers", "--debug", "--project-uuid="+project_uuid,
1001                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1002                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1003             self.assertEqual(exited, 0)
1004         except:
1005             logging.exception("")
1006
1007         expect_container = copy.deepcopy(stubs.expect_container_spec)
1008         expect_container["owner_uuid"] = project_uuid
1009         expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
1010                                        '--no-log-timestamps', '--disable-validate',
1011                                        "--eval-timeout=20", "--thread-count=4",
1012                                        '--enable-reuse', '--debug', '--on-error=continue',
1013                                        '--project-uuid='+project_uuid,
1014                                        '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
1015
1016         stubs.api.container_requests().create.assert_called_with(
1017             body=JsonDiffMatcher(expect_container))
1018         self.assertEqual(capture_stdout.getvalue(),
1019                          stubs.expect_container_request_uuid + '\n')
1020
1021     @stubs
1022     def test_submit_container_eval_timeout(self, stubs):
1023         project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
1024         capture_stdout = cStringIO.StringIO()
1025         try:
1026             exited = arvados_cwl.main(
1027                 ["--submit", "--no-wait", "--api=containers", "--debug", "--eval-timeout=60",
1028                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1029                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1030             self.assertEqual(exited, 0)
1031         except:
1032             logging.exception("")
1033
1034         expect_container = copy.deepcopy(stubs.expect_container_spec)
1035         expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
1036                                        '--no-log-timestamps', '--disable-validate',
1037                                        '--eval-timeout=60.0', '--thread-count=4',
1038                                        '--enable-reuse', '--debug', '--on-error=continue',
1039                                        '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
1040
1041         stubs.api.container_requests().create.assert_called_with(
1042             body=JsonDiffMatcher(expect_container))
1043         self.assertEqual(capture_stdout.getvalue(),
1044                          stubs.expect_container_request_uuid + '\n')
1045
1046
1047     @stubs
1048     def test_submit_container_thread_count(self, stubs):
1049         project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
1050         capture_stdout = cStringIO.StringIO()
1051         try:
1052             exited = arvados_cwl.main(
1053                 ["--submit", "--no-wait", "--api=containers", "--debug", "--thread-count=20",
1054                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1055                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1056             self.assertEqual(exited, 0)
1057         except:
1058             logging.exception("")
1059
1060         expect_container = copy.deepcopy(stubs.expect_container_spec)
1061         expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
1062                                        '--no-log-timestamps', '--disable-validate',
1063                                        '--eval-timeout=20', '--thread-count=20',
1064                                        '--enable-reuse', '--debug', '--on-error=continue',
1065                                        '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
1066
1067         stubs.api.container_requests().create.assert_called_with(
1068             body=JsonDiffMatcher(expect_container))
1069         self.assertEqual(capture_stdout.getvalue(),
1070                          stubs.expect_container_request_uuid + '\n')
1071
1072
1073     @stubs
1074     def test_submit_job_runner_image(self, stubs):
1075         capture_stdout = cStringIO.StringIO()
1076         try:
1077             exited = arvados_cwl.main(
1078                 ["--submit", "--no-wait", "--api=jobs", "--debug", "--submit-runner-image=arvados/jobs:123",
1079                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1080                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1081             self.assertEqual(exited, 0)
1082         except:
1083             logging.exception("")
1084
1085         stubs.expect_pipeline_instance["components"]["cwl-runner"]["runtime_constraints"]["docker_image"] = "arvados/jobs:123"
1086
1087         expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
1088         stubs.api.pipeline_instances().create.assert_called_with(
1089             body=JsonDiffMatcher(expect_pipeline))
1090         self.assertEqual(capture_stdout.getvalue(),
1091                          stubs.expect_pipeline_uuid + '\n')
1092
1093     @stubs
1094     def test_submit_container_runner_image(self, stubs):
1095         capture_stdout = cStringIO.StringIO()
1096         try:
1097             exited = arvados_cwl.main(
1098                 ["--submit", "--no-wait", "--api=containers", "--debug", "--submit-runner-image=arvados/jobs:123",
1099                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1100                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1101             self.assertEqual(exited, 0)
1102         except:
1103             logging.exception("")
1104
1105         stubs.expect_container_spec["container_image"] = "arvados/jobs:123"
1106
1107         expect_container = copy.deepcopy(stubs.expect_container_spec)
1108         stubs.api.container_requests().create.assert_called_with(
1109             body=JsonDiffMatcher(expect_container))
1110         self.assertEqual(capture_stdout.getvalue(),
1111                          stubs.expect_container_request_uuid + '\n')
1112
1113     @stubs
1114     def test_submit_priority(self, stubs):
1115         capture_stdout = cStringIO.StringIO()
1116         try:
1117             exited = arvados_cwl.main(
1118                 ["--submit", "--no-wait", "--api=containers", "--debug", "--priority=669",
1119                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1120                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1121             self.assertEqual(exited, 0)
1122         except:
1123             logging.exception("")
1124
1125         stubs.expect_container_spec["priority"] = 669
1126
1127         expect_container = copy.deepcopy(stubs.expect_container_spec)
1128         stubs.api.container_requests().create.assert_called_with(
1129             body=JsonDiffMatcher(expect_container))
1130         self.assertEqual(capture_stdout.getvalue(),
1131                          stubs.expect_container_request_uuid + '\n')
1132
1133
1134     @mock.patch("arvados.commands.keepdocker.find_one_image_hash")
1135     @mock.patch("cwltool.docker.DockerCommandLineJob.get_image")
1136     @mock.patch("arvados.api")
1137     def test_arvados_jobs_image(self, api, get_image, find_one_image_hash):
1138         arvrunner = mock.MagicMock()
1139         arvrunner.project_uuid = ""
1140         api.return_value = mock.MagicMock()
1141         arvrunner.api = api.return_value
1142         arvrunner.api.links().list().execute.side_effect = ({"items": [{"created_at": "",
1143                                                                         "head_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzb",
1144                                                                         "link_class": "docker_image_repo+tag",
1145                                                                         "name": "arvados/jobs:"+arvados_cwl.__version__,
1146                                                                         "owner_uuid": "",
1147                                                                         "properties": {"image_timestamp": ""}}], "items_available": 1, "offset": 0},
1148                                                             {"items": [{"created_at": "",
1149                                                                         "head_uuid": "",
1150                                                                         "link_class": "docker_image_hash",
1151                                                                         "name": "123456",
1152                                                                         "owner_uuid": "",
1153                                                                         "properties": {"image_timestamp": ""}}], "items_available": 1, "offset": 0}
1154         )
1155         find_one_image_hash.return_value = "123456"
1156
1157         arvrunner.api.collections().list().execute.side_effect = ({"items": [{"uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzb",
1158                                                                               "owner_uuid": "",
1159                                                                               "manifest_text": "",
1160                                                                               "properties": ""
1161                                                                           }], "items_available": 1, "offset": 0},)
1162         arvrunner.api.collections().create().execute.return_value = {"uuid": ""}
1163         self.assertEqual("arvados/jobs:"+arvados_cwl.__version__,
1164                          arvados_cwl.runner.arvados_jobs_image(arvrunner, "arvados/jobs:"+arvados_cwl.__version__))
1165
1166     @stubs
1167     def test_submit_secrets(self, stubs):
1168         capture_stdout = cStringIO.StringIO()
1169         try:
1170             exited = arvados_cwl.main(
1171                 ["--submit", "--no-wait", "--api=containers", "--debug",
1172                  "tests/wf/secret_wf.cwl", "tests/secret_test_job.yml"],
1173                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1174             self.assertEqual(exited, 0)
1175         except:
1176             logging.exception("")
1177
1178
1179         expect_container = {
1180             "command": [
1181                 "arvados-cwl-runner",
1182                 "--local",
1183                 "--api=containers",
1184                 "--no-log-timestamps",
1185                 "--disable-validate",
1186                 "--eval-timeout=20",
1187                 '--thread-count=4',
1188                 "--enable-reuse",
1189                 '--debug',
1190                 "--on-error=continue",
1191                 "/var/lib/cwl/workflow.json#main",
1192                 "/var/lib/cwl/cwl.input.json"
1193             ],
1194             "container_image": "arvados/jobs:"+arvados_cwl.__version__,
1195             "cwd": "/var/spool/cwl",
1196             "mounts": {
1197                 "/var/lib/cwl/cwl.input.json": {
1198                     "content": {
1199                         "pw": {
1200                             "$include": "/secrets/s0"
1201                         }
1202                     },
1203                     "kind": "json"
1204                 },
1205                 "/var/lib/cwl/workflow.json": {
1206                     "content": {
1207                         "$graph": [
1208                             {
1209                                 "$namespaces": {
1210                                     "cwltool": "http://commonwl.org/cwltool#"
1211                                 },
1212                                 "arguments": [
1213                                     "md5sum",
1214                                     "example.conf"
1215                                 ],
1216                                 "class": "CommandLineTool",
1217                                 "hints": [
1218                                     {
1219                                         "class": "http://commonwl.org/cwltool#Secrets",
1220                                         "secrets": [
1221                                             "#secret_job.cwl/pw"
1222                                         ]
1223                                     }
1224                                 ],
1225                                 "id": "#secret_job.cwl",
1226                                 "inputs": [
1227                                     {
1228                                         "id": "#secret_job.cwl/pw",
1229                                         "type": "string"
1230                                     }
1231                                 ],
1232                                 "outputs": [
1233                                     {
1234                                         "id": "#secret_job.cwl/out",
1235                                         "type": "stdout"
1236                                     }
1237                                 ],
1238                                 "stdout": "hashed_example.txt",
1239                                 "requirements": [
1240                                     {
1241                                         "class": "InitialWorkDirRequirement",
1242                                         "listing": [
1243                                             {
1244                                                 "entry": "username: user\npassword: $(inputs.pw)\n",
1245                                                 "entryname": "example.conf"
1246                                             }
1247                                         ]
1248                                     }
1249                                 ]
1250                             },
1251                             {
1252                                 "class": "Workflow",
1253                                 "hints": [
1254                                     {
1255                                         "class": "DockerRequirement",
1256                                         "dockerPull": "debian:8"
1257                                     },
1258                                     {
1259                                         "class": "http://commonwl.org/cwltool#Secrets",
1260                                         "secrets": [
1261                                             "#main/pw"
1262                                         ]
1263                                     }
1264                                 ],
1265                                 "id": "#main",
1266                                 "inputs": [
1267                                     {
1268                                         "id": "#main/pw",
1269                                         "type": "string"
1270                                     }
1271                                 ],
1272                                 "outputs": [
1273                                     {
1274                                         "id": "#main/out",
1275                                         "outputSource": "#main/step1/out",
1276                                         "type": "File"
1277                                     }
1278                                 ],
1279                                 "steps": [
1280                                     {
1281                                         "id": "#main/step1",
1282                                         "in": [
1283                                             {
1284                                                 "id": "#main/step1/pw",
1285                                                 "source": "#main/pw"
1286                                             }
1287                                         ],
1288                                         "out": [
1289                                             "#main/step1/out"
1290                                         ],
1291                                         "run": "#secret_job.cwl"
1292                                     }
1293                                 ]
1294                             }
1295                         ],
1296                         "cwlVersion": "v1.0"
1297                     },
1298                     "kind": "json"
1299                 },
1300                 "/var/spool/cwl": {
1301                     "kind": "collection",
1302                     "writable": True
1303                 },
1304                 "stdout": {
1305                     "kind": "file",
1306                     "path": "/var/spool/cwl/cwl.output.json"
1307                 }
1308             },
1309             "name": "secret_wf.cwl",
1310             "output_path": "/var/spool/cwl",
1311             "priority": 500,
1312             "properties": {},
1313             "runtime_constraints": {
1314                 "API": True,
1315                 "ram": 1073741824,
1316                 "vcpus": 1
1317             },
1318             "secret_mounts": {
1319                 "/secrets/s0": {
1320                     "content": "blorp",
1321                     "kind": "text"
1322                 }
1323             },
1324             "state": "Committed",
1325             "use_existing": True
1326         }
1327
1328         stubs.api.container_requests().create.assert_called_with(
1329             body=JsonDiffMatcher(expect_container))
1330         self.assertEqual(capture_stdout.getvalue(),
1331                          stubs.expect_container_request_uuid + '\n')
1332
1333     @stubs
1334     def test_submit_request_uuid(self, stubs):
1335         stubs.expect_container_request_uuid = "zzzzz-xvhdp-yyyyyyyyyyyyyyy"
1336
1337         stubs.api.container_requests().update().execute.return_value = {
1338             "uuid": stubs.expect_container_request_uuid,
1339             "container_uuid": "zzzzz-dz642-zzzzzzzzzzzzzzz",
1340             "state": "Queued"
1341         }
1342
1343         capture_stdout = cStringIO.StringIO()
1344         try:
1345             exited = arvados_cwl.main(
1346                 ["--submit", "--no-wait", "--api=containers", "--debug", "--submit-request-uuid=zzzzz-xvhdp-yyyyyyyyyyyyyyy",
1347                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1348                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1349             self.assertEqual(exited, 0)
1350         except:
1351             logging.exception("")
1352
1353         stubs.api.container_requests().update.assert_called_with(
1354             uuid="zzzzz-xvhdp-yyyyyyyyyyyyyyy", body=JsonDiffMatcher(stubs.expect_container_spec))
1355         self.assertEqual(capture_stdout.getvalue(),
1356                          stubs.expect_container_request_uuid + '\n')
1357
1358
1359 class TestCreateTemplate(unittest.TestCase):
1360     existing_template_uuid = "zzzzz-d1hrv-validworkfloyml"
1361
1362     def _adjust_script_params(self, expect_component):
1363         expect_component['script_parameters']['x'] = {
1364             'dataclass': 'File',
1365             'required': True,
1366             'type': 'File',
1367             'value': '169f39d466a5438ac4a90e779bf750c7+53/blorp.txt',
1368         }
1369         expect_component['script_parameters']['y'] = {
1370             'dataclass': 'Collection',
1371             'required': True,
1372             'type': 'Directory',
1373             'value': '99999999999999999999999999999998+99',
1374         }
1375         expect_component['script_parameters']['z'] = {
1376             'dataclass': 'Collection',
1377             'required': True,
1378             'type': 'Directory',
1379         }
1380
1381     @stubs
1382     def test_create(self, stubs):
1383         project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
1384
1385         capture_stdout = cStringIO.StringIO()
1386
1387         exited = arvados_cwl.main(
1388             ["--create-workflow", "--debug",
1389              "--api=jobs",
1390              "--project-uuid", project_uuid,
1391              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1392             capture_stdout, sys.stderr, api_client=stubs.api)
1393         self.assertEqual(exited, 0)
1394
1395         stubs.api.pipeline_instances().create.refute_called()
1396         stubs.api.jobs().create.refute_called()
1397
1398         expect_component = copy.deepcopy(stubs.expect_job_spec)
1399         self._adjust_script_params(expect_component)
1400         expect_template = {
1401             "components": {
1402                 "submit_wf.cwl": expect_component,
1403             },
1404             "name": "submit_wf.cwl",
1405             "owner_uuid": project_uuid,
1406         }
1407         stubs.api.pipeline_templates().create.assert_called_with(
1408             body=JsonDiffMatcher(expect_template), ensure_unique_name=True)
1409
1410         self.assertEqual(capture_stdout.getvalue(),
1411                          stubs.expect_pipeline_template_uuid + '\n')
1412
1413
1414     @stubs
1415     def test_create_name(self, stubs):
1416         project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
1417
1418         capture_stdout = cStringIO.StringIO()
1419
1420         exited = arvados_cwl.main(
1421             ["--create-workflow", "--debug",
1422              "--project-uuid", project_uuid,
1423              "--api=jobs",
1424              "--name", "testing 123",
1425              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1426             capture_stdout, sys.stderr, api_client=stubs.api)
1427         self.assertEqual(exited, 0)
1428
1429         stubs.api.pipeline_instances().create.refute_called()
1430         stubs.api.jobs().create.refute_called()
1431
1432         expect_component = copy.deepcopy(stubs.expect_job_spec)
1433         self._adjust_script_params(expect_component)
1434         expect_template = {
1435             "components": {
1436                 "testing 123": expect_component,
1437             },
1438             "name": "testing 123",
1439             "owner_uuid": project_uuid,
1440         }
1441         stubs.api.pipeline_templates().create.assert_called_with(
1442             body=JsonDiffMatcher(expect_template), ensure_unique_name=True)
1443
1444         self.assertEqual(capture_stdout.getvalue(),
1445                          stubs.expect_pipeline_template_uuid + '\n')
1446
1447
1448     @stubs
1449     def test_update_name(self, stubs):
1450         project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
1451
1452         capture_stdout = cStringIO.StringIO()
1453
1454         exited = arvados_cwl.main(
1455             ["--update-workflow", self.existing_template_uuid,
1456              "--debug",
1457              "--project-uuid", project_uuid,
1458              "--api=jobs",
1459              "--name", "testing 123",
1460              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1461             capture_stdout, sys.stderr, api_client=stubs.api)
1462         self.assertEqual(exited, 0)
1463
1464         stubs.api.pipeline_instances().create.refute_called()
1465         stubs.api.jobs().create.refute_called()
1466
1467         expect_component = copy.deepcopy(stubs.expect_job_spec)
1468         self._adjust_script_params(expect_component)
1469         expect_template = {
1470             "components": {
1471                 "testing 123": expect_component,
1472             },
1473             "name": "testing 123",
1474             "owner_uuid": project_uuid,
1475         }
1476         stubs.api.pipeline_templates().create.refute_called()
1477         stubs.api.pipeline_templates().update.assert_called_with(
1478             body=JsonDiffMatcher(expect_template), uuid=self.existing_template_uuid)
1479
1480         self.assertEqual(capture_stdout.getvalue(),
1481                          self.existing_template_uuid + '\n')
1482
1483
1484 class TestCreateWorkflow(unittest.TestCase):
1485     existing_workflow_uuid = "zzzzz-7fd4e-validworkfloyml"
1486     expect_workflow = StripYAMLComments(
1487         open("tests/wf/expect_packed.cwl").read())
1488
1489     @stubs
1490     def test_create(self, stubs):
1491         project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
1492
1493         capture_stdout = cStringIO.StringIO()
1494
1495         exited = arvados_cwl.main(
1496             ["--create-workflow", "--debug",
1497              "--api=containers",
1498              "--project-uuid", project_uuid,
1499              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1500             capture_stdout, sys.stderr, api_client=stubs.api)
1501         self.assertEqual(exited, 0)
1502
1503         stubs.api.pipeline_templates().create.refute_called()
1504         stubs.api.container_requests().create.refute_called()
1505
1506         body = {
1507             "workflow": {
1508                 "owner_uuid": project_uuid,
1509                 "name": "submit_wf.cwl",
1510                 "description": "",
1511                 "definition": self.expect_workflow,
1512             }
1513         }
1514         stubs.api.workflows().create.assert_called_with(
1515             body=JsonDiffMatcher(body))
1516
1517         self.assertEqual(capture_stdout.getvalue(),
1518                          stubs.expect_workflow_uuid + '\n')
1519
1520
1521     @stubs
1522     def test_create_name(self, stubs):
1523         project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
1524
1525         capture_stdout = cStringIO.StringIO()
1526
1527         exited = arvados_cwl.main(
1528             ["--create-workflow", "--debug",
1529              "--api=containers",
1530              "--project-uuid", project_uuid,
1531              "--name", "testing 123",
1532              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1533             capture_stdout, sys.stderr, api_client=stubs.api)
1534         self.assertEqual(exited, 0)
1535
1536         stubs.api.pipeline_templates().create.refute_called()
1537         stubs.api.container_requests().create.refute_called()
1538
1539         body = {
1540             "workflow": {
1541                 "owner_uuid": project_uuid,
1542                 "name": "testing 123",
1543                 "description": "",
1544                 "definition": self.expect_workflow,
1545             }
1546         }
1547         stubs.api.workflows().create.assert_called_with(
1548             body=JsonDiffMatcher(body))
1549
1550         self.assertEqual(capture_stdout.getvalue(),
1551                          stubs.expect_workflow_uuid + '\n')
1552
1553     @stubs
1554     def test_incompatible_api(self, stubs):
1555         capture_stderr = cStringIO.StringIO()
1556         logging.getLogger('arvados.cwl-runner').addHandler(
1557             logging.StreamHandler(capture_stderr))
1558
1559         exited = arvados_cwl.main(
1560             ["--update-workflow", self.existing_workflow_uuid,
1561              "--api=jobs",
1562              "--debug",
1563              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1564             sys.stderr, sys.stderr, api_client=stubs.api)
1565         self.assertEqual(exited, 1)
1566         self.assertRegexpMatches(
1567             capture_stderr.getvalue(),
1568             "--update-workflow arg '{}' uses 'containers' API, but --api='jobs' specified".format(self.existing_workflow_uuid))
1569
1570     @stubs
1571     def test_update(self, stubs):
1572         capture_stdout = cStringIO.StringIO()
1573
1574         exited = arvados_cwl.main(
1575             ["--update-workflow", self.existing_workflow_uuid,
1576              "--debug",
1577              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1578             capture_stdout, sys.stderr, api_client=stubs.api)
1579         self.assertEqual(exited, 0)
1580
1581         body = {
1582             "workflow": {
1583                 "name": "submit_wf.cwl",
1584                 "description": "",
1585                 "definition": self.expect_workflow,
1586             }
1587         }
1588         stubs.api.workflows().update.assert_called_with(
1589             uuid=self.existing_workflow_uuid,
1590             body=JsonDiffMatcher(body))
1591         self.assertEqual(capture_stdout.getvalue(),
1592                          self.existing_workflow_uuid + '\n')
1593
1594
1595     @stubs
1596     def test_update_name(self, stubs):
1597         capture_stdout = cStringIO.StringIO()
1598
1599         exited = arvados_cwl.main(
1600             ["--update-workflow", self.existing_workflow_uuid,
1601              "--debug", "--name", "testing 123",
1602              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1603             capture_stdout, sys.stderr, api_client=stubs.api)
1604         self.assertEqual(exited, 0)
1605
1606         body = {
1607             "workflow": {
1608                 "name": "testing 123",
1609                 "description": "",
1610                 "definition": self.expect_workflow,
1611             }
1612         }
1613         stubs.api.workflows().update.assert_called_with(
1614             uuid=self.existing_workflow_uuid,
1615             body=JsonDiffMatcher(body))
1616         self.assertEqual(capture_stdout.getvalue(),
1617                          self.existing_workflow_uuid + '\n')
1618
1619
1620     @stubs
1621     def test_create_collection_per_tool(self, stubs):
1622         project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
1623
1624         capture_stdout = cStringIO.StringIO()
1625
1626         exited = arvados_cwl.main(
1627             ["--create-workflow", "--debug",
1628              "--api=containers",
1629              "--project-uuid", project_uuid,
1630              "tests/collection_per_tool/collection_per_tool.cwl"],
1631             capture_stdout, sys.stderr, api_client=stubs.api)
1632         self.assertEqual(exited, 0)
1633
1634         toolfile = "tests/collection_per_tool/collection_per_tool_packed.cwl"
1635         expect_workflow = StripYAMLComments(open(toolfile).read())
1636
1637         body = {
1638             "workflow": {
1639                 "owner_uuid": project_uuid,
1640                 "name": "collection_per_tool.cwl",
1641                 "description": "",
1642                 "definition": expect_workflow,
1643             }
1644         }
1645         stubs.api.workflows().create.assert_called_with(
1646             body=JsonDiffMatcher(body))
1647
1648         self.assertEqual(capture_stdout.getvalue(),
1649                          stubs.expect_workflow_uuid + '\n')
1650
1651 class TestTemplateInputs(unittest.TestCase):
1652     expect_template = {
1653         "components": {
1654             "inputs_test.cwl": {
1655                 'runtime_constraints': {
1656                     'docker_image': 'arvados/jobs:'+arvados_cwl.__version__,
1657                     'min_ram_mb_per_node': 1024
1658                 },
1659                 'script_parameters': {
1660                     'cwl:tool':
1661                     '6c5ee1cd606088106d9f28367cde1e41+60/workflow.cwl#main',
1662                     'optionalFloatInput': None,
1663                     'fileInput': {
1664                         'type': 'File',
1665                         'dataclass': 'File',
1666                         'required': True,
1667                         'title': "It's a file; we expect to find some characters in it.",
1668                         'description': 'If there were anything further to say, it would be said here,\nor here.'
1669                     },
1670                     'floatInput': {
1671                         'type': 'float',
1672                         'dataclass': 'number',
1673                         'required': True,
1674                         'title': 'Floats like a duck',
1675                         'default': 0.1,
1676                         'value': 0.1,
1677                     },
1678                     'optionalFloatInput': {
1679                         'type': ['null', 'float'],
1680                         'dataclass': 'number',
1681                         'required': False,
1682                     },
1683                     'boolInput': {
1684                         'type': 'boolean',
1685                         'dataclass': 'boolean',
1686                         'required': True,
1687                         'title': 'True or false?',
1688                     },
1689                 },
1690                 'repository': 'arvados',
1691                 'script_version': 'master',
1692                 'minimum_script_version': '570509ab4d2ef93d870fd2b1f2eab178afb1bad9',
1693                 'script': 'cwl-runner',
1694             },
1695         },
1696         "name": "inputs_test.cwl",
1697     }
1698
1699     @stubs
1700     def test_inputs_empty(self, stubs):
1701         exited = arvados_cwl.main(
1702             ["--create-template",
1703              "tests/wf/inputs_test.cwl", "tests/order/empty_order.json"],
1704             cStringIO.StringIO(), sys.stderr, api_client=stubs.api)
1705         self.assertEqual(exited, 0)
1706
1707         stubs.api.pipeline_templates().create.assert_called_with(
1708             body=JsonDiffMatcher(self.expect_template), ensure_unique_name=True)
1709
1710     @stubs
1711     def test_inputs(self, stubs):
1712         exited = arvados_cwl.main(
1713             ["--create-template",
1714              "tests/wf/inputs_test.cwl", "tests/order/inputs_test_order.json"],
1715             cStringIO.StringIO(), sys.stderr, api_client=stubs.api)
1716         self.assertEqual(exited, 0)
1717
1718         expect_template = copy.deepcopy(self.expect_template)
1719         params = expect_template[
1720             "components"]["inputs_test.cwl"]["script_parameters"]
1721         params["fileInput"]["value"] = '169f39d466a5438ac4a90e779bf750c7+53/blorp.txt'
1722         params["cwl:tool"] = '6c5ee1cd606088106d9f28367cde1e41+60/workflow.cwl#main'
1723         params["floatInput"]["value"] = 1.234
1724         params["boolInput"]["value"] = True
1725
1726         stubs.api.pipeline_templates().create.assert_called_with(
1727             body=JsonDiffMatcher(expect_template), ensure_unique_name=True)