Example #1
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)
Example #2
0
    def test_detect_invalid_links_unknown_link_type(self):
        """Test `verdi database integrity detect-invalid-links` when link type is invalid."""
        result = self.cli_runner.invoke(cmd_database.detect_invalid_links, [])
        self.assertEqual(result.exit_code, 0)
        self.assertClickResultNoException(result)

        class WrongLinkType(enum.Enum):

            WRONG_CREATE = 'wrong_create'

        # Create an invalid link: invalid link type
        data = Data().store().backend_entity
        calculation = CalculationNode().store().backend_entity

        data.add_incoming(calculation, link_type=WrongLinkType.WRONG_CREATE, link_label='create')

        result = self.cli_runner.invoke(cmd_database.detect_invalid_links, [])
        self.assertNotEqual(result.exit_code, 0)
        self.assertIsNotNone(result.exception)
Example #3
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)
Example #4
0
    def test_detect_invalid_links_create_links(self):
        """Test `verdi database integrity detect-invalid-links` when there are multiple incoming `create` 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 `create` links
        data = Data().store().backend_entity
        calculation = CalculationNode().store().backend_entity

        data.add_incoming(calculation,
                          link_type=LinkType.CREATE,
                          link_label='create')
        data.add_incoming(calculation,
                          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)
Example #5
0
    def test_get_stored_link_triples(self):
        """Validate the `get_stored_link_triples` method."""
        data = Data().store()
        calculation = CalculationNode()

        calculation.add_incoming(data, LinkType.INPUT_CALC, 'input')
        calculation.store()
        stored_triples = calculation.get_stored_link_triples()

        self.assertEqual(len(stored_triples), 1)

        link_triple = stored_triples[0]

        # Verify the type and value of the tuple elements
        self.assertTrue(isinstance(link_triple, LinkTriple))
        self.assertTrue(isinstance(link_triple.node, Node))
        self.assertTrue(isinstance(link_triple.link_type, LinkType))
        self.assertEqual(link_triple.node.uuid, data.uuid)
        self.assertEqual(link_triple.link_type, LinkType.INPUT_CALC)
        self.assertEqual(link_triple.link_label, 'input')
Example #6
0
    def _generate_calculation_node(process_state=ProcessState.FINISHED, exit_status=None, entry_point=None):
        """Generate an instance of a `CalculationNode`..

        :param process_state: state to set
        :param exit_status: optional exit status, will be set to `0` if `process_state` is `ProcessState.FINISHED`
        :return: a `CalculationNode` instance.
        """
        from aiida.orm import CalculationNode

        if process_state is ProcessState.FINISHED and exit_status is None:
            exit_status = 0

        node = CalculationNode(process_type=entry_point)
        node.set_process_state(process_state)

        if exit_status is not None:
            node.set_exit_status(exit_status)

        return node
Example #7
0
    def test_get_incoming(self):
        """Test that `Node.get_incoming` will return stored and cached input links."""
        source_one = Data().store()
        source_two = Data().store()
        target = CalculationNode()

        target.add_incoming(source_one, LinkType.INPUT_CALC, 'link_one')
        target.add_incoming(source_two, LinkType.INPUT_CALC, 'link_two')

        # Without link type
        incoming_nodes = target.get_incoming().all()
        incoming_uuids = sorted([neighbor.node.uuid for neighbor in incoming_nodes])
        self.assertEqual(incoming_uuids, sorted([source_one.uuid, source_two.uuid]))

        # Using a single link type
        incoming_nodes = target.get_incoming(link_type=LinkType.INPUT_CALC).all()
        incoming_uuids = sorted([neighbor.node.uuid for neighbor in incoming_nodes])
        self.assertEqual(incoming_uuids, sorted([source_one.uuid, source_two.uuid]))

        # Using a link type tuple
        incoming_nodes = target.get_incoming(link_type=(LinkType.INPUT_CALC, LinkType.INPUT_WORK)).all()
        incoming_uuids = sorted([neighbor.node.uuid for neighbor in incoming_nodes])
        self.assertEqual(incoming_uuids, sorted([source_one.uuid, source_two.uuid]))
Example #8
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)
Example #9
0
    def test_inputs_parents_relationship(self):
        """This test checks that the inputs_q, parents_q relationship and the
        corresponding properties work as expected."""
        n_1 = Data().store()
        n_2 = CalculationNode()
        n_3 = Data().store()

        # Create a link between these 2 nodes
        n_2.add_incoming(n_1, link_type=LinkType.INPUT_CALC, link_label='N1')
        n_2.store()
        n_3.add_incoming(n_2, link_type=LinkType.CREATE, link_label='N2')

        # Check that the result of outputs is a list
        self.assertIsInstance(n_1.backend_entity.dbmodel.inputs, list, 'This is expected to be a list')

        # Check that the result of outputs_q is a query
        from sqlalchemy.orm.dynamic import AppenderQuery
        self.assertIsInstance(
            n_1.backend_entity.dbmodel.inputs_q, AppenderQuery, 'This is expected to be an AppenderQuery'
        )

        # Check that the result of inputs is correct
        out = {_.pk for _ in n_3.backend_entity.dbmodel.inputs}
        self.assertEqual(out, set([n_2.pk]))
Example #10
0
    def test_process_node_updatable_attribute(self):
        """Check that updatable attributes and only those can be mutated for a stored but unsealed CalculationNode."""
        node = CalculationNode()
        attrs_to_set = {
            'bool': self.boolval,
            'integer': self.intval,
            'float': self.floatval,
            'string': self.stringval,
            'dict': self.dictval,
            'list': self.listval,
            'state': self.stateval
        }

        for key, value in attrs_to_set.items():
            node.set_attribute(key, value)

        # Check before storing
        node.set_attribute(CalculationNode.PROCESS_STATE_KEY, self.stateval)
        self.assertEqual(node.get_attribute(CalculationNode.PROCESS_STATE_KEY),
                         self.stateval)

        node.store()

        # Check after storing
        self.assertEqual(node.get_attribute(CalculationNode.PROCESS_STATE_KEY),
                         self.stateval)

        # I should be able to mutate the updatable attribute but not the others
        node.set_attribute(CalculationNode.PROCESS_STATE_KEY, 'FINISHED')
        node.delete_attribute(CalculationNode.PROCESS_STATE_KEY)

        # Deleting non-existing attribute should raise attribute error
        with self.assertRaises(AttributeError):
            node.delete_attribute(CalculationNode.PROCESS_STATE_KEY)

        with self.assertRaises(ModificationNotAllowed):
            node.set_attribute('bool', False)

        with self.assertRaises(ModificationNotAllowed):
            node.delete_attribute('bool')

        node.seal()

        # After sealing, even updatable attributes should be immutable
        with self.assertRaises(ModificationNotAllowed):
            node.set_attribute(CalculationNode.PROCESS_STATE_KEY, 'FINISHED')

        with self.assertRaises(ModificationNotAllowed):
            node.delete_attribute(CalculationNode.PROCESS_STATE_KEY)
Example #11
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
Example #12
0
    def test_get_node_by_label(self):
        """Test the get_node_by_label() method of the `LinkManager`

        In particular, check both the it returns the correct values, but also that it raises the expected
        exceptions where appropriate (missing link with a given label, or more than one link)
        """
        data = Data().store()
        calc_one_a = CalculationNode()
        calc_one_b = CalculationNode()
        calc_two = CalculationNode()

        # Two calcs using the data with the same label
        calc_one_a.add_incoming(data, link_type=LinkType.INPUT_CALC, link_label='input')
        calc_one_b.add_incoming(data, link_type=LinkType.INPUT_CALC, link_label='input')
        # A different label
        calc_two.add_incoming(data, link_type=LinkType.INPUT_CALC, link_label='the_input')

        calc_one_a.store()
        calc_one_b.store()
        calc_two.store()

        # Retrieve a link when the label is unique
        output_the_input = data.get_outgoing(link_type=LinkType.INPUT_CALC).get_node_by_label('the_input')
        self.assertEqual(output_the_input.pk, calc_two.pk)

        with self.assertRaises(exceptions.MultipleObjectsError):
            data.get_outgoing(link_type=LinkType.INPUT_CALC).get_node_by_label('input')

        with self.assertRaises(exceptions.NotExistent):
            data.get_outgoing(link_type=LinkType.INPUT_CALC).get_node_by_label('some_weird_label')
Example #13
0
 def setUp(self):
     super(TestNodeLinks, self).setUp()
     self.node_source = CalculationNode()
     self.node_target = Data()
Example #14
0
 def setUp(self):
     super().setUp()
     self.node_source = CalculationNode()
     self.node_target = Data()