def test_successful(self, mock_store): """Tests calling RecipeDefinition.validate_data() successfully.""" definition = { 'version': '1.0', 'input_data': [{ 'name': 'Recipe Input', 'type': 'file', 'media_types': ['text/plain'], }], 'jobs': [{ 'name': 'Job 1', 'job_type': { 'name': self.job_type_1.name, 'version': self.job_type_1.version, }, 'recipe_inputs': [{ 'recipe_input': 'Recipe Input', 'job_input': self.input_name_1, }], }, { 'name': 'Job 2', 'job_type': { 'name': self.job_type_2.name, 'version': self.job_type_2.version, }, 'dependencies': [{ 'name': 'Job 1', 'connections': [{ 'output': self.output_name_1, 'input': self.input_name_2, }], }], }], } recipe = RecipeDefinition(definition) recipe.validate_job_interfaces() data = { 'version': '1.0', 'input_data': [{ 'name': 'Recipe Input', 'file_id': self.file_1.id, }], 'workspace_id': 1, } recipe_data = LegacyRecipeData(data) # No exception is success recipe.validate_data(recipe_data)
def test_missing_workspace(self, mock_store): """Tests calling RecipeDefinition.validate_data() with a missing required workspace.""" definition = { 'version': '1.0', 'input_data': [{ 'name': 'Recipe Input', 'type': 'file', 'media_types': ['text/plain'], }], 'jobs': [{ 'name': 'Job 1', 'job_type': { 'name': self.job_type_1.name, 'version': self.job_type_1.version, }, 'recipe_inputs': [{ 'recipe_input': 'Recipe Input', 'job_input': self.input_name_1, }], }, { 'name': 'Job 2', 'job_type': { 'name': self.job_type_2.name, 'version': self.job_type_2.version, }, 'dependencies': [{ 'name': 'Job 1', 'connections': [{ 'output': self.output_name_1, 'input': self.input_name_2, }], }], }], } recipe = RecipeDefinition(definition) recipe.validate_job_interfaces() data = { 'version': '1.0', 'input_data': [{ 'name': 'Recipe Input', 'file_id': self.file_1.id, }], } recipe_data = LegacyRecipeData(data) self.assertRaises(InvalidRecipeData, recipe.validate_data, recipe_data)
def test_successful_no_workspace(self, mock_store): """Tests calling RecipeDefinition.validate_data() successfully with no workspace.""" definition = { 'version': '1.0', 'input_data': [{ 'name': 'Recipe Input', 'type': 'file', 'media_types': ['text/plain'], }], 'jobs': [{ 'name': 'Job 3', 'job_type': { 'name': self.job_type_3.name, 'version': self.job_type_3.version, }, 'recipe_inputs': [{ 'recipe_input': 'Recipe Input', 'job_input': self.input_name_3, }], }], } recipe = RecipeDefinition(definition) recipe.validate_job_interfaces() data = { 'version': '1.0', 'input_data': [{ 'name': 'Recipe Input', 'file_id': self.file_1.id, }], } recipe_data = LegacyRecipeData(data) # No exception is success recipe.validate_data(recipe_data)
def setUp(self): django.setup() workspace = storage_test_utils.create_workspace() source_file = source_test_utils.create_source(workspace=workspace) self.event = trigger_test_utils.create_trigger_event() interface_1 = { 'version': '1.0', 'command': 'test_command', 'command_arguments': 'test_arg', 'input_data': [{ 'name': 'Test Input 1', 'type': 'file', 'media_types': ['text/plain'], }], 'output_data': [{ 'name': 'Test Output 1', 'type': 'files', 'media_type': 'image/png', }] } self.job_type_1 = job_test_utils.create_job_type(interface=interface_1) interface_2 = { 'version': '1.0', 'command': 'test_command', 'command_arguments': 'test_arg', 'input_data': [{ 'name': 'Test Input 2', 'type': 'files', 'media_types': ['image/png', 'image/tiff'], }], 'output_data': [{ 'name': 'Test Output 2', 'type': 'file', }] } self.job_type_2 = job_test_utils.create_job_type(interface=interface_2) definition = { 'version': '1.0', 'input_data': [{ 'name': 'Recipe Input', 'type': 'file', 'media_types': ['text/plain'], }], 'jobs': [{ 'name': 'Job 1', 'job_type': { 'name': self.job_type_1.name, 'version': self.job_type_1.version, }, 'recipe_inputs': [{ 'recipe_input': 'Recipe Input', 'job_input': 'Test Input 1', }] }, { 'name': 'Job 2', 'job_type': { 'name': self.job_type_2.name, 'version': self.job_type_2.version, }, 'dependencies': [{ 'name': 'Job 1', 'connections': [{ 'output': 'Test Output 1', 'input': 'Test Input 2', }] }] }] } recipe_definition = RecipeDefinition(definition) recipe_definition.validate_job_interfaces() self.recipe_type = recipe_test_utils.create_recipe_type(definition=definition) data = { 'version': '1.0', 'input_data': [{ 'name': 'Recipe Input', 'file_id': source_file.id, }], 'workspace_id': workspace.id, } self.data = LegacyRecipeData(data)
class TestRecipeTypeManagerEditRecipeType(TransactionTestCase): def setUp(self): django.setup() self.workspace = storage_test_utils.create_workspace() interface_1 = { 'version': '1.0', 'command': 'my_command', 'command_arguments': 'args', 'input_data': [{ 'name': 'Test Input 1', 'type': 'file', 'media_types': ['text/plain'], }], 'output_data': [{ 'name': 'Test Output 1', 'type': 'files', 'media_type': 'image/png', }] } self.job_type_1 = job_test_utils.create_job_type(interface=interface_1) interface_2 = { 'version': '1.0', 'command': 'my_command', 'command_arguments': 'args', 'input_data': [{ 'name': 'Test Input 2', 'type': 'files', 'media_types': ['image/png', 'image/tiff'], }], 'output_data': [{ 'name': 'Test Output 2', 'type': 'file', }] } self.job_type_2 = job_test_utils.create_job_type(interface=interface_2) self.definition = { 'version': '1.0', 'input_data': [{ 'name': 'Recipe Input', 'type': 'file', 'media_types': ['text/plain'], }], 'jobs': [{ 'name': 'Job 1', 'job_type': { 'name': self.job_type_1.name, 'version': self.job_type_1.version, }, 'recipe_inputs': [{ 'recipe_input': 'Recipe Input', 'job_input': 'Test Input 1', }] }, { 'name': 'Job 2', 'job_type': { 'name': self.job_type_2.name, 'version': self.job_type_2.version, }, 'dependencies': [{ 'name': 'Job 1', 'connections': [{ 'output': 'Test Output 1', 'input': 'Test Input 2', }] }] }] } self.recipe_def = LegacyRecipeDefinition(self.definition) self.recipe_def.validate_job_interfaces() self.new_definition = { 'version': '1.0', 'input_data': [{ 'name': 'Recipe Input', 'type': 'file', 'media_types': ['text/plain'], }], 'jobs': [{ 'name': 'Job 1', 'job_type': { 'name': self.job_type_1.name, 'version': self.job_type_1.version, }, 'recipe_inputs': [{ 'recipe_input': 'Recipe Input', 'job_input': 'Test Input 1', }] }] } self.new_recipe_def = LegacyRecipeDefinition(self.new_definition) self.new_recipe_def.validate_job_interfaces() self.configuration = { 'version': '1.0', 'condition': { 'media_type': 'text/plain' }, 'data': { 'input_data_name': 'Recipe Input', 'workspace_name': self.workspace.name } } self.trigger_config = recipe_test_utils.MockTriggerRuleConfiguration( recipe_test_utils.MOCK_TYPE, self.configuration) self.new_configuration = { 'version': '1.0', 'condition': { 'media_type': 'application/json' }, 'data': { 'input_data_name': 'Recipe Input', 'workspace_name': self.workspace.name } } self.new_trigger_config = recipe_test_utils.MockTriggerRuleConfiguration( recipe_test_utils.MOCK_TYPE, self.new_configuration) def test_change_simple_no_trigger(self): """Tests calling RecipeTypeManager.edit_recipe_type() with only basic attributes and no previous trigger rule""" # Create recipe_type name = 'test-recipe' version = '1.0' title = 'Test Recipe' desc = 'Test description' recipe_type = RecipeType.objects.create_recipe_type( name, version, title, desc, self.recipe_def, None) with transaction.atomic(): recipe_type = RecipeType.objects.select_for_update().get( pk=recipe_type.id) # Edit the recipe new_title = 'New title' new_desc = 'New description' RecipeType.objects.edit_recipe_type(recipe_type.id, new_title, new_desc, None, None, False) recipe_type = RecipeType.objects.select_related('trigger_rule').get( pk=recipe_type.id) # Check results self.assertEqual(recipe_type.title, new_title) self.assertEqual(recipe_type.description, new_desc) self.assertDictEqual(recipe_type.get_recipe_definition().get_dict(), self.recipe_def.get_dict()) self.assertEqual(recipe_type.revision_num, 1) self.assertIsNone(recipe_type.trigger_rule) num_of_revs = RecipeTypeRevision.objects.filter( recipe_type_id=recipe_type.id).count() self.assertEqual(num_of_revs, 1) def test_change_simple_with_trigger(self): """Tests calling RecipeTypeManager.edit_recipe_type() with only basic attributes and a previous trigger rule""" # Create recipe_type name = 'test-recipe' version = '1.0' title = 'Test Recipe' desc = 'Test description' trigger_rule = trigger_test_utils.create_trigger_rule( trigger_type=recipe_test_utils.MOCK_TYPE, configuration=self.trigger_config.get_dict()) trigger_rule_id = trigger_rule.id recipe_type = RecipeType.objects.create_recipe_type( name, version, title, desc, self.recipe_def, trigger_rule) with transaction.atomic(): recipe_type = RecipeType.objects.select_for_update().get( pk=recipe_type.id) # Edit the recipe new_title = 'New title' new_desc = 'New description' RecipeType.objects.edit_recipe_type(recipe_type.id, new_title, new_desc, None, None, False) recipe_type = RecipeType.objects.select_related('trigger_rule').get( pk=recipe_type.id) # Check results self.assertEqual(recipe_type.title, new_title) self.assertEqual(recipe_type.description, new_desc) self.assertDictEqual(recipe_type.get_recipe_definition().get_dict(), self.recipe_def.get_dict()) self.assertEqual(recipe_type.revision_num, 1) self.assertEqual(recipe_type.trigger_rule_id, trigger_rule_id) num_of_revs = RecipeTypeRevision.objects.filter( recipe_type_id=recipe_type.id).count() self.assertEqual(num_of_revs, 1) def test_change_to_definition(self): """Tests calling RecipeTypeManager.edit_recipe_type() with a change to the definition""" # Create recipe_type name = 'test-recipe' version = '1.0' title = 'Test Recipe' desc = 'Test description' trigger_rule = trigger_test_utils.create_trigger_rule( trigger_type=recipe_test_utils.MOCK_TYPE, configuration=self.trigger_config.get_dict()) trigger_rule_id = trigger_rule.id recipe_type = RecipeType.objects.create_recipe_type( name, version, title, desc, self.recipe_def, trigger_rule) with transaction.atomic(): recipe_type = RecipeType.objects.select_for_update().get( pk=recipe_type.id) # Edit the recipe RecipeType.objects.edit_recipe_type(recipe_type.id, None, None, self.new_recipe_def, None, False) recipe_type = RecipeType.objects.select_related('trigger_rule').get( pk=recipe_type.id) # Check results self.assertEqual(recipe_type.title, title) self.assertEqual(recipe_type.description, desc) self.assertDictEqual(recipe_type.get_recipe_definition().get_dict(), self.new_recipe_def.get_dict()) self.assertEqual(recipe_type.revision_num, 2) self.assertEqual(recipe_type.trigger_rule_id, trigger_rule_id) trigger_rule = TriggerRule.objects.get(pk=trigger_rule_id) self.assertTrue(trigger_rule.is_active) # New revision due to definition change num_of_revs = RecipeTypeRevision.objects.filter( recipe_type_id=recipe_type.id).count() self.assertEqual(num_of_revs, 2) def test_change_to_trigger_rule(self): """Tests calling RecipeTypeManager.edit_recipe_type() with a change to the trigger rule""" # Create recipe_type name = 'test-recipe' version = '1.0' title = 'Test Recipe' desc = 'Test description' trigger_rule = trigger_test_utils.create_trigger_rule( trigger_type=recipe_test_utils.MOCK_TYPE, configuration=self.trigger_config.get_dict()) trigger_rule_id = trigger_rule.id new_trigger_rule = trigger_test_utils.create_trigger_rule( trigger_type=recipe_test_utils.MOCK_TYPE, configuration=self.new_trigger_config.get_dict()) new_trigger_rule_id = new_trigger_rule.id recipe_type = RecipeType.objects.create_recipe_type( name, version, title, desc, self.recipe_def, trigger_rule) with transaction.atomic(): recipe_type = RecipeType.objects.select_for_update().get( pk=recipe_type.id) # Edit the recipe RecipeType.objects.edit_recipe_type(recipe_type.id, None, None, None, new_trigger_rule, False) recipe_type = RecipeType.objects.select_related('trigger_rule').get( pk=recipe_type.id) # Check results self.assertEqual(recipe_type.title, title) self.assertEqual(recipe_type.description, desc) self.assertDictEqual(recipe_type.get_recipe_definition().get_dict(), self.recipe_def.get_dict()) self.assertEqual(recipe_type.revision_num, 1) self.assertEqual(recipe_type.trigger_rule_id, new_trigger_rule_id) trigger_rule = TriggerRule.objects.get(pk=trigger_rule_id) self.assertFalse(trigger_rule.is_active) new_trigger_rule = TriggerRule.objects.get(pk=new_trigger_rule_id) self.assertTrue(new_trigger_rule.is_active) num_of_revs = RecipeTypeRevision.objects.filter( recipe_type_id=recipe_type.id).count() self.assertEqual(num_of_revs, 1) def test_remove_trigger_rule(self): """Tests calling RecipeTypeManager.edit_recipe_type() that removes the trigger rule""" # Create recipe_type name = 'test-recipe' version = '1.0' title = 'Test Recipe' desc = 'Test description' trigger_rule = trigger_test_utils.create_trigger_rule( trigger_type=recipe_test_utils.MOCK_TYPE, configuration=self.trigger_config.get_dict()) trigger_rule_id = trigger_rule.id recipe_type = RecipeType.objects.create_recipe_type( name, version, title, desc, self.recipe_def, trigger_rule) with transaction.atomic(): recipe_type = RecipeType.objects.select_for_update().get( pk=recipe_type.id) # Edit the recipe RecipeType.objects.edit_recipe_type(recipe_type.id, None, None, None, None, True) recipe_type = RecipeType.objects.select_related('trigger_rule').get( pk=recipe_type.id) # Check results self.assertEqual(recipe_type.title, title) self.assertEqual(recipe_type.description, desc) self.assertDictEqual(recipe_type.get_recipe_definition().get_dict(), self.recipe_def.get_dict()) self.assertEqual(recipe_type.revision_num, 1) self.assertIsNone(recipe_type.trigger_rule) trigger_rule = TriggerRule.objects.get(pk=trigger_rule_id) self.assertFalse(trigger_rule.is_active) num_of_revs = RecipeTypeRevision.objects.filter( recipe_type_id=recipe_type.id).count() self.assertEqual(num_of_revs, 1) def test_change_to_both(self): """Tests calling RecipeTypeManager.edit_recipe_type() with a change to both the definition and trigger rule""" # Create recipe_type name = 'test-recipe' version = '1.0' title = 'Test Recipe' desc = 'Test description' trigger_rule = trigger_test_utils.create_trigger_rule( trigger_type=recipe_test_utils.MOCK_TYPE, configuration=self.trigger_config.get_dict()) trigger_rule_id = trigger_rule.id new_trigger_rule = trigger_test_utils.create_trigger_rule( trigger_type=recipe_test_utils.MOCK_TYPE, configuration=self.new_trigger_config.get_dict()) new_trigger_rule_id = new_trigger_rule.id recipe_type = RecipeType.objects.create_recipe_type( name, version, title, desc, self.recipe_def, trigger_rule) with transaction.atomic(): recipe_type = RecipeType.objects.select_for_update().get( pk=recipe_type.id) # Edit the recipe RecipeType.objects.edit_recipe_type(recipe_type.id, None, None, self.new_recipe_def, new_trigger_rule, False) recipe_type = RecipeType.objects.select_related('trigger_rule').get( pk=recipe_type.id) # Check results self.assertEqual(recipe_type.title, title) self.assertEqual(recipe_type.description, desc) self.assertDictEqual(recipe_type.get_recipe_definition().get_dict(), self.new_recipe_def.get_dict()) self.assertEqual(recipe_type.revision_num, 2) self.assertEqual(recipe_type.trigger_rule_id, new_trigger_rule_id) trigger_rule = TriggerRule.objects.get(pk=trigger_rule_id) self.assertFalse(trigger_rule.is_active) new_trigger_rule = TriggerRule.objects.get(pk=new_trigger_rule_id) self.assertTrue(new_trigger_rule.is_active) # New revision due to definition change num_of_revs = RecipeTypeRevision.objects.filter( recipe_type_id=recipe_type.id).count() self.assertEqual(num_of_revs, 2) def test_invalid_trigger_rule(self): """Tests calling RecipeTypeManager.edit_recipe_type() with a new invalid trigger rule""" # Create recipe_type name = 'test-recipe' version = '1.0' title = 'Test Recipe' desc = 'Test description' trigger_rule = trigger_test_utils.create_trigger_rule( trigger_type=recipe_test_utils.MOCK_TYPE, configuration=self.trigger_config.get_dict()) trigger_rule_id = trigger_rule.id new_trigger_rule = trigger_test_utils.create_trigger_rule( trigger_type=recipe_test_utils.MOCK_ERROR_TYPE, configuration=self.new_trigger_config.get_dict()) recipe_type = RecipeType.objects.create_recipe_type( name, version, title, desc, self.recipe_def, trigger_rule) with transaction.atomic(): recipe_type = RecipeType.objects.select_for_update().get( pk=recipe_type.id) # Edit the recipe self.assertRaises(InvalidRecipeConnection, RecipeType.objects.edit_recipe_type, recipe_type.id, None, None, self.new_recipe_def, new_trigger_rule, False) recipe_type = RecipeType.objects.select_related('trigger_rule').get( pk=recipe_type.id) # Check results self.assertEqual(recipe_type.title, title) self.assertEqual(recipe_type.description, desc) self.assertDictEqual(recipe_type.get_recipe_definition().get_dict(), self.recipe_def.get_dict()) self.assertEqual(recipe_type.revision_num, 1) self.assertEqual(recipe_type.trigger_rule_id, trigger_rule_id) trigger_rule = TriggerRule.objects.get(pk=trigger_rule_id) self.assertTrue(trigger_rule.is_active) num_of_revs = RecipeTypeRevision.objects.filter( recipe_type_id=recipe_type.id).count() self.assertEqual(num_of_revs, 1)
class TestRecipeTypeManagerCreateRecipeType(TransactionTestCase): def setUp(self): django.setup() self.workspace = storage_test_utils.create_workspace() interface_1 = { 'version': '1.0', 'command': 'my_command', 'command_arguments': 'args', 'input_data': [{ 'name': 'Test Input 1', 'type': 'file', 'media_types': ['text/plain'], }], 'output_data': [{ 'name': 'Test Output 1', 'type': 'files', 'media_type': 'image/png', }] } self.job_type_1 = job_test_utils.create_job_type(interface=interface_1) interface_2 = { 'version': '1.0', 'command': 'my_command', 'command_arguments': 'args', 'input_data': [{ 'name': 'Test Input 2', 'type': 'files', 'media_types': ['image/png', 'image/tiff'], }], 'output_data': [{ 'name': 'Test Output 2', 'type': 'file', }] } self.job_type_2 = job_test_utils.create_job_type(interface=interface_2) self.definition = { 'version': '1.0', 'input_data': [{ 'name': 'Recipe Input', 'type': 'file', 'media_types': ['text/plain'], }], 'jobs': [{ 'name': 'Job 1', 'job_type': { 'name': self.job_type_1.name, 'version': self.job_type_1.version, }, 'recipe_inputs': [{ 'recipe_input': 'Recipe Input', 'job_input': 'Test Input 1', }] }, { 'name': 'Job 2', 'job_type': { 'name': self.job_type_2.name, 'version': self.job_type_2.version, }, 'dependencies': [{ 'name': 'Job 1', 'connections': [{ 'output': 'Test Output 1', 'input': 'Test Input 2', }] }] }] } self.recipe_def = LegacyRecipeDefinition(self.definition) self.recipe_def.validate_job_interfaces() def test_successful(self): """Tests calling RecipeTypeManager.create_recipe_type() successfully.""" name = 'test-recipe' version = '1.0' title = 'Test Recipe' desc = 'Test description' recipe_type = RecipeType.objects.create_recipe_type( name, version, title, desc, self.recipe_def, None) results_recipe_type = RecipeType.objects.get(pk=recipe_type.id) self.assertEqual(results_recipe_type.name, name) self.assertEqual(results_recipe_type.version, version) self.assertEqual(results_recipe_type.title, title) self.assertEqual(results_recipe_type.description, desc) self.assertDictEqual(results_recipe_type.definition, self.definition) results_recipe_type_rev = RecipeTypeRevision.objects.get( recipe_type_id=recipe_type.id, revision_num=1) self.assertDictEqual(results_recipe_type_rev.definition, self.definition)
def test_successful_supersede_mixed(self): """Tests calling QueueManager.queue_new_recipe() successfully when superseding a recipe where the results of a Seed job get passed to the input of a legacy job """ workspace = storage_test_utils.create_workspace() source_file = source_test_utils.create_source(workspace=workspace) event = trigger_test_utils.create_trigger_event() interface_1 = { 'seedVersion': '1.0.0', 'job': { 'name': 'job-type-a', 'jobVersion': '1.0.0', 'packageVersion': '1.0.0', 'title': 'Job Type 1', 'description': 'This is a description', 'maintainer': { 'name': 'John Doe', 'email': '*****@*****.**' }, 'timeout': 10, 'interface': { 'command': '', 'inputs': { 'files': [{ 'name': 'test-input-a' }] }, 'outputs': { 'files': [{ 'name': 'test-output-a', 'pattern': '*.png' }] } } } } job_type_1 = job_test_utils.create_seed_job_type(manifest=interface_1) interface_2 = { 'version': '1.0', 'command': 'test_command', 'command_arguments': 'test_arg', 'input_data': [{ 'name': 'Test Input 2', 'type': 'file', 'media_types': ['image/png', 'image/tiff'], }], 'output_data': [{ 'name': 'Test Output 2', 'type': 'file', }] } job_type_2 = job_test_utils.create_job_type(interface=interface_2) definition = { 'version': '1.0', 'input_data': [{ 'name': 'Recipe Input', 'type': 'file', 'media_types': ['text/plain'], }], 'jobs': [{ 'name': 'Job 1', 'job_type': { 'name': job_type_1.name, 'version': job_type_1.version, }, 'recipe_inputs': [{ 'recipe_input': 'Recipe Input', 'job_input': 'test-input-a', }] }, { 'name': 'Job 2', 'job_type': { 'name': job_type_2.name, 'version': job_type_2.version, }, 'dependencies': [{ 'name': 'Job 1', 'connections': [{ 'output': 'test-output-a', 'input': 'Test Input 2', }] }] }] } recipe_definition = RecipeDefinition(definition) recipe_definition.validate_job_interfaces() recipe_type = recipe_test_utils.create_recipe_type( definition=definition) data = { 'version': '1.0', 'input_data': [{ 'name': 'Recipe Input', 'file_id': source_file.id, }], 'workspace_id': workspace.id, } data = LegacyRecipeData(data) # Queue initial recipe and complete its first job handler = Queue.objects.queue_new_recipe(recipe_type, data, event) recipe = Recipe.objects.get(id=handler.recipe.id) recipe_job_1 = RecipeNode.objects.select_related('job') recipe_job_1 = recipe_job_1.get(recipe_id=handler.recipe.id, node_name='Job 1') Job.objects.update_jobs_to_running([recipe_job_1.job], now()) results = JobResults() results.add_file_parameter('test-output-a', product_test_utils.create_product().id) job_test_utils.create_job_exe(job=recipe_job_1.job, status='COMPLETED', output=results) Job.objects.update_jobs_to_completed([recipe_job_1.job], now()) Job.objects.process_job_output([recipe_job_1.job_id], now()) # Create a new recipe type that has a new version of job 2 (job 1 is identical) new_job_type_2 = job_test_utils.create_job_type( name=job_type_2.name, version='New Version', interface=job_type_2.manifest) new_definition = { 'version': '1.0', 'input_data': [{ 'name': 'Recipe Input', 'type': 'file', 'media_types': ['text/plain'], }], 'jobs': [{ 'name': 'New Job 1', 'job_type': { 'name': job_type_1.name, 'version': job_type_1.version, }, 'recipe_inputs': [{ 'recipe_input': 'Recipe Input', 'job_input': 'test-input-a', }] }, { 'name': 'New Job 2', 'job_type': { 'name': new_job_type_2.name, 'version': new_job_type_2.version, }, 'dependencies': [{ 'name': 'New Job 1', 'connections': [{ 'output': 'test-output-a', 'input': 'Test Input 2', }] }] }] } new_recipe_type = recipe_test_utils.create_recipe_type( name=recipe_type.name, definition=new_definition) event = trigger_test_utils.create_trigger_event() recipe_job_1 = RecipeNode.objects.select_related('job').get( recipe_id=handler.recipe.id, node_name='Job 1') recipe_job_2 = RecipeNode.objects.select_related('job').get( recipe_id=handler.recipe.id, node_name='Job 2') superseded_jobs = { 'Job 1': recipe_job_1.job, 'Job 2': recipe_job_2.job } graph_a = recipe_type.get_recipe_definition().get_graph() graph_b = new_recipe_type.get_recipe_definition().get_graph() delta = RecipeGraphDelta(graph_a, graph_b) # Queue new recipe that supersedes the old recipe new_handler = Queue.objects.queue_new_recipe( new_recipe_type, None, event, superseded_recipe=recipe, delta=delta, superseded_jobs=superseded_jobs) # Ensure old recipe is superseded recipe = Recipe.objects.get(id=handler.recipe.id) self.assertTrue(recipe.is_superseded) # Ensure new recipe supersedes old recipe new_recipe = Recipe.objects.get(id=new_handler.recipe.id) self.assertEqual(new_recipe.superseded_recipe_id, handler.recipe.id) # Ensure that job 1 is already completed (it was copied from original recipe) and that job 2 is queued new_recipe_job_1 = RecipeNode.objects.select_related('job').get( recipe_id=new_handler.recipe.id, node_name='New Job 1') new_recipe_job_2 = RecipeNode.objects.select_related('job').get( recipe_id=new_handler.recipe.id, node_name='New Job 2') self.assertEqual(new_recipe_job_1.job.status, 'COMPLETED') self.assertFalse(new_recipe_job_1.is_original) self.assertEqual(new_recipe_job_2.job.status, 'QUEUED') self.assertTrue(new_recipe_job_2.is_original)