Exemplo n.º 1
0
def test_full_type_backwards_compatibility(node_class, restapi_server,
                                           server_url):
    """Functionality test for the compatibility with old process_type entries.

    This will only check the integrity of the shape of the tree, there is no checking on how the
    data should be represented internally in term of full types, labels, etc. The only thing that
    must work correctly is the internal consistency of `full_type` as it is interpreted by the
    get_full_type_filters and the querybuilder.
    """
    node_empty = node_class()
    node_empty.process_type = ''
    node_empty.store()

    node_nones = node_class()
    node_nones.process_type = None
    node_nones.store()

    server = restapi_server()
    server_thread = Thread(target=server.serve_forever)

    try:
        server_thread.start()
        type_count_response = requests.get(f'{server_url}/nodes/full_types',
                                           timeout=10)
    finally:
        server.shutdown()

    # All nodes (contains either a process branch or data branch)
    # The main branch for all nodes does not currently return a queryable full_type
    current_namespace = type_count_response.json()['data']
    assert len(current_namespace['subspaces']) == 1

    # All subnodes (contains a workflow, calculation or data_type branch)
    current_namespace = current_namespace['subspaces'][0]
    query_all = orm.QueryBuilder().append(orm.Node,
                                          filters=get_full_type_filters(
                                              current_namespace['full_type']))
    assert len(current_namespace['subspaces']) == 1
    assert query_all.count() == 2

    # If this is a process node, there is an extra branch before the leaf
    # (calcfunction, workfunction, calcjob or workchain)
    if issubclass(node_class, orm.ProcessNode):
        current_namespace = current_namespace['subspaces'][0]
        query_all = orm.QueryBuilder().append(
            orm.Node,
            filters=get_full_type_filters(current_namespace['full_type']))
        assert len(current_namespace['subspaces']) == 1
        assert query_all.count() == 2

    # This will be the last leaf: the specific data type or the empty process_type
    current_namespace = current_namespace['subspaces'][0]
    query_all = orm.QueryBuilder().append(orm.Node,
                                          filters=get_full_type_filters(
                                              current_namespace['full_type']))
    assert len(current_namespace['subspaces']) == 0
    assert query_all.count() == 2
Exemplo n.º 2
0
def test_get_full_type_filters():
    """Coverage test for the `get_full_type_filters` function."""

    # Equals on both
    filters = get_full_type_filters(
        f'node_type{FULL_TYPE_CONCATENATOR}process_type')
    assert filters['node_type'] == {'==': 'node_type'}
    assert filters['process_type'] == {'==': 'process_type'}

    # Like on `node_type`
    filters = get_full_type_filters(
        f'node_type{LIKE_OPERATOR_CHARACTER}{FULL_TYPE_CONCATENATOR}process_type'
    )
    assert filters['node_type'] == {'like': 'node\\_type%'}
    assert filters['process_type'] == {'==': 'process_type'}

    # Like on `process_type`
    filters = get_full_type_filters(
        f'node_type{FULL_TYPE_CONCATENATOR}process_type{LIKE_OPERATOR_CHARACTER}'
    )
    assert filters['node_type'] == {'==': 'node_type'}
    assert filters['process_type'] == {'like': 'process\\_type%'}

    # Like on both
    filters = get_full_type_filters(
        'node_type{like}{concat}process_type{like}'.format(
            like=LIKE_OPERATOR_CHARACTER, concat=FULL_TYPE_CONCATENATOR))
    assert filters['node_type'] == {'like': 'node\\_type%'}
    assert filters['process_type'] == {'like': 'process\\_type%'}

    # Test patologic case of process_type = '' / None
    filters = get_full_type_filters(f'node_type{FULL_TYPE_CONCATENATOR}')
    assert filters['node_type'] == {'==': 'node_type'}
    assert filters['process_type'] == {'or': [{'==': ''}, {'==': None}]}

    filters = get_full_type_filters(
        f'node_type{LIKE_OPERATOR_CHARACTER}{FULL_TYPE_CONCATENATOR}')
    assert filters['node_type'] == {'like': 'node\\_type%'}
    assert filters['process_type'] == {'or': [{'==': ''}, {'==': None}]}
