Exemple #1
0
    def test_detect_invalid_links_workflow_create(self):
        """Test `verdi database integrity detect-invalid-links` outgoing `create` from `workflow`."""
        result = self.cli_runner.invoke(cmd_database.detect_invalid_links, [])
        self.assertEqual(result.exit_code, 0)
        self.assertClickResultNoException(result)

        # Create an invalid link: outgoing `create` from a workflow
        data = Data().store().backend_entity
        workflow = WorkflowNode().store().backend_entity

        data.add_incoming(workflow,
                          link_type=LinkType.CREATE,
                          link_label='create')

        result = self.cli_runner.invoke(cmd_database.detect_invalid_links, [])
        self.assertNotEqual(result.exit_code, 0)
        self.assertIsNotNone(result.exception)
Exemple #2
0
    def test_node_indegree_unique_pair(self):
        """Test that the validation of links with indegree `unique_pair` works correctly

        The example here is a `DataNode` that has two incoming links with the same label, but with different types.
        This is legal and should pass validation.
        """
        caller = WorkflowNode().store()
        data = Data().store()
        called = CalculationNode()

        # Verify that adding two incoming links with the same link label but different type is allowed
        called.add_incoming(caller, link_type=LinkType.CALL_CALC, link_label='call')
        called.add_incoming(data, link_type=LinkType.INPUT_CALC, link_label='call')
        called.store()

        uuids_incoming = set(node.uuid for node in called.get_incoming().all_nodes())
        uuids_expected = set([caller.uuid, data.uuid])
        self.assertEqual(uuids_incoming, uuids_expected)
    def _generate_eos_node(include_magnetization=True):
        from aiida.common import LinkType
        from aiida.orm import Float, WorkflowNode

        node = WorkflowNode(process_type='aiida.workflows:common_workflows.eos').store()

        for index in range(5):
            structure = generate_structure().store()
            energy = Float(index).store()

            structure.add_incoming(node, link_type=LinkType.RETURN, link_label=f'structures__{index}')
            energy.add_incoming(node, link_type=LinkType.RETURN, link_label=f'total_energies__{index}')

            if include_magnetization:
                magnetization = Float(index).store()
                magnetization.add_incoming(node, link_type=LinkType.RETURN, link_label=f'total_magnetizations__{index}')

        return node
    def _generate_dissociation_curve_node(include_magnetization=True):
        from aiida.common import LinkType
        from aiida.orm import Float, WorkflowNode

        node = WorkflowNode(process_type='aiida.workflows:common_workflows.dissociation_curve').store()

        for index in range(5):
            distance = Float(index / 10).store()
            energy = Float(index).store()

            distance.add_incoming(node, link_type=LinkType.RETURN, link_label=f'distances__{index}')
            energy.add_incoming(node, link_type=LinkType.RETURN, link_label=f'total_energies__{index}')

            if include_magnetization:
                magnetization = Float(index).store()
                magnetization.add_incoming(node, link_type=LinkType.RETURN, link_label=f'total_magnetizations__{index}')

        return node
Exemple #5
0
    def test_detect_invalid_links_call_links(self):
        """Test `verdi database integrity detect-invalid-links` when there are multiple incoming `call` links."""
        result = self.cli_runner.invoke(cmd_database.detect_invalid_links, [])
        self.assertEqual(result.exit_code, 0)
        self.assertClickResultNoException(result)

        # Create an invalid link: two `call` links
        workflow = WorkflowNode().store().backend_entity
        calculation = CalculationNode().store().backend_entity

        calculation.add_incoming(workflow,
                                 link_type=LinkType.CALL_CALC,
                                 link_label='call')
        calculation.add_incoming(workflow,
                                 link_type=LinkType.CALL_CALC,
                                 link_label='call')

        result = self.cli_runner.invoke(cmd_database.detect_invalid_links, [])
        self.assertNotEqual(result.exit_code, 0)
        self.assertIsNotNone(result.exception)
Exemple #6
0
    def test_process_report(self):
        """Test verdi process report"""
        node = WorkflowNode().store()

        # Running without identifiers should not except and not print anything
        options = []
        result = self.cli_runner.invoke(cmd_process.process_report, options)
        self.assertIsNone(result.exception, result.output)
        self.assertEqual(len(get_result_lines(result)), 0)

        # Giving a single identifier should print a non empty string message
        options = [str(node.pk)]
        result = self.cli_runner.invoke(cmd_process.process_report, options)
        self.assertIsNone(result.exception, result.output)
        self.assertTrue(len(get_result_lines(result)) > 0)

        # Giving multiple identifiers should print a non empty string message
        options = [str(calc.pk) for calc in [node]]
        result = self.cli_runner.invoke(cmd_process.process_report, options)
        self.assertIsNone(result.exception, result.output)
        self.assertTrue(len(get_result_lines(result)) > 0)
