def _load_single_workflow(self, app_label, version_slug, force): try: load_workflow(app_label, version_slug, force=force) except WorkflowError as e: self.stderr.write( 'An error occurred while loading the workflow: {}' .format(e))
def handle(self, *args, **options): try: load_workflow(options['app_label'], version_slug=options['version_slug'], force=options['force']) except WorkflowError as e: self.stderr.write( 'An error occurred while loading the workflow: {}'.format(e))
def test_loadworkflowsampledata(self): """ Ensure that workflow sample data loads correctly. """ # Load the test workflow load_workflow(TEST_WORKFLOW, VERSION_1) assert_test_dir_workflow_loaded(self) assert_test_dir_v1_loaded(self) # Calling the command with an invalid version argument should fail. invalid_args = ( VERSION_1, # Workflow slug omitted TEST_WORKFLOW, # Version slug omitted '{}/{}/'.format(TEST_WORKFLOW, VERSION_1), # Too many slashes '{}.{}'.format(TEST_WORKFLOW, VERSION_1), # Wrong separator ) for invalid_arg in invalid_args: invalid_stderr = StringIO() invalid_message = 'Please specify workflow versions in the format' call_command('loadworkflowsampledata', invalid_arg, stderr=invalid_stderr) self.assertIn(invalid_message, invalid_stderr.getvalue()) # Loading valid sample data should succeed without errors v1_str = '{}/{}'.format(TEST_WORKFLOW, VERSION_1) stdout = StringIO() call_command('loadworkflowsampledata', v1_str, stdout=stdout) output = stdout.getvalue() success_message = 'Successfully loaded sample data' self.assertIn(success_message, output) # clean up for subsequent commands del sys.modules['orchestra.tests.workflows.test_dir.load_sample_data'] # Loading sample data for a nonexistent workflow should fail stderr1 = StringIO() call_command('loadworkflowsampledata', '{}/{}'.format(NONEXISTENT_WORKFLOW_SLUG, VERSION_1), stderr=stderr1) output1 = stderr1.getvalue() no_workflow_error = ('Workflow {} has not been loaded into the ' 'database'.format(NONEXISTENT_WORKFLOW_SLUG)) self.assertIn(no_workflow_error, output1) # Loading sample data for a nonexistent version should fail stderr2 = StringIO() call_command('loadworkflowsampledata', '{}/{}'.format(TEST_WORKFLOW, NONEXISTENT_VERSION), stderr=stderr2) output2 = stderr2.getvalue() no_version_error = ( 'Version {} does not exist'.format(NONEXISTENT_VERSION)) self.assertIn(no_version_error, output2) # Loading a workflow with no loading script should fail # Simulate this by moving the file. module_path = os.path.join(TEST_WORKFLOW_DIR, 'load_sample_data.py') new_path = os.path.join(TEST_WORKFLOW_DIR, 'load_sample_data.py.BAK') try: os.rename(module_path, new_path) stderr3 = StringIO() call_command('loadworkflowsampledata', v1_str, stderr=stderr3) output3 = stderr3.getvalue() no_module_error = 'An error occurred while loading sample data' self.assertIn(no_module_error, output3) finally: os.rename(new_path, module_path) # Loading sample data for a workflow with no load function in its JSON # manifest should fail. workflow = Workflow.objects.get(slug=TEST_WORKFLOW) workflow.sample_data_load_function = None workflow.save() stderr4 = StringIO() call_command('loadworkflowsampledata', v1_str, stderr=stderr4) output4 = stderr4.getvalue() no_load_function_error = ( 'Workflow {} does not provide sample data'.format(TEST_WORKFLOW)) self.assertIn(no_load_function_error, output4)
def test_load_workflow_version(self): """ Ensure that workflow version loading works as desired. """ # Verify initial DB state. assert_test_dir_workflow_not_loaded(self) assert_test_dir_v1_not_loaded(self) assert_test_dir_v2_not_loaded(self) # Load V1 of the workflow. load_workflow(WORKFLOWS['valid']['app_label'], VERSION_1) workflow = Workflow.objects.get(slug='test_dir') assert_test_dir_workflow_loaded(self) assert_test_dir_v1_loaded(self) assert_test_dir_v2_not_loaded(self) # Load the JSON data for the versions. v1_file_path = os.path.join(WORKFLOWS['valid']['dir'], 'v1/version.json') with open(v1_file_path, 'r') as v1_file: v1_data = json.load(v1_file) v2_file_path = os.path.join(WORKFLOWS['valid']['dir'], 'v2/version.json') with open(v2_file_path, 'r') as v2_file: v2_data = json.load(v2_file) # Without --force, can't overwrite a version. # We wrap calls to load_workflow_version in transaction.atomic, because # the call might create corrupt database state otherwise. force_error = 'Version {} already exists'.format(VERSION_1) with self.assertRaisesMessage(WorkflowError, force_error): with transaction.atomic(): load_workflow_version(v1_data, workflow) # Even with --force, can't overwrite a version with a new step v1_data['steps'].append({'slug': 'invalid_new_step'}) step_change_err_msg = ('Even with --force, cannot change the steps of ' 'a workflow.') with self.assertRaisesMessage(WorkflowError, step_change_err_msg): with transaction.atomic(): load_workflow_version(v1_data, workflow, force=True) v1_data['steps'] = v1_data['steps'][:-1] # Even with --force, can't change a step's creation dependencies. step_2_create_dependencies = v1_data['steps'][1]['creation_depends_on'] step_2_create_dependencies.append('s3') topology_change_err_msg = ('Even with --force, cannot change the ' 'topology of a workflow.') with self.assertRaisesMessage(WorkflowError, topology_change_err_msg): with transaction.atomic(): load_workflow_version(v1_data, workflow, force=True) v1_data['steps'][1]['creation_depends_on'] = ( step_2_create_dependencies[:-1]) # Even with --force, can't change a step's submission dependencies. step_3_submit_dependencies = v1_data['steps'][2][ 'submission_depends_on'] step_3_submit_dependencies.append('s1') with self.assertRaisesMessage(WorkflowError, topology_change_err_msg): with transaction.atomic(): load_workflow_version(v1_data, workflow, force=True) v1_data['steps'][2]['submission_depends_on'] = ( step_3_submit_dependencies[:-1]) # Otherwise, --force should reload versions correctly. with transaction.atomic(): load_workflow_version(v1_data, workflow, force=True) assert_test_dir_workflow_loaded(self) assert_test_dir_v1_loaded(self) assert_test_dir_v2_not_loaded(self) # New versions with bad slugs should not load correctly v2_step_2 = v2_data['steps'][1] v2_step_2_create_dependencies = v2_step_2['creation_depends_on'] v2_step_2_create_dependencies.append('not_a_real_step') bad_slug_error = '{}.{} contains a non-existent slug' with self.assertRaisesMessage( WorkflowError, bad_slug_error.format('s2', 'creation_depends_on')): with transaction.atomic(): load_workflow_version(v2_data, workflow) v2_step_2['creation_depends_on'] = (v2_step_2_create_dependencies[:-1]) v2_step_2_submit_dependencies = v2_step_2['submission_depends_on'] v2_step_2_submit_dependencies.append('not_a_real_step') with self.assertRaisesMessage( WorkflowError, bad_slug_error.format('s2', 'submission_depends_on')): with transaction.atomic(): load_workflow_version(v2_data, workflow) v2_step_2['submission_depends_on'] = ( v2_step_2_submit_dependencies[:-1]) v2_step_2_certification_dependencies = v2_step_2[ 'required_certifications'] v2_step_2_certification_dependencies.append('not_a_real_certification') with self.assertRaisesMessage( WorkflowError, bad_slug_error.format('s2', 'required_certifications')): with transaction.atomic(): load_workflow_version(v2_data, workflow) v2_step_2['required_certifications'] = ( v2_step_2_certification_dependencies[:-1]) # Otherwise, new versions should load correctly with transaction.atomic(): load_workflow_version(v2_data, workflow) assert_test_dir_workflow_loaded(self) assert_test_dir_v1_loaded(self) assert_test_dir_v2_loaded(self)
def test_load_workflow(self): """ Ensure that a workflow directory loads correctly. """ # Verify initial DB state. assert_test_dir_workflow_not_loaded(self) assert_test_dir_v1_not_loaded(self) assert_test_dir_v2_not_loaded(self) # Shouldn't be able to parse a workflow with nonexistent # certification dependencies. with self.assertRaisesMessage( WorkflowError, 'Certification certification1 requires non-existent ' 'certification'): load_workflow(WORKFLOWS['bad_certifications']['app_label'], VERSION_1) # Shouldn't be able to load a non-existent version. version_error = 'Invalid version requested: {}'.format(INVALID_VERSION) with self.assertRaisesMessage(WorkflowError, version_error): load_workflow(WORKFLOWS['valid']['app_label'], INVALID_VERSION) # Loading a version should create the right database objects. load_workflow(WORKFLOWS['valid']['app_label'], VERSION_1) assert_test_dir_workflow_loaded(self) assert_test_dir_v1_loaded(self) assert_test_dir_v2_not_loaded(self) # Loading a second version should create the right database objects. load_workflow(WORKFLOWS['valid']['app_label'], VERSION_2) assert_test_dir_workflow_loaded(self) assert_test_dir_v1_loaded(self) assert_test_dir_v2_loaded(self) # Reloading a version without --force should fail. force_error = 'Version {} already exists'.format(VERSION_1) with self.assertRaisesMessage(WorkflowError, force_error): load_workflow(WORKFLOWS['valid']['app_label'], VERSION_1) # Reloading versions with --force should succeed. load_workflow(WORKFLOWS['valid']['app_label'], VERSION_1, force=True) load_workflow(WORKFLOWS['valid']['app_label'], VERSION_2, force=True) assert_test_dir_workflow_loaded(self) assert_test_dir_v1_loaded(self) assert_test_dir_v2_loaded(self)
def test_load_workflow_version(self): """ Ensure that workflow version loading works as desired. """ # Verify initial DB state. assert_test_dir_workflow_not_loaded(self) assert_test_dir_v1_not_loaded(self) assert_test_dir_v2_not_loaded(self) # Load V1 of the workflow. load_workflow(WORKFLOWS['valid']['app_label'], VERSION_1) workflow = Workflow.objects.get(slug='test_dir') assert_test_dir_workflow_loaded(self) assert_test_dir_v1_loaded(self) assert_test_dir_v2_not_loaded(self) # Load the JSON data for the versions. v1_file_path = os.path.join(WORKFLOWS['valid']['dir'], 'v1/version.json') with open(v1_file_path, 'r') as v1_file: v1_data = json.load(v1_file) v2_file_path = os.path.join(WORKFLOWS['valid']['dir'], 'v2/version.json') with open(v2_file_path, 'r') as v2_file: v2_data = json.load(v2_file) # Without --force, can't overwrite a version. # We wrap calls to load_workflow_version in transaction.atomic, because # the call might create corrupt database state otherwise. force_error = 'Version {} already exists'.format(VERSION_1) with self.assertRaisesMessage(WorkflowError, force_error): with transaction.atomic(): load_workflow_version(v1_data, workflow) # Even with --force, can't overwrite a version with a new step v1_data['steps'].append({'slug': 'invalid_new_step'}) step_change_err_msg = ('Even with --force, cannot change the steps of ' 'a workflow.') with self.assertRaisesMessage(WorkflowError, step_change_err_msg): with transaction.atomic(): load_workflow_version(v1_data, workflow, force=True) v1_data['steps'] = v1_data['steps'][:-1] # Even with --force, can't change a step's creation dependencies. step_2_create_dependencies = v1_data['steps'][1]['creation_depends_on'] step_2_create_dependencies.append('s3') topology_change_err_msg = ('Even with --force, cannot change the ' 'topology of a workflow.') with self.assertRaisesMessage(WorkflowError, topology_change_err_msg): with transaction.atomic(): load_workflow_version(v1_data, workflow, force=True) v1_data['steps'][1]['creation_depends_on'] = ( step_2_create_dependencies[:-1]) # Even with --force, can't change a step's submission dependencies. step_3_submit_dependencies = v1_data['steps'][2][ 'submission_depends_on'] step_3_submit_dependencies.append('s1') with self.assertRaisesMessage(WorkflowError, topology_change_err_msg): with transaction.atomic(): load_workflow_version(v1_data, workflow, force=True) v1_data['steps'][2]['submission_depends_on'] = ( step_3_submit_dependencies[:-1]) # Otherwise, --force should reload versions correctly. with transaction.atomic(): load_workflow_version(v1_data, workflow, force=True) assert_test_dir_workflow_loaded(self) assert_test_dir_v1_loaded(self) assert_test_dir_v2_not_loaded(self) # New versions with bad slugs should not load correctly v2_step_2 = v2_data['steps'][1] v2_step_2_create_dependencies = v2_step_2['creation_depends_on'] v2_step_2_create_dependencies.append('not_a_real_step') bad_slug_error = '{}.{} contains a non-existent slug' with self.assertRaisesMessage( WorkflowError, bad_slug_error.format('s2', 'creation_depends_on')): with transaction.atomic(): load_workflow_version(v2_data, workflow) v2_step_2['creation_depends_on'] = ( v2_step_2_create_dependencies[:-1]) v2_step_2_submit_dependencies = v2_step_2['submission_depends_on'] v2_step_2_submit_dependencies.append('not_a_real_step') with self.assertRaisesMessage( WorkflowError, bad_slug_error.format('s2', 'submission_depends_on')): with transaction.atomic(): load_workflow_version(v2_data, workflow) v2_step_2['submission_depends_on'] = ( v2_step_2_submit_dependencies[:-1]) v2_step_2_certification_dependencies = v2_step_2[ 'required_certifications'] v2_step_2_certification_dependencies.append('not_a_real_certification') with self.assertRaisesMessage( WorkflowError, bad_slug_error.format('s2', 'required_certifications')): with transaction.atomic(): load_workflow_version(v2_data, workflow) v2_step_2['required_certifications'] = ( v2_step_2_certification_dependencies[:-1]) # Otherwise, new versions should load correctly with transaction.atomic(): load_workflow_version(v2_data, workflow) assert_test_dir_workflow_loaded(self) assert_test_dir_v1_loaded(self) assert_test_dir_v2_loaded(self)
def test_load_workflow(self): """ Ensure that a workflow directory loads correctly. """ # Verify initial DB state. assert_test_dir_workflow_not_loaded(self) assert_test_dir_v1_not_loaded(self) assert_test_dir_v2_not_loaded(self) # Shouldn't be able to parse a workflow with nonexistent # certification dependencies. with self.assertRaisesMessage( WorkflowError, 'Certification certification1 requires non-existent ' 'certification'): load_workflow(WORKFLOWS['bad_certifications']['app_label'], VERSION_1) # Shouldn't be able to load a non-existent version. version_error = 'Invalid version requested: {}'.format( INVALID_VERSION) with self.assertRaisesMessage(WorkflowError, version_error): load_workflow(WORKFLOWS['valid']['app_label'], INVALID_VERSION) # Loading a version should create the right database objects. load_workflow(WORKFLOWS['valid']['app_label'], VERSION_1) assert_test_dir_workflow_loaded(self) assert_test_dir_v1_loaded(self) assert_test_dir_v2_not_loaded(self) # Loading a second version should create the right database objects. load_workflow(WORKFLOWS['valid']['app_label'], VERSION_2) assert_test_dir_workflow_loaded(self) assert_test_dir_v1_loaded(self) assert_test_dir_v2_loaded(self) # Reloading a version without --force should fail. force_error = 'Version {} already exists'.format(VERSION_1) with self.assertRaisesMessage(WorkflowError, force_error): load_workflow(WORKFLOWS['valid']['app_label'], VERSION_1) # Reloading versions with --force should succeed. load_workflow(WORKFLOWS['valid']['app_label'], VERSION_1, force=True) load_workflow(WORKFLOWS['valid']['app_label'], VERSION_2, force=True) assert_test_dir_workflow_loaded(self) assert_test_dir_v1_loaded(self) assert_test_dir_v2_loaded(self)
def test_load_workflow_version(self): """ Ensure that workflow version loading works as desired. """ # Verify initial DB state. assert_test_dir_workflow_not_loaded(self) assert_test_dir_v1_not_loaded(self) assert_test_dir_v2_not_loaded(self) # Load V1 of the workflow. load_workflow(WORKFLOWS["valid"]["app_label"], VERSION_1) workflow = Workflow.objects.get(slug="test_dir") assert_test_dir_workflow_loaded(self) assert_test_dir_v1_loaded(self) assert_test_dir_v2_not_loaded(self) # Load the JSON data for the versions. v1_file_path = os.path.join(WORKFLOWS["valid"]["dir"], "v1/version.json") with open(v1_file_path, "r") as v1_file: v1_data = json.load(v1_file) v2_file_path = os.path.join(WORKFLOWS["valid"]["dir"], "v2/version.json") with open(v2_file_path, "r") as v2_file: v2_data = json.load(v2_file) # Without --force, can't overwrite a version. # We wrap calls to load_workflow_version in transaction.atomic, because # the call might create corrupt database state otherwise. force_error = "Version {} already exists".format(VERSION_1) with self.assertRaisesMessage(WorkflowError, force_error): with transaction.atomic(): load_workflow_version(v1_data, workflow) # Even with --force, can't overwrite a version with a new step v1_data["steps"].append({"slug": "invalid_new_step"}) step_change_err_msg = "Even with --force, cannot change the steps of " "a workflow." with self.assertRaisesMessage(WorkflowError, step_change_err_msg): with transaction.atomic(): load_workflow_version(v1_data, workflow, force=True) v1_data["steps"] = v1_data["steps"][:-1] # Even with --force, can't change a step's creation dependencies. step_2_create_dependencies = v1_data["steps"][1]["creation_depends_on"] step_2_create_dependencies.append("s3") topology_change_err_msg = "Even with --force, cannot change the " "topology of a workflow." with self.assertRaisesMessage(WorkflowError, topology_change_err_msg): with transaction.atomic(): load_workflow_version(v1_data, workflow, force=True) v1_data["steps"][1]["creation_depends_on"] = step_2_create_dependencies[:-1] # Even with --force, can't change a step's submission dependencies. step_3_submit_dependencies = v1_data["steps"][2]["submission_depends_on"] step_3_submit_dependencies.append("s1") with self.assertRaisesMessage(WorkflowError, topology_change_err_msg): with transaction.atomic(): load_workflow_version(v1_data, workflow, force=True) v1_data["steps"][2]["submission_depends_on"] = step_3_submit_dependencies[:-1] # Otherwise, --force should reload versions correctly. with transaction.atomic(): load_workflow_version(v1_data, workflow, force=True) assert_test_dir_workflow_loaded(self) assert_test_dir_v1_loaded(self) assert_test_dir_v2_not_loaded(self) # New versions with bad slugs should not load correctly v2_step_2 = v2_data["steps"][1] v2_step_2_create_dependencies = v2_step_2["creation_depends_on"] v2_step_2_create_dependencies.append("not_a_real_step") bad_slug_error = "{}.{} contains a non-existent slug" with self.assertRaisesMessage(WorkflowError, bad_slug_error.format("s2", "creation_depends_on")): with transaction.atomic(): load_workflow_version(v2_data, workflow) v2_step_2["creation_depends_on"] = v2_step_2_create_dependencies[:-1] v2_step_2_submit_dependencies = v2_step_2["submission_depends_on"] v2_step_2_submit_dependencies.append("not_a_real_step") with self.assertRaisesMessage(WorkflowError, bad_slug_error.format("s2", "submission_depends_on")): with transaction.atomic(): load_workflow_version(v2_data, workflow) v2_step_2["submission_depends_on"] = v2_step_2_submit_dependencies[:-1] v2_step_2_certification_dependencies = v2_step_2["required_certifications"] v2_step_2_certification_dependencies.append("not_a_real_certification") with self.assertRaisesMessage(WorkflowError, bad_slug_error.format("s2", "required_certifications")): with transaction.atomic(): load_workflow_version(v2_data, workflow) v2_step_2["required_certifications"] = v2_step_2_certification_dependencies[:-1] # Otherwise, new versions should load correctly with transaction.atomic(): load_workflow_version(v2_data, workflow) assert_test_dir_workflow_loaded(self) assert_test_dir_v1_loaded(self) assert_test_dir_v2_loaded(self)
def test_loadworkflowsampledata(self): """ Ensure that workflow sample data loads correctly. """ # Load the test workflow load_workflow(TEST_WORKFLOW, VERSION_1) assert_test_dir_workflow_loaded(self) assert_test_dir_v1_loaded(self) # Calling the command with an invalid version argument should fail. invalid_args = ( VERSION_1, # Workflow slug omitted TEST_WORKFLOW, # Version slug omitted '{}/{}/'.format(TEST_WORKFLOW, VERSION_1), # Too many slashes '{}.{}'.format(TEST_WORKFLOW, VERSION_1), # Wrong separator ) for invalid_arg in invalid_args: invalid_stderr = StringIO() invalid_message = 'Please specify workflow versions in the format' call_command('loadworkflowsampledata', invalid_arg, stderr=invalid_stderr) self.assertIn(invalid_message, invalid_stderr.getvalue()) # Loading valid sample data should succeed without errors v1_str = '{}/{}'.format(TEST_WORKFLOW, VERSION_1) stdout = StringIO() call_command('loadworkflowsampledata', v1_str, stdout=stdout) output = stdout.getvalue() success_message = 'Successfully loaded sample data' self.assertIn(success_message, output) # clean up for subsequent commands del sys.modules['orchestra.tests.workflows.test_dir.load_sample_data'] # Loading sample data for a nonexistent workflow should fail stderr1 = StringIO() call_command( 'loadworkflowsampledata', '{}/{}'.format(NONEXISTENT_WORKFLOW_SLUG, VERSION_1), stderr=stderr1) output1 = stderr1.getvalue() no_workflow_error = ('Workflow {} has not been loaded into the ' 'database'.format(NONEXISTENT_WORKFLOW_SLUG)) self.assertIn(no_workflow_error, output1) # Loading sample data for a nonexistent version should fail stderr2 = StringIO() call_command( 'loadworkflowsampledata', '{}/{}'.format(TEST_WORKFLOW, NONEXISTENT_VERSION), stderr=stderr2) output2 = stderr2.getvalue() no_version_error = ('Version {} does not exist' .format(NONEXISTENT_VERSION)) self.assertIn(no_version_error, output2) # Loading a workflow with no loading script should fail # Simulate this by moving the file. module_path = os.path.join(TEST_WORKFLOW_DIR, 'load_sample_data.py') new_path = os.path.join(TEST_WORKFLOW_DIR, 'load_sample_data.py.BAK') try: os.rename(module_path, new_path) stderr3 = StringIO() call_command('loadworkflowsampledata', v1_str, stderr=stderr3) output3 = stderr3.getvalue() no_module_error = 'An error occurred while loading sample data' self.assertIn(no_module_error, output3) finally: os.rename(new_path, module_path) # Loading sample data for a workflow with no load function in its JSON # manifest should fail. workflow = Workflow.objects.get(slug=TEST_WORKFLOW) workflow.sample_data_load_function = None workflow.save() stderr4 = StringIO() call_command('loadworkflowsampledata', v1_str, stderr=stderr4) output4 = stderr4.getvalue() no_load_function_error = ('Workflow {} does not provide sample data' .format(TEST_WORKFLOW)) self.assertIn(no_load_function_error, output4)