Exemplo n.º 3
0
    def test_get_full_type_filters(self):
        """Test the `get_full_type_filters` function."""

        with self.assertRaises(TypeError):
            get_full_type_filters(10)

        with self.assertRaises(ValueError):
            get_full_type_filters('string_without_full_type_concatenator')

        with self.assertRaises(ValueError):
            get_full_type_filters(
                'too_many_{like}{like}{concat}process_type'.format(
                    like=LIKE_OPERATOR_CHARACTER,
                    concat=FULL_TYPE_CONCATENATOR))

        with self.assertRaises(ValueError):
            get_full_type_filters(
                'node_type{concat}too_many_{like}{like}'.format(
                    like=LIKE_OPERATOR_CHARACTER,
                    concat=FULL_TYPE_CONCATENATOR))

        with self.assertRaises(ValueError):
            get_full_type_filters(
                'not_at_{like}_the_end{concat}process_type'.format(
                    like=LIKE_OPERATOR_CHARACTER,
                    concat=FULL_TYPE_CONCATENATOR))

        with self.assertRaises(ValueError):
            get_full_type_filters(
                'node_type{concat}not_at_{like}_the_end'.format(
                    like=LIKE_OPERATOR_CHARACTER,
                    concat=FULL_TYPE_CONCATENATOR))

        # Equals on both
        filters = get_full_type_filters('node_type{concat}process_type'.format(
            concat=FULL_TYPE_CONCATENATOR))
        self.assertEqual(filters['node_type'], 'node\\_type')
        self.assertEqual(filters['process_type'], 'process\\_type')

        # Like on `node_type`
        filters = get_full_type_filters(
            'node_type{like}{concat}process_type'.format(
                like=LIKE_OPERATOR_CHARACTER, concat=FULL_TYPE_CONCATENATOR))
        self.assertEqual(filters['node_type'], {'like': 'node\\_type%'})
        self.assertEqual(filters['process_type'], 'process\\_type')

        # Like on `process_type`
        filters = get_full_type_filters(
            'node_type{concat}process_type{like}'.format(
                like=LIKE_OPERATOR_CHARACTER, concat=FULL_TYPE_CONCATENATOR))
        self.assertEqual(filters['node_type'], 'node\\_type')
        self.assertEqual(filters['process_type'], {'like': 'process\\_type%'})

        # Like on both
        filters = get_full_type_filters(
            'node_type{like}{concat}process_type{like}'.format(
                like=LIKE_OPERATOR_CHARACTER, concat=FULL_TYPE_CONCATENATOR))
        self.assertEqual(filters['node_type'], {'like': 'node\\_type%'})
        self.assertEqual(filters['process_type'], {'like': 'process\\_type%'})
Exemplo n.º 4
0
    def set_query(self,
                  filters=None,
                  orders=None,
                  projections=None,
                  query_type=None,
                  node_id=None,
                  download_format=None,
                  download=None,
                  filename=None,
                  attributes=None,
                  attributes_filter=None,
                  extras=None,
                  extras_filter=None,
                  full_type=None):
        """
        Adds filters, default projections, order specs to the query_help,
        and initializes the qb object

        :param filters: dictionary with the filters
        :param orders: dictionary with the order for each tag
        :param projections: dictionary with the projection. It is discarded
            if query_type=='attributes'/'extras'
        :param query_type: (string) specify the result or the content ("attr")
        :param id: (integer) id of a specific node
        :param download_format: file format to download e.g. cif, xyz
        :param filename: name of the file to return its content
        :param attributes: flag to show attributes for nodes
        :param attributes_filter: list of attributes to query
        :param extras: flag to show extras for nodes
        :param extras_filter: list of extras to query
        """
        # pylint: disable=arguments-differ, too-many-locals

        ## Check the compatibility of query_type and id
        if query_type != 'default' and id is None:
            raise ValidationError('non default result/content can only be '
                                  'applied to a specific node (specify an id)')

        ## Set the type of query
        self.set_query_type(query_type,
                            attributes_filter=attributes_filter,
                            extras_filter=extras_filter,
                            download_format=download_format,
                            download=download,
                            filename=filename)

        ## Define projections
        if self._content_type is not None:
            # Use '*' so that the object itself will be returned.
            # In get_results() we access attributes/extras by
            # calling the attributes/extras.
            projections = ['*']
        else:
            pass  # i.e. use the input parameter projection

        # TODO this actually works, but the logic is a little bit obscure.
        # Make it clearer
        if self._result_type is not self.__label__:
            projections = self._default_projections

        if full_type:
            # If full_type is not none, convert it into node_type
            # and process_type filters to pass it to query help
            from aiida.restapi.common.identifiers import get_full_type_filters
            full_type_filter = get_full_type_filters(full_type)
            filters.update(full_type_filter)

        super().set_query(filters=filters,
                          orders=orders,
                          projections=projections,
                          node_id=node_id,
                          attributes=attributes,
                          attributes_filter=attributes_filter,
                          extras=extras,
                          extras_filter=extras_filter)