Exemple #7
0
    def setUpClass(cls, *args, **kwargs):
        """Create a basic valid graph that should help detect false positives."""
        super(TestVerdiDatabasaIntegrity, cls).setUpClass(*args, **kwargs)
        data_input = Data().store()
        data_output = Data().store()
        calculation = CalculationNode()
        workflow_parent = WorkflowNode()
        workflow_child = WorkflowNode()

        workflow_parent.add_incoming(data_input,
                                     link_label='input',
                                     link_type=LinkType.INPUT_WORK)
        workflow_parent.store()

        workflow_child.add_incoming(data_input,
                                    link_label='input',
                                    link_type=LinkType.INPUT_WORK)
        workflow_child.add_incoming(workflow_parent,
                                    link_label='call',
                                    link_type=LinkType.CALL_WORK)
        workflow_child.store()

        calculation.add_incoming(data_input,
                                 link_label='input',
                                 link_type=LinkType.INPUT_CALC)
        calculation.add_incoming(workflow_child,
                                 link_label='input',
                                 link_type=LinkType.CALL_CALC)
        calculation.store()

        data_output.add_incoming(calculation,
                                 link_label='output',
                                 link_type=LinkType.CREATE)
        data_output.add_incoming(workflow_child,
                                 link_label='output',
                                 link_type=LinkType.RETURN)
        data_output.add_incoming(workflow_parent,
                                 link_label='output',
                                 link_type=LinkType.RETURN)
Exemple #8
0
    def test_tab_completable_properties(self):
        """Test properties to go from one node to a neighboring one"""
        # pylint: disable=too-many-statements
        input1 = Data().store()
        input2 = Data().store()

        top_workflow = WorkflowNode()
        workflow = WorkflowNode()
        calc1 = CalculationNode()
        calc2 = CalculationNode()

        output1 = Data().store()
        output2 = Data().store()

        # The `top_workflow` has two inputs, proxies them to `workflow`, that in turn calls two calculations, passing
        # one data node to each as input, and return the two data nodes returned one by each called calculation
        top_workflow.add_incoming(input1, link_type=LinkType.INPUT_WORK, link_label='a')
        top_workflow.add_incoming(input2, link_type=LinkType.INPUT_WORK, link_label='b')
        top_workflow.store()

        workflow.add_incoming(input1, link_type=LinkType.INPUT_WORK, link_label='a')
        workflow.add_incoming(input2, link_type=LinkType.INPUT_WORK, link_label='b')
        workflow.add_incoming(top_workflow, link_type=LinkType.CALL_WORK, link_label='CALL')
        workflow.store()

        calc1.add_incoming(input1, link_type=LinkType.INPUT_CALC, link_label='input_value')
        calc1.add_incoming(workflow, link_type=LinkType.CALL_CALC, link_label='CALL')
        calc1.store()
        output1.add_incoming(calc1, link_type=LinkType.CREATE, link_label='result')

        calc2.add_incoming(input2, link_type=LinkType.INPUT_CALC, link_label='input_value')
        calc2.add_incoming(workflow, link_type=LinkType.CALL_CALC, link_label='CALL')
        calc2.store()
        output2.add_incoming(calc2, link_type=LinkType.CREATE, link_label='result')

        output1.add_incoming(workflow, link_type=LinkType.RETURN, link_label='result_a')
        output2.add_incoming(workflow, link_type=LinkType.RETURN, link_label='result_b')
        output1.add_incoming(top_workflow, link_type=LinkType.RETURN, link_label='result_a')
        output2.add_incoming(top_workflow, link_type=LinkType.RETURN, link_label='result_b')

        # creator
        self.assertEqual(output1.creator.pk, calc1.pk)
        self.assertEqual(output2.creator.pk, calc2.pk)

        # caller (for calculations)
        self.assertEqual(calc1.caller.pk, workflow.pk)
        self.assertEqual(calc2.caller.pk, workflow.pk)

        # caller (for workflows)
        self.assertEqual(workflow.caller.pk, top_workflow.pk)

        # .inputs for calculations
        self.assertEqual(calc1.inputs.input_value.pk, input1.pk)
        self.assertEqual(calc2.inputs.input_value.pk, input2.pk)
        with self.assertRaises(exceptions.NotExistent):
            _ = calc1.inputs.some_label

        # .inputs for workflows
        self.assertEqual(top_workflow.inputs.a.pk, input1.pk)
        self.assertEqual(top_workflow.inputs.b.pk, input2.pk)
        self.assertEqual(workflow.inputs.a.pk, input1.pk)
        self.assertEqual(workflow.inputs.b.pk, input2.pk)
        with self.assertRaises(exceptions.NotExistent):
            _ = workflow.inputs.some_label

        # .outputs for calculations
        self.assertEqual(calc1.outputs.result.pk, output1.pk)
        self.assertEqual(calc2.outputs.result.pk, output2.pk)
        with self.assertRaises(exceptions.NotExistent):
            _ = calc1.outputs.some_label

        # .outputs for workflows
        self.assertEqual(top_workflow.outputs.result_a.pk, output1.pk)
        self.assertEqual(top_workflow.outputs.result_b.pk, output2.pk)
        self.assertEqual(workflow.outputs.result_a.pk, output1.pk)
        self.assertEqual(workflow.outputs.result_b.pk, output2.pk)
        with self.assertRaises(exceptions.NotExistent):
            _ = workflow.outputs.some_label
Exemple #9
0
    def test_add_incoming_call_work(self):
        """Nodes can only have a single incoming CALL_WORK link, independent of the source node."""
        source_one = WorkflowNode()
        source_two = WorkflowNode()
        target = WorkflowNode()

        target.add_incoming(source_one, LinkType.CALL_WORK, 'link_label')

        # Can only have a single incoming CALL_WORK link
        with self.assertRaises(ValueError):
            target.validate_incoming(source_one, LinkType.CALL_WORK, 'link_label')

        # Even when the source node is different
        with self.assertRaises(ValueError):
            target.validate_incoming(source_two, LinkType.CALL_WORK, 'link_label')

        # Or when the link label is different
        with self.assertRaises(ValueError):
            target.validate_incoming(source_one, LinkType.CALL_WORK, 'other_label')