Exemplo n.º 5
0
def test_full_type_unregistered(process_class, restapi_server, server_url):
    """Functionality test for the compatibility with old process_type entries.

    This will only check the integrity of the shape of the tree, there is no checking on how the
    data should be represented internally in term of full types, labels, etc. The only thing that
    must work correctly is the internal consistency of `full_type` as it is interpreted by the
    get_full_type_filters and the querybuilder.
    """
    # pylint: disable=too-many-statements
    calc_unreg11 = process_class()
    calc_unreg11.set_process_type('unregistered_type1.process1')
    calc_unreg11.store()

    calc_unreg21 = process_class()
    calc_unreg21.set_process_type('unregistered_type2.process1')
    calc_unreg21.store()

    calc_unreg22 = process_class()
    calc_unreg22.set_process_type('unregistered_type2.process2')
    calc_unreg22.store()

    server = restapi_server()
    server_thread = Thread(target=server.serve_forever)

    try:
        server_thread.start()
        type_count_response = requests.get(f'{server_url}/nodes/full_types',
                                           timeout=10)
    finally:
        server.shutdown()

    # All nodes = only processes
    # The main branch for all nodes does not currently return a queryable full_type
    current_namespace = type_count_response.json()['data']
    assert len(current_namespace['subspaces']) == 1

    # All processes = only one kind of process (Calculation/Workflow)
    current_namespace = current_namespace['subspaces'][0]
    query_all = orm.QueryBuilder().append(orm.Node,
                                          filters=get_full_type_filters(
                                              current_namespace['full_type']))
    assert len(current_namespace['subspaces']) == 1
    assert query_all.count() == 3

    # All subprocesses = only one kind of subprocess (calcfunc/workfunc or calcjob/workchain)
    current_namespace = current_namespace['subspaces'][0]
    query_all = orm.QueryBuilder().append(orm.Node,
                                          filters=get_full_type_filters(
                                              current_namespace['full_type']))
    assert len(current_namespace['subspaces']) == 1
    assert query_all.count() == 3

    # One branch for each registered plugin and one for all unregistered
    # (there are only unregistered here)
    current_namespace = current_namespace['subspaces'][0]
    query_all = orm.QueryBuilder().append(orm.Node,
                                          filters=get_full_type_filters(
                                              current_namespace['full_type']))
    assert len(current_namespace['subspaces']) == 1
    assert query_all.count() == 3

    # There are only two  process types: unregistered_type1 (1) and unregistered_type2 (2)
    # The unregistered branch does not currently return a queryable full_type
    current_namespace = current_namespace['subspaces'][0]
    assert len(current_namespace['subspaces']) == 2

    type_namespace = current_namespace['subspaces'][0]
    query_type = orm.QueryBuilder().append(orm.Node,
                                           filters=get_full_type_filters(
                                               type_namespace['full_type']))
    assert len(type_namespace['subspaces']) == 1
    assert query_type.count() == 1

    type_namespace = current_namespace['subspaces'][1]
    query_type = orm.QueryBuilder().append(orm.Node,
                                           filters=get_full_type_filters(
                                               type_namespace['full_type']))
    assert len(type_namespace['subspaces']) == 2
    assert query_type.count() == 2

    # Finally we check each specific subtype (1 for unregistered_type1 and 2 for unregistered_type2)
    # These are lead nodes without any further subspace
    type_namespace = current_namespace['subspaces'][0]['subspaces'][0]
    query_type = orm.QueryBuilder().append(orm.Node,
                                           filters=get_full_type_filters(
                                               type_namespace['full_type']))
    assert len(type_namespace['subspaces']) == 0
    assert query_type.count() == 1

    type_namespace = current_namespace['subspaces'][1]['subspaces'][0]
    query_type = orm.QueryBuilder().append(orm.Node,
                                           filters=get_full_type_filters(
                                               type_namespace['full_type']))
    assert len(type_namespace['subspaces']) == 0
    assert query_type.count() == 1

    type_namespace = current_namespace['subspaces'][1]['subspaces'][1]
    query_type = orm.QueryBuilder().append(orm.Node,
                                           filters=get_full_type_filters(
                                               type_namespace['full_type']))
    assert len(type_namespace['subspaces']) == 0
    assert query_type.count() == 1
Exemplo n.º 6
0
def test_get_filters_errors():
    """Test the `get_full_type_filters` function."""

    with pytest.raises(TypeError):
        get_full_type_filters(10)

    with pytest.raises(ValueError):
        get_full_type_filters('string_without_full_type_concatenator')

    with pytest.raises(ValueError):
        get_full_type_filters(
            'too_many_{like}{like}{concat}process_type'.format(
                like=LIKE_OPERATOR_CHARACTER, concat=FULL_TYPE_CONCATENATOR))

    with pytest.raises(ValueError):
        get_full_type_filters('node_type{concat}too_many_{like}{like}'.format(
            like=LIKE_OPERATOR_CHARACTER, concat=FULL_TYPE_CONCATENATOR))

    with pytest.raises(ValueError):
        get_full_type_filters(
            f'not_at_{LIKE_OPERATOR_CHARACTER}_the_end{FULL_TYPE_CONCATENATOR}process_type'
        )

    with pytest.raises(ValueError):
        get_full_type_filters(
            f'node_type{FULL_TYPE_CONCATENATOR}not_at_{LIKE_OPERATOR_CHARACTER}_the_end'
        )